diff options
| author | Steven Rostedt <rostedt@goodmis.org> | 2026-06-29 11:32:44 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2026-06-29 11:32:44 -0400 |
| commit | a13905ac604cfd28fbda0815da16127bd4069bbe (patch) | |
| tree | 81c6504a0aae2c7d7ea118c7aff71ec943035ca7 /drivers | |
| parent | 56ece2fce5c00f577d92a376ba5dc4a2aa15925d (diff) | |
| parent | c35eb77a67515d4201bc91294f40761591f43bbd (diff) | |
| download | linux-next-a13905ac604cfd28fbda0815da16127bd4069bbe.tar.gz linux-next-a13905ac604cfd28fbda0815da16127bd4069bbe.zip | |
Merge tools/for-next
Diffstat (limited to 'drivers')
1553 files changed, 33491 insertions, 17098 deletions
diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 286379d9511d..eed3d0ec5413 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -999,6 +999,7 @@ again: if (ret == -EBUSY) { amdxdna_umap_put(mapp); + mmput(mm); goto again; } @@ -1009,11 +1010,13 @@ again: if (mmu_interval_read_retry(&mapp->notifier, mapp->range.notifier_seq)) { up_write(&xdna->notifier_lock); amdxdna_umap_put(mapp); + mmput(mm); goto again; } mapp->invalid = false; up_write(&xdna->notifier_lock); amdxdna_umap_put(mapp); + mmput(mm); goto again; put_mm: diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index f1ac4e00bd9f..4500b9ccb02e 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -511,6 +511,11 @@ static int aie2_init(struct amdxdna_dev *xdna) return -EINVAL; } + if (!xdna->group) { + XDNA_ERR(xdna, "Running without IOMMU not supported"); + return -EINVAL; + } + ndev = drmm_kzalloc(&xdna->ddev, sizeof(*ndev), GFP_KERNEL); if (!ndev) return -ENOMEM; diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 7994e7073903..3401883e207f 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -2,6 +2,7 @@ /* Copyright 2025 Arm, Ltd. */ #include <linux/err.h> +#include <linux/overflow.h> #include <linux/slab.h> #include <drm/ethosu_accel.h> @@ -163,17 +164,30 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, s8 mode = dma_st->mode; u64 len = dma->len; + if (len == U64_MAX) + return U64_MAX; + if (mode >= 1) { + if (dma->stride[0] < 0 && (u64)(-dma->stride[0]) > len) + return U64_MAX; len += dma->stride[0]; - len *= dma_st->size0; + if (check_mul_overflow(len, (u64)dma_st->size0, &len)) + return U64_MAX; } if (mode == 2) { + if (dma->stride[1] < 0 && (u64)(-dma->stride[1]) > len) + return U64_MAX; len += dma->stride[1]; - len *= dma_st->size1; + if (check_mul_overflow(len, (u64)dma_st->size1, &len)) + return U64_MAX; + } + if (dma->region >= 0) { + u64 end; + + if (check_add_overflow(len, dma->offset, &end)) + return U64_MAX; + info->region_size[dma->region] = max(info->region_size[dma->region], end); } - if (dma->region >= 0) - info->region_size[dma->region] = max(info->region_size[dma->region], - len + dma->offset); return len; } @@ -387,6 +401,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, return -EFAULT; i++; + if (i >= size / 4) + return -EINVAL; bocmds[i] = cmds[1]; addr = cmd_to_addr(cmds); } @@ -395,6 +411,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, case NPU_OP_DMA_START: srclen = dma_length(info, &st.dma, &st.dma.src); dstlen = dma_length(info, &st.dma, &st.dma.dst); + if (srclen == U64_MAX || dstlen == U64_MAX) + return -EINVAL; if (st.dma.dst.region >= 0) info->output_region[st.dma.dst.region] = true; @@ -431,8 +449,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, return ret; break; case NPU_OP_RESIZE: // U85 only - WARN_ON(1); // TODO - break; + return -EINVAL; case NPU_SET_KERNEL_WIDTH_M1: st.ifm.width = param; break; @@ -464,7 +481,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, st.ifm.broadcast = param; break; case NPU_SET_IFM_REGION: - st.ifm.region = param & 0x7f; + st.ifm.region = param & 0x7; break; case NPU_SET_IFM_WIDTH0_M1: st.ifm.width0 = param; @@ -599,7 +616,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, if (ethosu_is_u65(edev)) st.scale[1].length = cmds[1]; else - st.weight[1].length = cmds[1]; + st.weight[2].length = cmds[1]; break; case NPU_SET_WEIGHT3_BASE: st.weight[3].base = addr; diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c index 189dbe94cf14..dc20bc73c6ed 100644 --- a/drivers/accel/ivpu/ivpu_debugfs.c +++ b/drivers/accel/ivpu/ivpu_debugfs.c @@ -450,7 +450,7 @@ priority_bands_fops_write(struct file *file, const char __user *user_buf, size_t u32 band; int ret; - if (size >= sizeof(buf)) + if (*pos != 0 || size >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, pos, user_buf, size); diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 107f8ad31050..33c50779c06b 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -259,6 +259,22 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) return -EINVAL; } + if (!PAGE_ALIGNED(runtime_addr)) { + ivpu_err(vdev, "Runtime address 0x%llx not page aligned\n", runtime_addr); + return -EINVAL; + } + + if (!PAGE_ALIGNED(runtime_size)) { + ivpu_err(vdev, "Runtime size %llu not page aligned\n", runtime_size); + return -EINVAL; + } + + if (runtime_size < image_size) { + ivpu_err(vdev, "Runtime size too small: %llu, image size: %llu\n", + runtime_size, image_size); + return -EINVAL; + } + if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) { ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n", image_load_addr, image_size); diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c index 337c906b0210..275baf844b56 100644 --- a/drivers/accel/ivpu/ivpu_fw_log.c +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -98,6 +98,11 @@ static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const cha u32 log_start = only_new_msgs ? READ_ONCE(log->read_index) : 0; u32 log_end = READ_ONCE(log->write_index); + if (log_start >= data_size) + log_start = 0; + if (log_end > data_size) + log_end = data_size; + if (log->wrap_count == log->read_wrap_count) { if (log_end <= log_start) { drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name); diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index f47df092bb0d..9347f05a2b79 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -276,7 +276,7 @@ int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, if (ipc_buf) memcpy(ipc_buf, rx_msg->ipc_hdr, sizeof(*ipc_buf)); if (rx_msg->jsm_msg) { - u32 size = min_t(int, rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg)); + u32 size = min(rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg)); if (rx_msg->jsm_msg->result != VPU_JSM_STATUS_SUCCESS) { ivpu_err(vdev, "IPC resp result error: %d\n", rx_msg->jsm_msg->result); diff --git a/drivers/accel/ivpu/ivpu_ms.c b/drivers/accel/ivpu/ivpu_ms.c index be43851f5f32..cd176e77b9a0 100644 --- a/drivers/accel/ivpu/ivpu_ms.c +++ b/drivers/accel/ivpu/ivpu_ms.c @@ -291,6 +291,13 @@ int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file * if (ret) goto unlock; + if (info_size > ivpu_bo_size(bo)) { + ivpu_warn_ratelimited(vdev, "MS info overflow: %#llx > %#zx\n", + info_size, ivpu_bo_size(bo)); + ret = -EOVERFLOW; + goto unlock; + } + if (args->buffer_size < info_size) { ret = -ENOSPC; goto unlock; diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocket_gem.c index c8084719208a..a5fffa51ff35 100644 --- a/drivers/accel/rocket/rocket_gem.c +++ b/drivers/accel/rocket/rocket_gem.c @@ -79,11 +79,6 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * rkt_obj->size = args->size; rkt_obj->offset = 0; - ret = drm_gem_handle_create(file, gem_obj, &args->handle); - drm_gem_object_put(gem_obj); - if (ret) - goto err; - sgt = drm_gem_shmem_get_pages_sgt(shmem_obj); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); @@ -95,6 +90,8 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * rkt_obj->size, PAGE_SIZE, 0, 0); mutex_unlock(&rocket_priv->mm_lock); + if (ret) + goto err; ret = iommu_map_sgtable(rocket_priv->domain->domain, rkt_obj->mm.start, @@ -112,8 +109,18 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); args->dma_address = rkt_obj->mm.start; + ret = drm_gem_handle_create(file, gem_obj, &args->handle); + if (ret) + goto err_unmap; + + drm_gem_object_put(gem_obj); + return 0; +err_unmap: + iommu_unmap(rocket_priv->domain->domain, + rkt_obj->mm.start, rkt_obj->size); + err_remove_node: mutex_lock(&rocket_priv->mm_lock); drm_mm_remove_node(&rkt_obj->mm); diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 27f31744f29e..f19d6dd43473 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -193,6 +193,7 @@ static const struct dmi_system_id ac_dmi_table[] __initconst = { static int acpi_ac_probe(struct platform_device *pdev) { struct power_supply_config psy_cfg = {}; + struct device *dev = &pdev->dev; struct acpi_device *adev; struct acpi_ac *ac; int result; @@ -201,7 +202,7 @@ static int acpi_ac_probe(struct platform_device *pdev) if (!adev) return -ENODEV; - ac = kzalloc_obj(struct acpi_ac); + ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL); if (!ac) return -ENOMEM; @@ -211,7 +212,7 @@ static int acpi_ac_probe(struct platform_device *pdev) result = acpi_ac_get_state(ac); if (result) - goto err_release_ac; + return result; psy_cfg.drv_data = ac; @@ -220,33 +221,22 @@ static int acpi_ac_probe(struct platform_device *pdev) ac->charger_desc.properties = ac_props; ac->charger_desc.num_properties = ARRAY_SIZE(ac_props); ac->charger_desc.get_property = get_ac_property; - ac->charger = power_supply_register(&pdev->dev, - &ac->charger_desc, &psy_cfg); - if (IS_ERR(ac->charger)) { - result = PTR_ERR(ac->charger); - goto err_release_ac; - } + ac->charger = devm_power_supply_register(dev, &ac->charger_desc, &psy_cfg); + if (IS_ERR(ac->charger)) + return PTR_ERR(ac->charger); pr_info("AC Adapter [%s] (%s-line)\n", acpi_device_bid(adev), str_on_off(ac->state)); + result = devm_acpi_install_notify_handler(dev, ACPI_ALL_NOTIFY, + acpi_ac_notify, ac); + if (result) + return result; + ac->battery_nb.notifier_call = acpi_ac_battery_notify; register_acpi_notifier(&ac->battery_nb); - result = acpi_dev_install_notify_handler(adev, ACPI_ALL_NOTIFY, - acpi_ac_notify, ac); - if (result) - goto err_unregister; - return 0; - -err_unregister: - power_supply_unregister(ac->charger); - unregister_acpi_notifier(&ac->battery_nb); -err_release_ac: - kfree(ac); - - return result; } #ifdef CONFIG_PM_SLEEP @@ -271,12 +261,7 @@ static void acpi_ac_remove(struct platform_device *pdev) { struct acpi_ac *ac = platform_get_drvdata(pdev); - acpi_dev_remove_notify_handler(ac->device, ACPI_ALL_NOTIFY, - acpi_ac_notify); - power_supply_unregister(ac->charger); unregister_acpi_notifier(&ac->battery_nb); - - kfree(ac); } static struct platform_driver acpi_ac_driver = { diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index bed0791c17fc..008bd0552cb7 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -181,6 +181,17 @@ static const struct apd_device_desc hip08_spi_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 250000000, }; + +static const struct apd_device_desc leca_spi_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 400000000, +}; + +static const struct apd_device_desc leca_i2c_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 250000000, +}; + #endif /* CONFIG_ARM64 */ #endif @@ -251,6 +262,8 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { { "HISI02A2", APD_ADDR(hip08_i2c_desc) }, { "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) }, { "HISI0173", APD_ADDR(hip08_spi_desc) }, + { "LECA0002", APD_ADDR(leca_spi_desc) }, + { "LECA0003", APD_ADDR(leca_i2c_desc) }, { "NXP0001", APD_ADDR(nxp_i2c_desc) }, #endif { } diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index 8f1aeae8b72e..79ce6e72bf29 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -550,7 +550,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, return AE_TYPE; } - acpi_ipmi_msg_get(tx_msg); mutex_lock(&driver_data.ipmi_lock); /* Do not add a tx_msg that can not be flushed. */ if (ipmi_device->dead) { @@ -558,6 +557,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, ipmi_msg_release(tx_msg); return AE_NOT_EXIST; } + acpi_ipmi_msg_get(tx_msg); spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index ec94b09bb747..dc6d0091d17c 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -31,6 +31,8 @@ static DEFINE_MUTEX(isolated_cpus_lock); static DEFINE_MUTEX(round_robin_lock); +static bool acpi_pad_teardown; + static unsigned int power_saving_mwait_eax; static unsigned char tsc_detected_unstable; @@ -334,8 +336,8 @@ static ssize_t idlecpus_store(struct device *dev, static ssize_t idlecpus_show(struct device *dev, struct device_attribute *attr, char *buf) { - return cpumap_print_to_pagebuf(false, buf, - to_cpumask(pad_busy_cpus_bits)); + return sysfs_emit(buf, "%*pb\n", + cpumask_pr_args(to_cpumask(pad_busy_cpus_bits))); } static DEVICE_ATTR_RW(idlecpus); @@ -359,6 +361,9 @@ static int acpi_pad_pur(acpi_handle handle) union acpi_object *package; int num = -1; + if (unlikely(acpi_pad_teardown)) + return -1; + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer))) return num; @@ -407,40 +412,29 @@ static void acpi_pad_handle_notify(acpi_handle handle) static void acpi_pad_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *adev = data; - - switch (event) { - case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: - acpi_pad_handle_notify(handle); - acpi_bus_generate_netlink_event("acpi_pad", - dev_name(&adev->dev), event, 0); - break; - default: + if (event != ACPI_PROCESSOR_AGGREGATOR_NOTIFY) { pr_warn("Unsupported event [0x%x]\n", event); - break; + return; } + + acpi_pad_handle_notify(handle); + acpi_bus_generate_netlink_event("acpi_pad", dev_name(data), event, 0); } static int acpi_pad_probe(struct platform_device *pdev) { - struct acpi_device *adev; + acpi_pad_teardown = false; - adev = ACPI_COMPANION(&pdev->dev); - if (!adev) - return -ENODEV; - - return acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acpi_pad_notify, adev); + return devm_acpi_install_notify_handler(&pdev->dev, ACPI_DEVICE_NOTIFY, + acpi_pad_notify, &pdev->dev); } static void acpi_pad_remove(struct platform_device *pdev) { mutex_lock(&isolated_cpus_lock); + acpi_pad_teardown = true; acpi_pad_idle_cpus(0); mutex_unlock(&isolated_cpus_lock); - - acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), - ACPI_DEVICE_NOTIFY, acpi_pad_notify); } static const struct acpi_device_id pad_device_ids[] = { diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 05793ddef787..f93e877f87f6 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -63,7 +63,7 @@ MODULE_PARM_DESC(hw_changes_brightness, * Whether the struct acpi_video_device_attrib::device_id_scheme bit should be * assumed even if not actually set. */ -static bool device_id_scheme = false; +static bool device_id_scheme; module_param(device_id_scheme, bool, 0444); static int only_lcd; @@ -76,7 +76,6 @@ static DEFINE_MUTEX(video_list_lock); static LIST_HEAD(video_bus_head); static int acpi_video_bus_probe(struct auxiliary_device *aux_dev, const struct auxiliary_device_id *id); -static void acpi_video_bus_remove(struct auxiliary_device *aux); static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data); /* @@ -99,7 +98,6 @@ MODULE_DEVICE_TABLE(auxiliary, video_bus_auxiliary_id_table); static struct auxiliary_driver acpi_video_bus = { .probe = acpi_video_bus_probe, - .remove = acpi_video_bus_remove, .id_table = video_bus_auxiliary_id_table, }; @@ -1494,10 +1492,31 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, } EXPORT_SYMBOL(acpi_video_get_edid); -static int -acpi_video_bus_get_devices(struct acpi_video_bus *video, - struct acpi_device *device) +static void acpi_video_bus_put_devices(void *data) +{ + struct acpi_video_bus *video = data; + struct acpi_video_device *dev, *next; + + mutex_lock(&video->device_list_lock); + list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { + list_del(&dev->entry); + kfree(dev); + } + mutex_unlock(&video->device_list_lock); + + kfree(video->attached_array); + video->attached_array = NULL; +} + +static int devm_acpi_video_bus_get_devices(struct device *dev, + struct acpi_video_bus *video) { + int ret; + + ret = devm_add_action(dev, acpi_video_bus_put_devices, video); + if (ret) + return ret; + /* * There are systems where video module known to work fine regardless * of broken _DOD and ignoring returned value here doesn't cause @@ -1505,7 +1524,8 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video, */ acpi_video_device_enumerate(video); - return acpi_dev_for_each_child(device, acpi_video_bus_get_one_device, video); + return acpi_dev_for_each_child(video->device, + acpi_video_bus_get_one_device, video); } /* acpi_video interface */ @@ -1923,8 +1943,9 @@ static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev) } } -static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) +static void acpi_video_bus_remove_notify_handler(void *data) { + struct acpi_video_bus *video = data; struct acpi_video_device *dev; mutex_lock(&video->device_list_lock); @@ -1939,18 +1960,23 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) video->input = NULL; } -static int acpi_video_bus_put_devices(struct acpi_video_bus *video) +static void acpi_video_bus_free(void *data) { - struct acpi_video_device *dev, *next; + struct acpi_video_bus *video = data; - mutex_lock(&video->device_list_lock); - list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { - list_del(&dev->entry); - kfree(dev); - } - mutex_unlock(&video->device_list_lock); + video->device->driver_data = NULL; + kfree(video); +} - return 0; +static void acpi_video_bus_del(void *data) +{ + struct acpi_video_bus *video = data; + + mutex_lock(&video_list_lock); + list_del(&video->entry); + mutex_unlock(&video_list_lock); + + acpi_video_bus_unregister_backlight(video); } static int duplicate_dev_check(struct device *sibling, void *data) @@ -1978,7 +2004,8 @@ static bool acpi_video_bus_dev_is_duplicate(struct device *dev) static int acpi_video_bus_probe(struct auxiliary_device *aux_dev, const struct auxiliary_device_id *id_unused) { - struct acpi_device *device = ACPI_COMPANION(&aux_dev->dev); + struct device *dev = &aux_dev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); static DEFINE_MUTEX(probe_lock); struct acpi_video_bus *video; static int instance; @@ -1988,7 +2015,7 @@ static int acpi_video_bus_probe(struct auxiliary_device *aux_dev, /* Probe one video bus device at a time in case there are duplicates. */ guard(mutex)(&probe_lock); - if (!allow_duplicates && acpi_video_bus_dev_is_duplicate(&aux_dev->dev)) { + if (!allow_duplicates && acpi_video_bus_dev_is_duplicate(dev)) { pr_info(FW_BUG "Duplicate ACPI video bus devices for the" " same VGA controller, please try module " @@ -2001,6 +2028,13 @@ static int acpi_video_bus_probe(struct auxiliary_device *aux_dev, if (!video) return -ENOMEM; + video->device = device; + device->driver_data = video; + + error = devm_add_action_or_reset(dev, acpi_video_bus_free, video); + if (error) + return error; + /* * A hack to fix the duplicate name "VID" problem on T61 and the * duplicate name "VGA" problem on Pa 3553. @@ -2015,20 +2049,17 @@ static int acpi_video_bus_probe(struct auxiliary_device *aux_dev, auxiliary_set_drvdata(aux_dev, video); - video->device = device; - device->driver_data = video; - acpi_video_bus_find_cap(video); error = acpi_video_bus_check(video); if (error) - goto err_free_video; + return error; mutex_init(&video->device_list_lock); INIT_LIST_HEAD(&video->video_device_list); - error = acpi_video_bus_get_devices(video, device); + error = devm_acpi_video_bus_get_devices(dev, video); if (error) - goto err_put_video; + return error; /* * HP ZBook Fury 16 G10 requires ACPI video's child devices have _PS0 @@ -2040,10 +2071,6 @@ static int acpi_video_bus_probe(struct auxiliary_device *aux_dev, acpi_device_bid(device), str_yes_no(video->flags.multihead), str_yes_no(video->flags.rom), str_yes_no(video->flags.post)); - mutex_lock(&video_list_lock); - list_add_tail(&video->entry, &video_bus_head); - mutex_unlock(&video_list_lock); - /* * If backlight-type auto-detection is used then a native backlight may * show up later and this may change the result from video to native. @@ -2059,53 +2086,25 @@ static int acpi_video_bus_probe(struct auxiliary_device *aux_dev, !auto_detect) acpi_video_bus_register_backlight(video); - error = acpi_video_bus_add_notify_handler(video, &aux_dev->dev); - if (error) - goto err_del; - - error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_video_bus_notify, video); - if (error) - goto err_remove; - - return 0; - -err_remove: - acpi_video_bus_remove_notify_handler(video); -err_del: mutex_lock(&video_list_lock); - list_del(&video->entry); + list_add_tail(&video->entry, &video_bus_head); mutex_unlock(&video_list_lock); - acpi_video_bus_unregister_backlight(video); -err_put_video: - acpi_video_bus_put_devices(video); - kfree(video->attached_array); -err_free_video: - kfree(video); - device->driver_data = NULL; - - return error; -} -static void acpi_video_bus_remove(struct auxiliary_device *aux_dev) -{ - struct acpi_video_bus *video = auxiliary_get_drvdata(aux_dev); - struct acpi_device *device = ACPI_COMPANION(&aux_dev->dev); - - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_video_bus_notify); + error = devm_add_action_or_reset(dev, acpi_video_bus_del, video); + if (error) + return error; - mutex_lock(&video_list_lock); - list_del(&video->entry); - mutex_unlock(&video_list_lock); + error = acpi_video_bus_add_notify_handler(video, dev); + if (error) + return error; - acpi_video_bus_remove_notify_handler(video); - acpi_video_bus_unregister_backlight(video); - acpi_video_bus_put_devices(video); + error = devm_add_action_or_reset(dev, acpi_video_bus_remove_notify_handler, + video); + if (error) + return error; - kfree(video->attached_array); - kfree(video); - device->driver_data = NULL; + return devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY, + acpi_video_bus_notify, video); } static int __init is_i740(struct pci_dev *dev) diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h index d7d4649ce66f..16e7bffef798 100644 --- a/drivers/acpi/acpica/acapps.h +++ b/drivers/acpi/acpica/acapps.h @@ -3,7 +3,7 @@ * * Module Name: acapps - common include for ACPI applications/tools * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -17,7 +17,7 @@ /* Common info for tool signons */ #define ACPICA_NAME "Intel ACPI Component Architecture" -#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2025 Intel Corporation" +#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2026 Intel Corporation" #if ACPI_MACHINE_WIDTH == 64 #define ACPI_WIDTH " (64-bit version)" diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h index 662231f4f881..b1cf926ebace 100644 --- a/drivers/acpi/acpica/accommon.h +++ b/drivers/acpi/acpica/accommon.h @@ -3,7 +3,7 @@ * * Name: accommon.h - Common include files for generation of ACPICA source * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acconvert.h b/drivers/acpi/acpica/acconvert.h index 24998f2d7539..22bd6712831d 100644 --- a/drivers/acpi/acpica/acconvert.h +++ b/drivers/acpi/acpica/acconvert.h @@ -3,7 +3,7 @@ * * Module Name: acapps - common include for ACPI applications/tools * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index 91241bd6917a..09ab8b3db7ed 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -3,7 +3,7 @@ * * Name: acdebug.h - ACPI/AML debugger * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h index 5d48a344b35f..f6208722503b 100644 --- a/drivers/acpi/acpica/acdispat.h +++ b/drivers/acpi/acpica/acdispat.h @@ -3,7 +3,7 @@ * * Name: acdispat.h - dispatcher (parser to interpreter interface) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index b40fb3a5ac8a..ccdc3725c773 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -3,7 +3,7 @@ * * Name: acevents.h - Event subcomponent prototypes and defines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index c8a750d2674c..b6e31517650e 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -3,7 +3,7 @@ * * Name: acglobal.h - Declarations for global variables * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 6aec56c65fa0..78323df00ceb 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -3,7 +3,7 @@ * * Name: achware.h -- hardware specific interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index 1ee6ac9b2baf..a62a4e9e23d6 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -3,7 +3,7 @@ * * Name: acinterp.h - Interpreter subcomponent prototypes and defines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index f98640086f4e..1ecc700f6e88 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -3,7 +3,7 @@ * * Name: aclocal.h - Internal data types used across the ACPI subsystem * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -169,6 +169,7 @@ struct acpi_namespace_node { #define ANOBJ_IS_EXTERNAL 0x08 /* iASL only: This object created via External() */ #define ANOBJ_METHOD_NO_RETVAL 0x10 /* iASL only: Method has no return value */ #define ANOBJ_METHOD_SOME_NO_RETVAL 0x20 /* iASL only: Method has at least one return value */ +#define ANOBJ_IS_ALIAS 0x40 /* iASL only: Node is an alias to another node */ #define ANOBJ_IS_REFERENCED 0x80 /* iASL only: Object was referenced */ /* Internal ACPI table management - master table list */ diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 4e9402c02410..3a7b6dbcdf9d 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -3,7 +3,7 @@ * * Name: acmacros.h - C macros for the entire subsystem. * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 13f050fecb49..b5830e73ca10 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -3,7 +3,7 @@ * * Name: acnamesp.h - Namespace subcomponent prototypes and defines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 6ffcc7a0a0c2..9a82e4f734ba 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -3,7 +3,7 @@ * * Name: acobject.h - Definition of union acpi_operand_object (Internal object only) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h index a2a9e51d7ac6..d8a0beb0eaed 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -3,7 +3,7 @@ * * Name: acopcode.h - AML opcode information for the AML parser and interpreter * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index 65a15dee092b..5393a11bc924 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -3,7 +3,7 @@ * * Module Name: acparser.h - AML Parser subcomponent prototypes and defines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index 07d5790d09f8..2208dc7aff8d 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -3,7 +3,7 @@ * * Name: acpredef - Information table for ACPI predefined methods and objects * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h index e8a92be5adae..c3dfa92a89a7 100644 --- a/drivers/acpi/acpica/acresrc.h +++ b/drivers/acpi/acpica/acresrc.h @@ -3,7 +3,7 @@ * * Name: acresrc.h - Resource Manager function prototypes * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h index e690f604cfa0..5c8c14f55faa 100644 --- a/drivers/acpi/acpica/acstruct.h +++ b/drivers/acpi/acpica/acstruct.h @@ -3,7 +3,7 @@ * * Name: acstruct.h - Internal structs * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index ebef72bf58d0..1334f8643f76 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -3,7 +3,7 @@ * * Name: actables.h - ACPI table management * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 3990d509bbab..9a18cdbfd60f 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -3,7 +3,7 @@ * * Name: acutils.h -- prototypes for the common (subsystem-wide) procedures * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index c5b544a006c5..663c6146d6e2 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -5,7 +5,7 @@ * Declarations and definitions contained herein are derived * directly from the ACPI specification. * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h index 54d6e51e0b9a..2c725680590b 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h @@ -3,7 +3,7 @@ * * Module Name: amlresrc.h - AML resource descriptors * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dbhistry.c b/drivers/acpi/acpica/dbhistry.c index 554ae35108bd..beec446bf94a 100644 --- a/drivers/acpi/acpica/dbhistry.c +++ b/drivers/acpi/acpica/dbhistry.c @@ -3,7 +3,7 @@ * * Module Name: dbhistry - debugger HISTORY command * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c index e2f00c54cb36..8a72ff39b5a2 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -4,7 +4,7 @@ * Module Name: dsargs - Support for execution of dynamic arguments for static * objects (regions, fields, buffer fields, etc.) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c index c1f79d7a2026..90f16c03fd72 100644 --- a/drivers/acpi/acpica/dscontrol.c +++ b/drivers/acpi/acpica/dscontrol.c @@ -4,7 +4,7 @@ * Module Name: dscontrol - Support for execution control opcodes - * if/else/while/return * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dsdebug.c b/drivers/acpi/acpica/dsdebug.c index 274b74255551..691c001591fa 100644 --- a/drivers/acpi/acpica/dsdebug.c +++ b/drivers/acpi/acpica/dsdebug.c @@ -3,7 +3,7 @@ * * Module Name: dsdebug - Parser/Interpreter interface - debugging * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index df132c9089c7..d7d56cc600d3 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -3,7 +3,7 @@ * * Module Name: dsfield - Dispatcher field routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c index 57cd9e2d1109..f73e68090c80 100644 --- a/drivers/acpi/acpica/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -3,7 +3,7 @@ * * Module Name: dsinit - Object initialization namespace walk * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 45ec32e81903..3b2ced5ab6c8 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -3,7 +3,7 @@ * * Module Name: dsmethod - Parser/Interpreter interface - control method parsing * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -705,6 +705,8 @@ void acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, struct acpi_walk_state *walk_state) { + u32 i; + struct acpi_namespace_node *ref_node; ACPI_FUNCTION_TRACE_PTR(ds_terminate_control_method, walk_state); @@ -715,6 +717,47 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, } if (walk_state) { + /* + * Check if the return value is a ref_of reference to a method local + * or argument. If so, clear the reference to avoid use-after-free + * when the walk state is deleted. + */ + if (walk_state->return_desc && + (walk_state->return_desc->common.type == + ACPI_TYPE_LOCAL_REFERENCE) + && (walk_state->return_desc->reference.class == + ACPI_REFCLASS_REFOF)) { + ref_node = walk_state->return_desc->reference.object; + if (ref_node) { + + /* Check against method locals */ + for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) { + if (ref_node == + &walk_state->local_variables[i]) { + acpi_ut_remove_reference + (walk_state->return_desc); + walk_state->return_desc = NULL; + break; + } + } + + /* Check against method arguments if not already cleared */ + if (walk_state->return_desc) { + for (i = 0; i < ACPI_METHOD_NUM_ARGS; + i++) { + if (ref_node == + &walk_state->arguments[i]) { + acpi_ut_remove_reference + (walk_state-> + return_desc); + walk_state-> + return_desc = NULL; + break; + } + } + } + } + } /* Delete all arguments and locals */ diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index 1bf7eec49899..25dd034c911d 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -3,7 +3,7 @@ * * Module Name: dsobject - Dispatcher object management routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index 5699b0872848..fa13086e304c 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -3,7 +3,7 @@ * * Module Name: dsopcode - Dispatcher support for regions and fields * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dspkginit.c b/drivers/acpi/acpica/dspkginit.c index 1ed2386fab82..1a33c2f63c84 100644 --- a/drivers/acpi/acpica/dspkginit.c +++ b/drivers/acpi/acpica/dspkginit.c @@ -3,7 +3,7 @@ * * Module Name: dspkginit - Completion of deferred package initialization * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index 5c5c6d8a4e48..675aaa671012 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -4,7 +4,7 @@ * Module Name: dswexec - Dispatcher method execution callbacks; * dispatch to interpreter. * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 666419b6a5c6..5a3709aa6c77 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -3,7 +3,7 @@ * * Module Name: dswload - Dispatcher first pass namespace load callbacks * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c index bfc54c914757..277ae080df78 100644 --- a/drivers/acpi/acpica/dswload2.c +++ b/drivers/acpi/acpica/dswload2.c @@ -3,7 +3,7 @@ * * Module Name: dswload2 - Dispatcher second pass namespace load callbacks * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c index 375a8fa43d9d..7fef0a058393 100644 --- a/drivers/acpi/acpica/dswscope.c +++ b/drivers/acpi/acpica/dswscope.c @@ -3,7 +3,7 @@ * * Module Name: dswscope - Scope stack manipulation * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index 02aaddb89df9..5e948854f78a 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -3,7 +3,7 @@ * * Module Name: dswstate - Dispatcher parse tree walk management routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index 6cdd39c987b8..8b12e91b8903 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -3,7 +3,7 @@ * * Module Name: evevent - Fixed Event handling and dispatch * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c index df2a4ab0e0da..d1a266ebb6ef 100644 --- a/drivers/acpi/acpica/evglock.c +++ b/drivers/acpi/acpica/evglock.c @@ -3,7 +3,7 @@ * * Module Name: evglock - Global Lock support * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index ba65b2ea49b2..53b52efc7c2b 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -3,7 +3,7 @@ * * Module Name: evgpe - General Purpose Event handling and dispatch * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index fadd93caf1d5..47d8d50aab9b 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -3,7 +3,7 @@ * * Module Name: evgpeblk - GPE block creation and initialization. * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index eb769739420e..33174496bb0c 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -3,7 +3,7 @@ * * Module Name: evgpeinit - System GPE initialization and update * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index d15b1d75c8ec..4f8af92dd0cc 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -3,7 +3,7 @@ * * Module Name: evgpeutil - GPE utilities * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c index 5a35dae945e2..d9a3b1ca5b9c 100644 --- a/drivers/acpi/acpica/evhandler.c +++ b/drivers/acpi/acpica/evhandler.c @@ -3,7 +3,7 @@ * * Module Name: evhandler - Support for Address Space handlers * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -130,6 +130,14 @@ acpi_ev_has_default_handler(struct acpi_namespace_node *node, /* Walk the linked list of handlers for this object */ while (handler_obj) { + + /* Validate handler object type before accessing fields */ + + if (handler_obj->common.type != + ACPI_TYPE_LOCAL_ADDRESS_HANDLER) { + break; + } + if (handler_obj->address_space.space_id == space_id) { if (handler_obj->address_space.handler_flags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) { @@ -292,6 +300,9 @@ union acpi_operand_object *acpi_ev_find_region_handler(acpi_adr_space_type /* Walk the handler list for this device */ while (handler_obj) { + if (handler_obj->common.type != ACPI_TYPE_LOCAL_ADDRESS_HANDLER) { + break; + } /* Same space_id indicates a handler is installed */ diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index 04a23a6c3bb1..723db68626ea 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -3,7 +3,7 @@ * * Module Name: evmisc - Miscellaneous event manager support functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index b6198f73c81d..96423b38f240 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -3,7 +3,7 @@ * * Module Name: evregion - Operation Region support * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index b03952798af5..4e64e58b72ef 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -3,7 +3,7 @@ * * Module Name: evrgnini- ACPI address_space (op_region) init * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 86a8d41c079c..da4e45f9f2d8 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -3,7 +3,7 @@ * * Module Name: evxface - External interfaces for ACPI events * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 4b052908d2e7..1819737905bf 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -3,7 +3,7 @@ * * Module Name: evxfevnt - External Interfaces, ACPI event disable/enable * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 60dacec1b121..4be8f6e6976e 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -3,7 +3,7 @@ * * Module Name: evxfgpe - External Interfaces for General Purpose Events (GPEs) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -78,18 +78,22 @@ ACPI_EXPORT_SYMBOL(acpi_update_all_gpes) /******************************************************************************* * - * FUNCTION: acpi_enable_gpe + * FUNCTION: acpi_enable_gpe_cond * * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block + * dispatch_type - GPE dispatch type to match * * RETURN: Status * - * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is - * hardware-enabled. + * DESCRIPTION: Add a reference to a GPE so long as its dispatch type matches + * the supplied one, or it is different from ACPI_GPE_DISPATCH_NONE + * if the supplied one is ACPI_GPE_DISPATCH_MASK. On the first + * reference, the GPE is hardware-enabled. * ******************************************************************************/ -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) +acpi_status acpi_enable_gpe_cond(acpi_handle gpe_device, u32 gpe_number, + u8 dispatch_type) { acpi_status status = AE_BAD_PARAMETER; struct acpi_gpe_event_info *gpe_event_info; @@ -100,14 +104,18 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* - * Ensure that we have a valid GPE number and that there is some way - * of handling the GPE (handler or a GPE method). In other words, we - * won't allow a valid GPE to be enabled if there is no way to handle it. + * Ensure that we have a valid GPE number and that the dispatch type of + * the GPE matches the supplied one (or it is not ACPI_GPE_DISPATCH_NONE + * if the supplied one is ACPI_GPE_DISPATCH_MASK). */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); if (gpe_event_info) { - if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != - ACPI_GPE_DISPATCH_NONE) { + if (dispatch_type == ACPI_GPE_DISPATCH_MASK) + dispatch_type = ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags); + else if (dispatch_type != ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) + dispatch_type = ACPI_GPE_DISPATCH_NONE; + + if (dispatch_type != ACPI_GPE_DISPATCH_NONE) { status = acpi_ev_add_gpe_reference(gpe_event_info, TRUE); if (ACPI_SUCCESS(status) && ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) { @@ -128,6 +136,30 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } +ACPI_EXPORT_SYMBOL(acpi_enable_gpe_cond) + +/******************************************************************************* + * + * FUNCTION: acpi_enable_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is + * hardware-enabled. + * + ******************************************************************************/ +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + /* + * Ensure that there is some way of handling the GPE (handler or a GPE + * method). In other words, we won't allow a valid GPE to be enabled if + * there is no way to handle it. + */ + return acpi_enable_gpe_cond(gpe_device, gpe_number, ACPI_GPE_DISPATCH_MASK); +} ACPI_EXPORT_SYMBOL(acpi_enable_gpe) /******************************************************************************* diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index bccc672c934c..177f80da1c7d 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -4,7 +4,7 @@ * Module Name: evxfregn - External Interfaces, ACPI Operation Regions and * Address Spaces. * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exconcat.c b/drivers/acpi/acpica/exconcat.c index c248c9b162fa..107df9d05fe6 100644 --- a/drivers/acpi/acpica/exconcat.c +++ b/drivers/acpi/acpica/exconcat.c @@ -3,7 +3,7 @@ * * Module Name: exconcat - Concatenate-type AML operators * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 4d7dd0fc6b07..da39d578595b 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -3,7 +3,7 @@ * * Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -90,6 +90,8 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, union acpi_operand_object *return_obj; union acpi_operand_object *ddb_handle; u32 table_index; + char oem_id[ACPI_OEM_ID_SIZE + 1]; + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; ACPI_FUNCTION_TRACE(ex_load_table_op); @@ -102,12 +104,32 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, *return_desc = return_obj; + /* + * Validate OEM ID and OEM Table ID string lengths. + * acpi_tb_find_table expects strings that can safely read + * ACPI_OEM_ID_SIZE and ACPI_OEM_TABLE_ID_SIZE bytes. + */ + if ((operand[1]->string.length > ACPI_OEM_ID_SIZE) || + (operand[2]->string.length > ACPI_OEM_TABLE_ID_SIZE)) { + return_ACPI_STATUS(AE_AML_STRING_LIMIT); + } + + /* + * Copy OEM strings to local buffers with guaranteed null-termination. + * This prevents heap-buffer-overflow when acpi_tb_find_table reads + * ACPI_OEM_ID_SIZE/ACPI_OEM_TABLE_ID_SIZE bytes. + */ + memcpy(oem_id, operand[1]->string.pointer, operand[1]->string.length); + oem_id[operand[1]->string.length] = 0; + memcpy(oem_table_id, operand[2]->string.pointer, + operand[2]->string.length); + oem_table_id[operand[2]->string.length] = 0; + /* Find the ACPI table in the RSDT/XSDT */ acpi_ex_exit_interpreter(); status = acpi_tb_find_table(operand[0]->string.pointer, - operand[1]->string.pointer, - operand[2]->string.pointer, &table_index); + oem_id, oem_table_id, &table_index); acpi_ex_enter_interpreter(); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) { diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index fded9bfc2436..b51f20d0978e 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -3,7 +3,7 @@ * * Module Name: exconvrt - Object conversion routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index 052c69567997..64f39274d370 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -3,7 +3,7 @@ * * Module Name: excreate - Named object creation * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c index 81a07a52b73c..a592bcc5d726 100644 --- a/drivers/acpi/acpica/exdebug.c +++ b/drivers/acpi/acpica/exdebug.c @@ -3,7 +3,7 @@ * * Module Name: exdebug - Support for stores to the AML Debug Object * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index d8aeebaab70a..56500b2bedaa 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -3,7 +3,7 @@ * * Module Name: exdump - Interpreter debug output routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index ced3ff9d0a86..9a55524ed8f4 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -3,7 +3,7 @@ * * Module Name: exfield - AML execution - field_unit read/write * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 0771934c0455..bdd8e6ec3fea 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -3,7 +3,7 @@ * * Module Name: exfldio - Aml Field I/O * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index 07cbac58ed21..e67d3d547990 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -3,7 +3,7 @@ * * Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index 1fa013197fcf..cc0f9978e461 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -3,7 +3,7 @@ * * Module Name: exmutex - ASL Mutex Acquire/Release functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c index 76ab73c37e90..3ae2bd8aa3c3 100644 --- a/drivers/acpi/acpica/exnames.c +++ b/drivers/acpi/acpica/exnames.c @@ -3,7 +3,7 @@ * * Module Name: exnames - interpreter/scanner name load/execute * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 6ac7e0ca5c9d..7a6e4c6d9d7b 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -3,7 +3,7 @@ * * Module Name: exoparg1 - AML execution - opcodes with 1 argument * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c index a94fa4d70e99..ca434470435f 100644 --- a/drivers/acpi/acpica/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -3,7 +3,7 @@ * * Module Name: exoparg2 - AML execution - opcodes with 2 arguments * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index 2fc8070814e3..3a0559a3f531 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -3,7 +3,7 @@ * * Module Name: exoparg3 - AML execution - opcodes with 3 arguments * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -159,7 +159,7 @@ acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state) /* Truncate request if larger than the actual String/Buffer */ - else if ((index + length) > operand[0]->string.length) { + else if ((index + length) > operand[0]->string.length || (index + length) < index) { /* Check for overflow */ length = (acpi_size)operand[0]->string.length - (acpi_size)index; diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index cb078e39abf7..ab502bf27d87 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -3,7 +3,7 @@ * * Module Name: exoparg6 - AML execution - opcodes with 6 arguments * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index 1b1a006e82de..3acfa60d27d9 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -3,7 +3,7 @@ * * Module Name: exprep - ACPI AML field prep utilities * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index a390a1c2b0ab..fc144919e493 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -3,7 +3,7 @@ * * Module Name: exregion - ACPI default op_region (address space) handlers * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c index dd83631090fc..1c6c3d531947 100644 --- a/drivers/acpi/acpica/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -3,7 +3,7 @@ * * Module Name: exresnte - AML Interpreter object resolution * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index 4589de3f3012..029918333e29 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -3,7 +3,7 @@ * * Module Name: exresolv - AML Interpreter object resolution * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index 782ee353a709..8127d4046d0b 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -3,7 +3,7 @@ * * Module Name: exresop - AML Interpreter operand/object resolution * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exserial.c b/drivers/acpi/acpica/exserial.c index 6d2581ec22ad..835ea4b2822a 100644 --- a/drivers/acpi/acpica/exserial.c +++ b/drivers/acpi/acpica/exserial.c @@ -3,7 +3,7 @@ * * Module Name: exserial - field_unit support for serial address spaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index cbc42207496d..86be6bce1648 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -3,7 +3,7 @@ * * Module Name: exstore - AML Interpreter object store support * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c index 0470b2639831..a047f7337aee 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -4,7 +4,7 @@ * Module Name: exstoren - AML Interpreter object store support, * Store to Node (namespace object) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c index 5b168fbc03e8..8d17412085ed 100644 --- a/drivers/acpi/acpica/exstorob.c +++ b/drivers/acpi/acpica/exstorob.c @@ -3,7 +3,7 @@ * * Module Name: exstorob - AML object store support, store to object * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c index 7f843c9d8a06..41d6f8f7a368 100644 --- a/drivers/acpi/acpica/exsystem.c +++ b/drivers/acpi/acpica/exsystem.c @@ -3,7 +3,7 @@ * * Module Name: exsystem - Interface to OS services * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c index 36934d4f26fb..4a6c7fb147d7 100644 --- a/drivers/acpi/acpica/extrace.c +++ b/drivers/acpi/acpica/extrace.c @@ -3,7 +3,7 @@ * * Module Name: extrace - Support for interpreter execution tracing * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index cc10c0732218..a1aa89ad8f03 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -3,7 +3,7 @@ * * Module Name: exutils - interpreter/scanner utilities * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c index a1e1fa787566..e70f60364bba 100644 --- a/drivers/acpi/acpica/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -3,7 +3,7 @@ * * Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c index 631fd8e2b774..73f60d735b1e 100644 --- a/drivers/acpi/acpica/hwesleep.c +++ b/drivers/acpi/acpica/hwesleep.c @@ -4,7 +4,7 @@ * Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the * extended FADT-V5 sleep registers. * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 386f4759c317..98d3662507b4 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -3,7 +3,7 @@ * * Module Name: hwgpe - Low level GPE enable/disable/clear functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index 87d78bef6323..d0d46765c5c1 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -4,7 +4,7 @@ * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the * original/legacy sleep/PM registers. * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c index a5e0bccae6a4..c08bbaa956a6 100644 --- a/drivers/acpi/acpica/hwtimer.c +++ b/drivers/acpi/acpica/hwtimer.c @@ -3,7 +3,7 @@ * * Name: hwtimer.c - ACPI Power Management Timer Interface * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c index 496fd9e49f0b..abbe3042bf3f 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -3,7 +3,7 @@ * * Module Name: hwvalid - I/O request validation * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 847cd1b2493d..980ea76106b7 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -3,7 +3,7 @@ * * Module Name: hwxface - Public ACPICA hardware interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c index 9aabe30416da..dd70fcb99637 100644 --- a/drivers/acpi/acpica/hwxfsleep.c +++ b/drivers/acpi/acpica/hwxfsleep.c @@ -3,7 +3,7 @@ * * Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsarguments.c b/drivers/acpi/acpica/nsarguments.c index 366d54a1d157..e00cce28d714 100644 --- a/drivers/acpi/acpica/nsarguments.c +++ b/drivers/acpi/acpica/nsarguments.c @@ -3,7 +3,7 @@ * * Module Name: nsarguments - Validation of args for ACPI predefined methods * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c index f05a92b88642..b903f70e41bb 100644 --- a/drivers/acpi/acpica/nsconvert.c +++ b/drivers/acpi/acpica/nsconvert.c @@ -4,7 +4,7 @@ * Module Name: nsconvert - Object conversions for objects returned by * predefined methods * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 6dc20486ad51..15bb57e36131 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -3,7 +3,7 @@ * * Module Name: nsdump - table dumping routines for debug * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index d5b16aaec233..7cbb0786f7cc 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -3,7 +3,7 @@ * * Module Name: nsdump - table dumping routines for debug * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c index 03373e7f7978..70453eb63373 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -3,7 +3,7 @@ * * Module Name: nsinit - namespace initialization * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c index 6ec4c646fff7..e89998d823d2 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -3,7 +3,7 @@ * * Module Name: nsload - namespace loading/expanding/contracting procedures * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index 22aeeeb56cff..19802da865c5 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -222,6 +222,12 @@ acpi_ns_build_normalized_path(struct acpi_namespace_node *node, goto build_trailing_null; } + /* Validate the Node to avoid use-after-free vulnerabilities */ + + if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { + goto build_trailing_null; + } + next_node = node; while (next_node && next_node != acpi_gbl_root_node) { if (next_node != node) { diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c index 79d86da1c892..a4ccacecca53 100644 --- a/drivers/acpi/acpica/nsobject.c +++ b/drivers/acpi/acpica/nsobject.c @@ -173,6 +173,12 @@ void acpi_ns_detach_object(struct acpi_namespace_node *node) obj_desc = node->object; + /* Alias nodes point directly to other namespace nodes; skip teardown */ + if (node->flags & ANOBJ_IS_ALIAS) { + node->object = NULL; + return_VOID; + } + if (!obj_desc || (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) { return_VOID; } diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c index 959e6379bc4c..1e9c70c388ec 100644 --- a/drivers/acpi/acpica/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -3,7 +3,7 @@ * * Module Name: nsparse - namespace interface to AML parser * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 81995ee48c49..d7b4f8d2e461 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -3,7 +3,7 @@ * * Module Name: nspredef - Validation of ACPI predefined methods and objects * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c index ca137ce5674f..f1c510ca76a3 100644 --- a/drivers/acpi/acpica/nsprepkg.c +++ b/drivers/acpi/acpica/nsprepkg.c @@ -3,7 +3,7 @@ * * Module Name: nsprepkg - Validation of package objects for predefined names * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -631,6 +631,13 @@ acpi_ns_custom_package(struct acpi_evaluate_info *info, /* Get version number, must be Integer */ + if (!(*elements)) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Return Package has a NULL version element")); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + if ((*elements)->common.type != ACPI_TYPE_INTEGER) { ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index accfdcfb7e62..8e5da0c42da2 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -3,7 +3,7 @@ * * Module Name: nsrepair - Repair for objects returned by predefined methods * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 8dbb870f40d2..62734b96b745 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -4,7 +4,7 @@ * Module Name: nsrepair2 - Repair for objects returned by specific * predefined methods * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index 49cc07e2ac5a..65b517f92972 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -4,7 +4,7 @@ * Module Name: nsutils - Utilities for accessing ACPI namespace, accessing * parents and siblings and Scope manipulation * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index 5670ff5a43cd..a37d75b5b9be 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -3,7 +3,7 @@ * * Module Name: nswalk - Functions for walking the ACPI namespace * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index b6895a48ae68..b6534187cd43 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -4,7 +4,7 @@ * Module Name: nsxfname - Public interfaces to the ACPI subsystem * ACPI Namespace oriented interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -512,6 +512,10 @@ acpi_status acpi_install_method(u8 *buffer) parser_state.aml += acpi_ps_get_opcode_size(opcode); parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state); + if ((parser_state.pkg_end > parser_state.aml_end) || + (parser_state.pkg_end < parser_state.aml)) { + return (AE_AML_PACKAGE_LIMIT); + } path = acpi_ps_get_next_namestring(&parser_state); method_flags = *parser_state.aml++; diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index 6f6ae38ec044..4643c839df7f 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -3,7 +3,7 @@ * * Module Name: psargs - Parse AML opcode arguments * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -48,6 +48,7 @@ acpi_ps_get_next_package_length(struct acpi_parse_state *parser_state) u32 package_length = 0; u32 byte_count; u8 byte_zero_mask = 0x3F; /* Default [0:5] */ + u32 remaining; ACPI_FUNCTION_TRACE(ps_get_next_package_length); @@ -55,7 +56,23 @@ acpi_ps_get_next_package_length(struct acpi_parse_state *parser_state) * Byte 0 bits [6:7] contain the number of additional bytes * used to encode the package length, either 0,1,2, or 3 */ + + /* Check if we have at least one byte to read */ + remaining = (u32)ACPI_PTR_DIFF(parser_state->aml_end, aml); + if (remaining == 0) { + return_UINT32(0); + } + byte_count = (aml[0] >> 6); + + /* Validate byte_count and ensure we have enough bytes to read */ + if (byte_count >= remaining) { + + /* Clamp to available bytes and advance to end */ + parser_state->aml = parser_state->aml_end; + return_UINT32(0); + } + parser_state->aml += ((acpi_size)byte_count + 1); /* Get bytes 3, 2, 1 as needed */ @@ -131,10 +148,16 @@ char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state) /* Point past any namestring prefix characters (backslash or carat) */ - while (ACPI_IS_ROOT_PREFIX(*end) || ACPI_IS_PARENT_PREFIX(*end)) { + while (end < parser_state->aml_end && + (ACPI_IS_ROOT_PREFIX(*end) || ACPI_IS_PARENT_PREFIX(*end))) { end++; } + if (end >= parser_state->aml_end) { + parser_state->aml = parser_state->aml_end; + return_PTR(NULL); + } + /* Decode the path prefix character */ switch (*end) { @@ -159,6 +182,11 @@ char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state) /* Multiple name segments, 4 chars each, count in next byte */ + if ((end + 1) >= parser_state->aml_end) { + parser_state->aml = parser_state->aml_end; + return_PTR(NULL); + } + end += 2 + (*(end + 1) * ACPI_NAMESEG_SIZE); break; @@ -170,6 +198,11 @@ char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state) break; } + if (end > parser_state->aml_end) { + parser_state->aml = parser_state->aml_end; + return_PTR(NULL); + } + parser_state->aml = end; return_PTR((char *)start); } @@ -367,6 +400,8 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, u32 length; u16 opcode; u8 *aml = parser_state->aml; + u32 remaining = (u32)ACPI_PTR_DIFF(parser_state->aml_end, aml); + u64 partial_value; ACPI_FUNCTION_TRACE_U32(ps_get_next_simple_arg, arg_type); @@ -376,8 +411,13 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, /* Get 1 byte from the AML stream */ opcode = AML_BYTE_OP; - arg->common.value.integer = (u64) *aml; - length = 1; + if (remaining >= 1) { + arg->common.value.integer = (u64)*aml; + length = 1; + } else { + arg->common.value.integer = 0; + length = 0; + } break; case ARGP_WORDDATA: @@ -385,8 +425,19 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, /* Get 2 bytes from the AML stream */ opcode = AML_WORD_OP; - ACPI_MOVE_16_TO_64(&arg->common.value.integer, aml); - length = 2; + if (remaining >= 2) { + ACPI_MOVE_16_TO_64(&arg->common.value.integer, aml); + length = 2; + } else { + arg->common.value.integer = 0; + length = 0; + if (remaining > 0) { + partial_value = 0; + memcpy(&partial_value, aml, remaining); + arg->common.value.integer = partial_value; + length = remaining; + } + } break; case ARGP_DWORDDATA: @@ -394,8 +445,19 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, /* Get 4 bytes from the AML stream */ opcode = AML_DWORD_OP; - ACPI_MOVE_32_TO_64(&arg->common.value.integer, aml); - length = 4; + if (remaining >= 4) { + ACPI_MOVE_32_TO_64(&arg->common.value.integer, aml); + length = 4; + } else { + arg->common.value.integer = 0; + length = 0; + if (remaining > 0) { + partial_value = 0; + memcpy(&partial_value, aml, remaining); + arg->common.value.integer = partial_value; + length = remaining; + } + } break; case ARGP_QWORDDATA: @@ -403,8 +465,19 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, /* Get 8 bytes from the AML stream */ opcode = AML_QWORD_OP; - ACPI_MOVE_64_TO_64(&arg->common.value.integer, aml); - length = 8; + if (remaining >= 8) { + ACPI_MOVE_64_TO_64(&arg->common.value.integer, aml); + length = 8; + } else { + arg->common.value.integer = 0; + length = 0; + if (remaining > 0) { + partial_value = 0; + memcpy(&partial_value, aml, remaining); + arg->common.value.integer = partial_value; + length = remaining; + } + } break; case ARGP_CHARLIST: @@ -417,10 +490,28 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, /* Find the null terminator */ length = 0; - while (aml[length]) { + while ((length < remaining) && aml[length]) { length++; } - length++; + if (length < remaining) { + + /* Account for the terminating null */ + length++; + } else { + /* + * No terminator found - add null at buffer boundary + * and report a warning + */ + ACPI_WARNING((AE_INFO, + "Invalid AML string: no null terminator, truncating at offset %u", + (u32)(aml - parser_state->aml))); + + /* Add null terminator at the boundary */ + if (remaining > 0) { + aml[remaining - 1] = 0; + length = remaining; + } + } break; case ARGP_NAME: @@ -474,6 +565,10 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state); aml = parser_state->aml; + if (aml >= parser_state->aml_end) { + return_PTR(NULL); + } + /* Determine field type */ switch (ACPI_GET8(parser_state->aml)) { @@ -522,6 +617,11 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state /* Get the 4-character name */ + if ((parser_state->aml + ACPI_NAMESEG_SIZE) > + parser_state->aml_end) { + acpi_ps_free_op(field); + return_PTR(NULL); + } ACPI_MOVE_32_TO_32(&name, parser_state->aml); acpi_ps_set_name(field, name); parser_state->aml += ACPI_NAMESEG_SIZE; @@ -567,6 +667,10 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state /* Get the two bytes (Type/Attribute) */ + if ((parser_state->aml + 2) > parser_state->aml_end) { + acpi_ps_free_op(field); + return_PTR(NULL); + } access_type = ACPI_GET8(parser_state->aml); parser_state->aml++; access_attribute = ACPI_GET8(parser_state->aml); @@ -578,6 +682,10 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state /* This opcode has a third byte, access_length */ if (opcode == AML_INT_EXTACCESSFIELD_OP) { + if (parser_state->aml >= parser_state->aml_end) { + acpi_ps_free_op(field); + return_PTR(NULL); + } access_length = ACPI_GET8(parser_state->aml); parser_state->aml++; @@ -775,6 +883,10 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state, parser_state->pkg_end = acpi_ps_get_next_package_end(parser_state); + if ((parser_state->pkg_end > parser_state->aml_end) + || (parser_state->pkg_end < parser_state->aml)) { + return_ACPI_STATUS(AE_AML_PACKAGE_LIMIT); + } break; case ARGP_FIELDLIST: diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index c989cadf271c..24a57f971c96 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -3,7 +3,7 @@ * * Module Name: psloop - Main AML parse loop * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -361,6 +361,13 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) walk_state->parser_state.aml = acpi_ps_get_next_package_end (&walk_state->parser_state); + if ((walk_state->parser_state.aml > + walk_state->parser_state.aml_end) + || (walk_state->parser_state.aml < + walk_state->aml)) { + return_ACPI_STATUS + (AE_AML_PACKAGE_LIMIT); + } walk_state->aml = walk_state->parser_state.aml; } @@ -421,11 +428,22 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) parser_state->aml = acpi_ps_get_next_package_end (parser_state); + if ((parser_state->aml > + parser_state->aml_end) + || (parser_state->aml < + walk_state->control_state-> + control.aml_predicate_start)) { + return_ACPI_STATUS + (AE_AML_PACKAGE_LIMIT); + } walk_state->aml = parser_state->aml; ACPI_ERROR((AE_INFO, "Skipping While/If block")); - if (*walk_state->aml == AML_ELSE_OP) { + if ((walk_state->aml < + parser_state->aml_end) + && (*walk_state->aml == + AML_ELSE_OP)) { ACPI_ERROR((AE_INFO, "Skipping Else block")); walk_state->parser_state.aml = @@ -433,6 +451,16 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) walk_state->parser_state.aml = acpi_ps_get_next_package_end (parser_state); + if ((walk_state->parser_state. + aml > + walk_state->parser_state. + aml_end) + || (walk_state-> + parser_state.aml < + walk_state->aml)) { + return_ACPI_STATUS + (AE_AML_PACKAGE_LIMIT); + } walk_state->aml = parser_state->aml; } diff --git a/drivers/acpi/acpica/psobject.c b/drivers/acpi/acpica/psobject.c index 496a1c1d5b0b..629cafab1930 100644 --- a/drivers/acpi/acpica/psobject.c +++ b/drivers/acpi/acpica/psobject.c @@ -3,7 +3,7 @@ * * Module Name: psobject - Support for parse objects * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c index bf6103986f48..0abb9b077cb4 100644 --- a/drivers/acpi/acpica/psopcode.c +++ b/drivers/acpi/acpica/psopcode.c @@ -3,7 +3,7 @@ * * Module Name: psopcode - Parser/Interpreter opcode information table * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c index 532ea307a675..479ce3df67f0 100644 --- a/drivers/acpi/acpica/psopinfo.c +++ b/drivers/acpi/acpica/psopinfo.c @@ -3,7 +3,7 @@ * * Module Name: psopinfo - AML opcode information functions and dispatch tables * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 55a416e56fd8..42ec8abef626 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -3,7 +3,7 @@ * * Module Name: psparse - Parser top level AML parse routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -70,6 +70,9 @@ u16 acpi_ps_peek_opcode(struct acpi_parse_state * parser_state) u16 opcode; aml = parser_state->aml; + if (aml >= parser_state->aml_end) { + return (0xFFFF); + } opcode = (u16) ACPI_GET8(aml); if (opcode == AML_EXTENDED_PREFIX) { @@ -77,6 +80,9 @@ u16 acpi_ps_peek_opcode(struct acpi_parse_state * parser_state) /* Extended opcode, get the second opcode byte */ aml++; + if (aml >= parser_state->aml_end) { + return (0xFFFF); + } opcode = (u16) ((opcode << 8) | ACPI_GET8(aml)); } @@ -300,6 +306,7 @@ acpi_ps_next_parse_state(struct acpi_walk_state *walk_state, { struct acpi_parse_state *parser_state = &walk_state->parser_state; acpi_status status = AE_CTRL_PENDING; + u8 *aml; ACPI_FUNCTION_TRACE_PTR(ps_next_parse_state, op); @@ -344,7 +351,14 @@ acpi_ps_next_parse_state(struct acpi_walk_state *walk_state, * Predicate of an IF was true, and we are at the matching ELSE. * Just close out this package */ + aml = parser_state->aml; + parser_state->aml = acpi_ps_get_next_package_end(parser_state); + if ((parser_state->aml > parser_state->aml_end) || + (parser_state->aml < aml)) { + status = AE_AML_PACKAGE_LIMIT; + break; + } status = AE_CTRL_PENDING; break; diff --git a/drivers/acpi/acpica/psscope.c b/drivers/acpi/acpica/psscope.c index c4e4483f0a0b..822ea96ac048 100644 --- a/drivers/acpi/acpica/psscope.c +++ b/drivers/acpi/acpica/psscope.c @@ -3,7 +3,7 @@ * * Module Name: psscope - Parser scope stack management routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index 5a285d3f2cdb..313cbfb10ba2 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -3,7 +3,7 @@ * * Module Name: pstree - Parser op tree manipulation/traversal/search * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c index ada1dc304d25..789d75dc40db 100644 --- a/drivers/acpi/acpica/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -3,7 +3,7 @@ * * Module Name: psutils - Parser miscellaneous utilities (Parser only) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c index 2f3ebcd8aebe..ac0521014c24 100644 --- a/drivers/acpi/acpica/pswalk.c +++ b/drivers/acpi/acpica/pswalk.c @@ -3,7 +3,7 @@ * * Module Name: pswalk - Parser routines to walk parsed op tree(s) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -49,8 +49,8 @@ void acpi_ps_delete_parse_tree(union acpi_parse_object *subtree_root) /* This debug option will print the entire parse tree */ - acpi_os_printf(" %*.s%s %p", (level * 4), - " ", + acpi_os_printf(" %*s%s %p", (level * 4), + "", acpi_ps_get_opcode_name(op-> common. aml_opcode), diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index d480de075a90..f8df9398e41f 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -3,7 +3,7 @@ * * Module Name: psxface - Parser external interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index 279bfa27da94..5ab41e9b9039 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -315,7 +315,7 @@ struct acpi_rsconvert_info acpi_rs_convert_csi2_serial_bus[14] = { * ******************************************************************************/ -struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = { +struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[18] = { {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus), ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)}, @@ -391,6 +391,11 @@ struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = { AML_OFFSET(i2c_serial_bus.type_specific_flags), 0}, + /* Read LVR from Type Specific Flags, bits[15:8] */ + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr), + AML_OFFSET(i2c_serial_bus.type_specific_flags) + 1, + 1}, + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed), AML_OFFSET(i2c_serial_bus.connection_speed), 1}, diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c index 5b98e09fff76..07af035aa4de 100644 --- a/drivers/acpi/acpica/tbdata.c +++ b/drivers/acpi/acpica/tbdata.c @@ -3,7 +3,7 @@ * * Module Name: tbdata - Table manager data structure functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index c6658b2f3027..4c3ef7cac065 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -3,7 +3,7 @@ * * Module Name: tbfadt - FADT table utilities * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -553,8 +553,11 @@ static void acpi_tb_convert_fadt(void) * Note: If the legacy length field is > 0xFF bits, ignore * this check. (GPE registers can be larger than the * 64-bit GAS structure can accommodate, 0xFF bits). + * Also skip if bit_width is 0, indicating the 64-bit field + * was not populated - legacy length will be used instead. */ if ((ACPI_MUL_8(length) <= ACPI_UINT8_MAX) && + (address64->bit_width != 0) && (address64->bit_width != ACPI_MUL_8(length))) { ACPI_BIOS_WARNING((AE_INFO, diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c index d71a73216380..60a772c87f42 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -3,7 +3,7 @@ * * Module Name: tbfind - find table * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index ee9b85bc238b..d36a803f9ff0 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -3,7 +3,7 @@ * * Module Name: tbinstal - ACPI table installation and removal * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c index e5631027f7f1..acfff3c96d09 100644 --- a/drivers/acpi/acpica/tbprint.c +++ b/drivers/acpi/acpica/tbprint.c @@ -3,7 +3,7 @@ * * Module Name: tbprint - Table output utilities * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index fa64851c7b62..adc6e3b0451e 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -3,7 +3,7 @@ * * Module Name: tbutils - ACPI Table utilities * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index a8f07d2641b6..de69ec75c5f1 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -3,7 +3,7 @@ * * Module Name: tbxface - ACPI table-oriented external interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 2a17c60a9a39..c0e34b06f93e 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -3,7 +3,7 @@ * * Module Name: tbxfload - Table load/unload external interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index 961577ba9486..27e2e162c351 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -3,7 +3,7 @@ * * Module Name: tbxfroot - Find the root ACPI table (RSDT) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c index c673d6c95e0a..48f55a36e52d 100644 --- a/drivers/acpi/acpica/utaddress.c +++ b/drivers/acpi/acpica/utaddress.c @@ -3,7 +3,7 @@ * * Module Name: utaddress - op_region address range check * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c index 2418a312733a..da12b6fec24f 100644 --- a/drivers/acpi/acpica/utalloc.c +++ b/drivers/acpi/acpica/utalloc.c @@ -3,7 +3,7 @@ * * Module Name: utalloc - local memory allocation routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utascii.c b/drivers/acpi/acpica/utascii.c index 259c28d3fecd..9ad2bb93efd3 100644 --- a/drivers/acpi/acpica/utascii.c +++ b/drivers/acpi/acpica/utascii.c @@ -3,7 +3,7 @@ * * Module Name: utascii - Utility ascii functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c index f6e6e98e9523..4193ceb93777 100644 --- a/drivers/acpi/acpica/utbuffer.c +++ b/drivers/acpi/acpica/utbuffer.c @@ -3,7 +3,7 @@ * * Module Name: utbuffer - Buffer dump routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utcache.c b/drivers/acpi/acpica/utcache.c index cabec193febb..6ec2a6e88d3e 100644 --- a/drivers/acpi/acpica/utcache.c +++ b/drivers/acpi/acpica/utcache.c @@ -3,7 +3,7 @@ * * Module Name: utcache - local cache allocation routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utcksum.c b/drivers/acpi/acpica/utcksum.c index e6f6030b3a3f..040e34569ac7 100644 --- a/drivers/acpi/acpica/utcksum.c +++ b/drivers/acpi/acpica/utcksum.c @@ -3,7 +3,7 @@ * * Module Name: utcksum - Support generating table checksums * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index 80458e70ac2b..e4d2f510f91e 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -3,7 +3,7 @@ * * Module Name: utcopy - Internal to external object translation utilities * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ @@ -731,7 +731,15 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, break; } - acpi_ut_add_reference(source_desc->reference.object); + /* + * Local/Arg/Debug references do not have a valid Object pointer + * that can be referenced + */ + if ((source_desc->reference.class != ACPI_REFCLASS_LOCAL) && + (source_desc->reference.class != ACPI_REFCLASS_ARG) && + (source_desc->reference.class != ACPI_REFCLASS_DEBUG)) { + acpi_ut_add_reference(source_desc->reference.object); + } break; case ACPI_TYPE_REGION: diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 9f197e293c7e..a31e07400c34 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -3,7 +3,7 @@ * * Module Name: utdebug - Debug print/trace routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index b82130d1a8bc..10482caac3ac 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -3,7 +3,7 @@ * * Module Name: utdecode - Utility decoding routines (value-to-string) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index abc6583ed369..c6cc677912cb 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -3,7 +3,7 @@ * * Module Name: uteval - Object evaluation * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 97c55a113bae..68b6656cfb1b 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -3,7 +3,7 @@ * * Module Name: utglobal - Global variables for the ACPI subsystem * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c index 8cd050e9cad5..df09a1a14fb6 100644 --- a/drivers/acpi/acpica/uthex.c +++ b/drivers/acpi/acpica/uthex.c @@ -3,7 +3,7 @@ * * Module Name: uthex -- Hex/ASCII support functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c index eb88335dea2c..e177229e6a3f 100644 --- a/drivers/acpi/acpica/utids.c +++ b/drivers/acpi/acpica/utids.c @@ -3,7 +3,7 @@ * * Module Name: utids - support for device Ids - HID, UID, CID, SUB, CLS * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index 4bef97e8223a..79d9412d9b80 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -3,7 +3,7 @@ * * Module Name: utinit - Common ACPI subsystem initialization * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utlock.c b/drivers/acpi/acpica/utlock.c index 123dbcbc60bc..cf6b931cd570 100644 --- a/drivers/acpi/acpica/utlock.c +++ b/drivers/acpi/acpica/utlock.c @@ -3,7 +3,7 @@ * * Module Name: utlock - Reader/Writer lock interfaces * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 8362204b57b5..60513dfab8e4 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -3,7 +3,7 @@ * * Module Name: utobject - ACPI object create/delete/size/cache routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c index 88d04183ad0a..64e26fb825fd 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -3,7 +3,7 @@ * * Module Name: utosi - Support for the _OSI predefined control method * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c index d9bd80e2d32a..dd1548231803 100644 --- a/drivers/acpi/acpica/utpredef.c +++ b/drivers/acpi/acpica/utpredef.c @@ -3,7 +3,7 @@ * * Module Name: utpredef - support functions for predefined names * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c index 423d10569736..52f41d7acbef 100644 --- a/drivers/acpi/acpica/utprint.c +++ b/drivers/acpi/acpica/utprint.c @@ -3,7 +3,7 @@ * * Module Name: utprint - Formatted printing routines * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c index e1cc3d348750..86ebd9fb869a 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -165,6 +165,28 @@ acpi_ut_walk_aml_resources(struct acpi_walk_state *walk_state, /* Walk the byte list, abort on any invalid descriptor type or length */ while (aml < end_aml) { + /* + * Validate that the remaining buffer space can hold enough + * bytes to safely access fields during validation. + * For large resource descriptors (bit 7 set), we need enough + * bytes to access the Type field in serial_bus resources. + * Small resource descriptors only need sizeof(struct aml_resource_end_tag). + */ + if ((acpi_size)(end_aml - aml) < + sizeof(struct aml_resource_end_tag)) { + return_ACPI_STATUS(AE_AML_BUFFER_LENGTH); + } + + /* + * For large resource descriptors, ensure enough space for + * the header plus serial_bus Type field access. + */ + if ((ACPI_GET8(aml) & ACPI_RESOURCE_NAME_LARGE) && + ((acpi_size)(end_aml - aml) < + ACPI_OFFSET(struct aml_resource_common_serialbus, + type) + 1)) { + return_ACPI_STATUS(AE_AML_BUFFER_LENGTH); + } /* Validate the Resource Type and Resource Length */ @@ -182,6 +204,14 @@ acpi_ut_walk_aml_resources(struct acpi_walk_state *walk_state, length = acpi_ut_get_descriptor_length(aml); + /* + * Validate that the descriptor length doesn't exceed the + * remaining buffer size to prevent reading beyond the end. + */ + if (length > (acpi_size)(end_aml - aml)) { + return_ACPI_STATUS(AE_AML_BUFFER_LENGTH); + } + /* Invoke the user function */ if (user_function) { diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c index a99c4c9e3d39..f013fb9c20f8 100644 --- a/drivers/acpi/acpica/uttrack.c +++ b/drivers/acpi/acpica/uttrack.c @@ -3,7 +3,7 @@ * * Module Name: uttrack - Memory allocation tracking routines (debug only) * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utuuid.c b/drivers/acpi/acpica/utuuid.c index 0682554934ca..aa8985be9048 100644 --- a/drivers/acpi/acpica/utuuid.c +++ b/drivers/acpi/acpica/utuuid.c @@ -3,7 +3,7 @@ * * Module Name: utuuid -- UUID support functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 56942b5f026b..e1d08a14d7a8 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -3,7 +3,7 @@ * * Module Name: utxface - External interfaces, miscellaneous utility functions * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c index c1702f8fba67..61f50e0736bb 100644 --- a/drivers/acpi/acpica/utxfinit.c +++ b/drivers/acpi/acpica/utxfinit.c @@ -3,7 +3,7 @@ * * Module Name: utxfinit - External interfaces for ACPICA initialization * - * Copyright (C) 2000 - 2025, Intel Corp. + * Copyright (C) 2000 - 2026, Intel Corp. * *****************************************************************************/ diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c index ffc867bac2d6..00158c8aa6d9 100644 --- a/drivers/acpi/arm64/gtdt.c +++ b/drivers/acpi/arm64/gtdt.c @@ -34,14 +34,33 @@ struct acpi_gtdt_descriptor { void *platform_timer; }; +struct gtdt_v3 { + struct acpi_table_gtdt gtdt_v2; + struct acpi_gtdt_el2 el2_vtimer; +}; + static struct acpi_gtdt_descriptor acpi_gtdt_desc __initdata; +static __init struct acpi_gtdt_el2 *gtdt_to_el2_vtimer(struct acpi_table_gtdt *gtdt) +{ + if (gtdt->header.revision < 3) + return NULL; + + return &container_of(gtdt, struct gtdt_v3, gtdt_v2)->el2_vtimer; +} + static __init bool platform_timer_valid(void *platform_timer) { struct acpi_gtdt_header *gh = platform_timer; + void *platform_timer_begin; + + if (acpi_gtdt_desc.gtdt->header.revision >= 3) + platform_timer_begin = container_of(acpi_gtdt_desc.gtdt, struct gtdt_v3, gtdt_v2) + 1; + else + platform_timer_begin = acpi_gtdt_desc.gtdt + 1; - return (platform_timer >= (void *)(acpi_gtdt_desc.gtdt + 1) && - platform_timer < acpi_gtdt_desc.gtdt_end && + return (platform_timer >= platform_timer_begin && + platform_timer + sizeof(*gh) <= acpi_gtdt_desc.gtdt_end && gh->length != 0 && platform_timer + gh->length <= acpi_gtdt_desc.gtdt_end); } @@ -101,6 +120,7 @@ static int __init map_gt_gsi(u32 interrupt, u32 flags) int __init acpi_gtdt_map_ppi(int type) { struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + struct acpi_gtdt_el2 *el2_vtimer = gtdt_to_el2_vtimer(gtdt); switch (type) { case ARCH_TIMER_PHYS_NONSECURE_PPI: @@ -113,6 +133,12 @@ int __init acpi_gtdt_map_ppi(int type) case ARCH_TIMER_HYP_PPI: return map_gt_gsi(gtdt->non_secure_el2_interrupt, gtdt->non_secure_el2_flags); + case ARCH_TIMER_HYP_VIRT_PPI: + if (el2_vtimer && el2_vtimer->virtual_el2_timer_gsiv) + return map_gt_gsi(el2_vtimer->virtual_el2_timer_gsiv, + el2_vtimer->virtual_el2_timer_flags); + + return 0; default: pr_err("Failed to map timer interrupt: invalid type.\n"); } @@ -130,6 +156,7 @@ int __init acpi_gtdt_map_ppi(int type) bool __init acpi_gtdt_c3stop(int type) { struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + struct acpi_gtdt_el2 *el2_vtimer = gtdt_to_el2_vtimer(gtdt); switch (type) { case ARCH_TIMER_PHYS_NONSECURE_PPI: @@ -141,6 +168,10 @@ bool __init acpi_gtdt_c3stop(int type) case ARCH_TIMER_HYP_PPI: return !(gtdt->non_secure_el2_flags & ACPI_GTDT_ALWAYS_ON); + case ARCH_TIMER_HYP_VIRT_PPI: + return el2_vtimer && el2_vtimer->virtual_el2_timer_gsiv && + !(el2_vtimer->virtual_el2_timer_flags & ACPI_GTDT_ALWAYS_ON); + default: pr_err("Failed to get c3stop info: invalid type.\n"); } @@ -166,6 +197,13 @@ int __init acpi_gtdt_init(struct acpi_table_header *table, u32 cnt = 0; gtdt = container_of(table, struct acpi_table_gtdt, header); + + if ((gtdt->header.revision >= 3 && gtdt->header.length < sizeof(struct gtdt_v3)) || + (gtdt->header.revision == 2 && gtdt->header.length < sizeof(*gtdt))) { + pr_err(FW_BUG "GTDT with invalid size %d\n", gtdt->header.length); + return -EINVAL; + } + acpi_gtdt_desc.gtdt = gtdt; acpi_gtdt_desc.gtdt_end = (void *)table + table->length; acpi_gtdt_desc.platform_timer = NULL; diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index b82dd67d98c9..f5e0eb299610 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1182,6 +1182,26 @@ static const struct dmi_system_id bat_dmi_table[] __initconst = { {}, }; +static void acpi_battery_wakeup_cleanup(void *data) +{ + device_init_wakeup(data, false); +} + +static int devm_acpi_battery_init_wakeup(struct device *dev) +{ + device_init_wakeup(dev, true); + return devm_add_action_or_reset(dev, acpi_battery_wakeup_cleanup, dev); +} + +static void sysfs_battery_cleanup(void *data) +{ + struct acpi_battery *battery = data; + + guard(mutex)(&battery->update_lock); + + sysfs_remove_battery(battery); +} + /* * Some machines'(E,G Lenovo Z480) ECs are not stable * during boot up and this causes battery driver fails to be @@ -1190,10 +1210,15 @@ static const struct dmi_system_id bat_dmi_table[] __initconst = { * may work. So add retry code here and 20ms sleep between * every retries. */ -static int acpi_battery_update_retry(struct acpi_battery *battery) +static int devm_acpi_battery_update_retry(struct device *dev, + struct acpi_battery *battery) { int retry, ret; + ret = devm_add_action(dev, sysfs_battery_cleanup, battery); + if (ret) + return ret; + guard(mutex)(&battery->update_lock); for (retry = 5; retry; retry--) { @@ -1206,27 +1231,21 @@ static int acpi_battery_update_retry(struct acpi_battery *battery) return ret; } -static void sysfs_battery_cleanup(struct acpi_battery *battery) -{ - guard(mutex)(&battery->update_lock); - - sysfs_remove_battery(battery); -} - static int acpi_battery_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct acpi_battery *battery; struct acpi_device *device; int result; - device = ACPI_COMPANION(&pdev->dev); + device = ACPI_COMPANION(dev); if (!device) return -ENODEV; if (device->dep_unmet) return -EPROBE_DEFER; - battery = devm_kzalloc(&pdev->dev, sizeof(*battery), GFP_KERNEL); + battery = devm_kzalloc(dev, sizeof(*battery), GFP_KERNEL); if (!battery) return -ENOMEM; @@ -1235,54 +1254,38 @@ static int acpi_battery_probe(struct platform_device *pdev) battery->phys_dev = &pdev->dev; battery->device = device; - result = devm_mutex_init(&pdev->dev, &battery->update_lock); + result = devm_mutex_init(dev, &battery->update_lock); if (result) return result; if (acpi_has_method(battery->device->handle, "_BIX")) set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); - result = acpi_battery_update_retry(battery); + result = devm_acpi_battery_update_retry(dev, battery); if (result) - goto fail; + return result; pr_info("Slot [%s] (battery %s)\n", acpi_device_bid(device), device->status.battery_present ? "present" : "absent"); - battery->pm_nb.notifier_call = battery_notify; - result = register_pm_notifier(&battery->pm_nb); + result = devm_acpi_battery_init_wakeup(dev); if (result) - goto fail; - - device_init_wakeup(&pdev->dev, true); + return result; - result = acpi_dev_install_notify_handler(device, ACPI_ALL_NOTIFY, - acpi_battery_notify, battery); + result = devm_acpi_install_notify_handler(dev, ACPI_ALL_NOTIFY, + acpi_battery_notify, battery); if (result) - goto fail_pm; - - return 0; - -fail_pm: - device_init_wakeup(&pdev->dev, false); - unregister_pm_notifier(&battery->pm_nb); -fail: - sysfs_battery_cleanup(battery); + return result; - return result; + battery->pm_nb.notifier_call = battery_notify; + return register_pm_notifier(&battery->pm_nb); } static void acpi_battery_remove(struct platform_device *pdev) { struct acpi_battery *battery = platform_get_drvdata(pdev); - acpi_dev_remove_notify_handler(battery->device, ACPI_ALL_NOTIFY, - acpi_battery_notify); - - device_init_wakeup(&pdev->dev, false); unregister_pm_notifier(&battery->pm_nb); - - sysfs_battery_cleanup(battery); } /* this is needed to learn about changes made in suspended state */ diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2ec095e2009e..3b7db94b81f7 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -679,6 +679,73 @@ void acpi_dev_remove_notify_handler(struct acpi_device *adev, } EXPORT_SYMBOL_GPL(acpi_dev_remove_notify_handler); +struct acpi_notify_handler_devres { + struct acpi_device *adev; + acpi_notify_handler handler; + u32 handler_type; +}; + +static void devm_acpi_notify_handler_release(struct device *dev, void *res) +{ + struct acpi_notify_handler_devres *dr = res; + + acpi_dev_remove_notify_handler(dr->adev, dr->handler_type, dr->handler); +} + +/** + * devm_acpi_install_notify_handler - Install an ACPI notify handler for a + * managed device + * @dev: Device to install a notify handler for + * @handler_type: Type of the notify handler + * @handler: Handler function to install + * @context: Data passed back to the handler function + * + * This function performs the same function as acpi_dev_install_notify_handler() + * called for the ACPI companion of @dev with the same @handler_type, @handler, + * and @context arguments, but the ACPI notify handler installed by it will be + * automatically removed on driver detach. + * + * Callers should ensure that all resources used by @handler have been allocated + * prior to invoking this function, in which case those resources should be + * devres-managed so that they won't be released before the notify handler + * removal. Otherwise, special synchronization between @handler and the + * management of those resources is required. + * + * When the request fails, an error message is printed. Don't add extra error + * messages at the call sites. + * + * Return: 0 on success or a negative error number. + */ +int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type, + acpi_notify_handler handler, void *context) +{ + struct acpi_notify_handler_devres *dr; + struct acpi_device *adev; + int ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return dev_err_probe(dev, -ENODEV, "No ACPI companion\n"); + + dr = devres_alloc(devm_acpi_notify_handler_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = acpi_dev_install_notify_handler(adev, handler_type, handler, context); + if (ret) { + devres_free(dr); + return dev_err_probe(dev, ret, "Failed to install an ACPI notify handler\n"); + } + + dr->adev = adev; + dr->handler = handler; + dr->handler_type = handler_type; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_acpi_install_notify_handler); + /* Handle events targeting \_SB device (at present only graceful shutdown) */ #define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81 @@ -831,9 +898,9 @@ const struct acpi_device *acpi_companion_match(const struct device *dev) * identifiers and a _DSD object with the "compatible" property, use that * property to match against the given list of identifiers. */ -static bool acpi_of_match_device(const struct acpi_device *adev, - const struct of_device_id *of_match_table, - const struct of_device_id **of_id) +bool acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id) { const union acpi_object *of_compatible, *obj; int i, nval; diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index b47301ee4c8a..3836ee75dd66 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -28,14 +28,15 @@ #define ACPI_BUTTON_NOTIFY_WAKE 0x02 #define ACPI_BUTTON_NOTIFY_STATUS 0x80 -#define ACPI_BUTTON_SUBCLASS_POWER "power" +#define ACPI_BUTTON_CLASS_POWER "button/power" #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" #define ACPI_BUTTON_TYPE_POWER 0x01 -#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" +#define ACPI_BUTTON_CLASS_SLEEP "button/sleep" #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" #define ACPI_BUTTON_TYPE_SLEEP 0x03 +#define ACPI_BUTTON_CLASS_LID "button/lid" #define ACPI_BUTTON_SUBCLASS_LID "lid" #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" #define ACPI_BUTTON_TYPE_LID 0x05 @@ -59,11 +60,11 @@ MODULE_DESCRIPTION("ACPI Button Driver"); MODULE_LICENSE("GPL"); static const struct acpi_device_id button_device_ids[] = { - {ACPI_BUTTON_HID_LID, 0}, - {ACPI_BUTTON_HID_SLEEP, 0}, - {ACPI_BUTTON_HID_SLEEPF, 0}, - {ACPI_BUTTON_HID_POWER, 0}, - {ACPI_BUTTON_HID_POWERF, 0}, + {ACPI_BUTTON_HID_LID, ACPI_BUTTON_TYPE_LID}, + {ACPI_BUTTON_HID_SLEEP, ACPI_BUTTON_TYPE_SLEEP}, + {ACPI_BUTTON_HID_SLEEPF, ACPI_BUTTON_TYPE_SLEEP}, + {ACPI_BUTTON_HID_POWER, ACPI_BUTTON_TYPE_POWER}, + {ACPI_BUTTON_HID_POWERF, ACPI_BUTTON_TYPE_POWER}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, button_device_ids); @@ -173,15 +174,16 @@ struct acpi_button { struct device *dev; /* physical button device */ unsigned int type; struct input_dev *input; + const char *class; /* for netlink messages */ char phys[32]; /* for input device */ unsigned long pushed; - int last_state; + bool last_state; ktime_t last_time; bool suspended; bool lid_state_initialized; + bool gpe_enabled; }; -static struct acpi_device *lid_device; static long lid_init_state = -1; static unsigned long lid_report_interval __read_mostly = 500; @@ -192,19 +194,19 @@ MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events"); static struct proc_dir_entry *acpi_button_dir; static struct proc_dir_entry *acpi_lid_dir; -static int acpi_lid_evaluate_state(struct acpi_device *device) +static int acpi_lid_evaluate_state(acpi_handle lid_handle) { unsigned long long lid_state; acpi_status status; - status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state); + status = acpi_evaluate_integer(lid_handle, "_LID", NULL, &lid_state); if (ACPI_FAILURE(status)) return -ENODEV; - return lid_state ? 1 : 0; + return !!lid_state; } -static int acpi_lid_notify_state(struct acpi_button *button, int state) +static void acpi_lid_notify_state(struct acpi_button *button, bool state) { struct acpi_device *device = button->adev; ktime_t next_report; @@ -217,18 +219,14 @@ static int acpi_lid_notify_state(struct acpi_button *button, int state) * So "last_time" is only updated after a timeout or an actual * switch. */ - if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE || - button->last_state != !!state) - do_update = true; - else - do_update = false; - + do_update = lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE || + button->last_state != state; next_report = ktime_add(button->last_time, ms_to_ktime(lid_report_interval)); - if (button->last_state == !!state && + if (button->last_state == state && ktime_after(ktime_get(), next_report)) { - /* Complain the buggy firmware */ - pr_warn_once("The lid device is not compliant to SW_LID.\n"); + /* Complain about the buggy firmware. */ + pr_warn_once(FW_BUG "Unexpected lid state reported by firmware\n"); /* * Send the unreliable complement switch event: @@ -279,11 +277,9 @@ static int acpi_lid_notify_state(struct acpi_button *button, int state) state ? "open" : "closed"); input_report_switch(button->input, SW_LID, !state); input_sync(button->input); - button->last_state = !!state; + button->last_state = state; button->last_time = ktime_get(); } - - return 0; } static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, @@ -292,21 +288,16 @@ static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, struct acpi_button *button = seq->private; int state; - state = acpi_lid_evaluate_state(button->adev); + state = acpi_lid_evaluate_state(button->adev->handle); seq_printf(seq, "state: %s\n", state < 0 ? "unsupported" : (state ? "open" : "closed")); return 0; } -static int acpi_button_add_fs(struct acpi_button *button) +static int acpi_lid_add_fs(struct acpi_button *button) { struct acpi_device *device = button->adev; struct proc_dir_entry *entry = NULL; - int ret = 0; - - /* procfs I/F for ACPI lid device only */ - if (button->type != ACPI_BUTTON_TYPE_LID) - return 0; if (acpi_button_dir || acpi_lid_dir) { pr_info("More than one Lid device found!\n"); @@ -320,33 +311,25 @@ static int acpi_button_add_fs(struct acpi_button *button) /* create /proc/acpi/button/lid */ acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); - if (!acpi_lid_dir) { - ret = -ENODEV; + if (!acpi_lid_dir) goto remove_button_dir; - } /* create /proc/acpi/button/lid/LID/ */ acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir); - if (!acpi_device_dir(device)) { - ret = -ENODEV; + if (!acpi_device_dir(device)) goto remove_lid_dir; - } /* create /proc/acpi/button/lid/LID/state */ entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO, acpi_device_dir(device), acpi_button_state_seq_show, button); - if (!entry) { - ret = -ENODEV; + if (!entry) goto remove_dev_dir; - } -done: - return ret; + return 0; remove_dev_dir: - remove_proc_entry(acpi_device_bid(device), - acpi_lid_dir); + remove_proc_entry(acpi_device_bid(device), acpi_lid_dir); acpi_device_dir(device) = NULL; remove_lid_dir: remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); @@ -354,16 +337,14 @@ remove_lid_dir: remove_button_dir: remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); acpi_button_dir = NULL; - goto done; + return -ENODEV; } -static int acpi_button_remove_fs(struct acpi_button *button) +static void acpi_lid_remove_fs(void *data) { + struct acpi_button *button = data; struct acpi_device *device = button->adev; - if (button->type != ACPI_BUTTON_TYPE_LID) - return 0; - remove_proc_entry(ACPI_BUTTON_FILE_STATE, acpi_device_dir(device)); remove_proc_entry(acpi_device_bid(device), @@ -373,44 +354,71 @@ static int acpi_button_remove_fs(struct acpi_button *button) acpi_lid_dir = NULL; remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); acpi_button_dir = NULL; +} - return 0; +static int devm_acpi_lid_add_fs(struct device *dev, struct acpi_button *button) +{ + int ret; + + ret = acpi_lid_add_fs(button); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, acpi_lid_remove_fs, button); +} + +static acpi_handle saved_lid_handle; +static DEFINE_MUTEX(acpi_lid_lock); + +static void acpi_lid_save(struct acpi_device *adev) +{ + guard(mutex)(&acpi_lid_lock); + + saved_lid_handle = adev->handle; +} + +static void acpi_lid_forget(struct acpi_device *adev) +{ + guard(mutex)(&acpi_lid_lock); + + if (saved_lid_handle == adev->handle) + saved_lid_handle = NULL; } /* Driver Interface */ int acpi_lid_open(void) { - if (!lid_device) + guard(mutex)(&acpi_lid_lock); + + if (!saved_lid_handle) return -ENODEV; - return acpi_lid_evaluate_state(lid_device); + return acpi_lid_evaluate_state(saved_lid_handle); } EXPORT_SYMBOL(acpi_lid_open); -static int acpi_lid_update_state(struct acpi_button *button, - bool signal_wakeup) +static void acpi_lid_update_state(struct acpi_button *button, bool signal_wakeup) { - struct acpi_device *device = button->adev; int state; - state = acpi_lid_evaluate_state(device); + state = acpi_lid_evaluate_state(button->adev->handle); if (state < 0) - return state; + return; if (state && signal_wakeup) acpi_pm_wakeup_event(button->dev); - return acpi_lid_notify_state(button, state); + acpi_lid_notify_state(button, state); } static void acpi_lid_initialize_state(struct acpi_button *button) { switch (lid_init_state) { case ACPI_BUTTON_LID_INIT_OPEN: - (void)acpi_lid_notify_state(button, 1); + acpi_lid_notify_state(button, true); break; case ACPI_BUTTON_LID_INIT_METHOD: - (void)acpi_lid_update_state(button, false); + acpi_lid_update_state(button, false); break; case ACPI_BUTTON_LID_INIT_IGNORE: default: @@ -468,8 +476,7 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) input_report_key(input, keycode, 0); input_sync(input); - acpi_bus_generate_netlink_event(acpi_device_class(device), - dev_name(&device->dev), + acpi_bus_generate_netlink_event(button->class, dev_name(&device->dev), event, ++button->pushed); } @@ -496,12 +503,11 @@ static int acpi_button_suspend(struct device *dev) static int acpi_button_resume(struct device *dev) { struct acpi_button *button = dev_get_drvdata(dev); - struct acpi_device *device = ACPI_COMPANION(dev); struct input_dev *input; button->suspended = false; if (button->type == ACPI_BUTTON_TYPE_LID) { - button->last_state = !!acpi_lid_evaluate_state(device); + button->last_state = !!acpi_lid_evaluate_state(ACPI_HANDLE(dev)); button->last_time = ktime_get(); acpi_lid_initialize_state(button); } @@ -520,190 +526,231 @@ static int acpi_button_resume(struct device *dev) static int acpi_lid_input_open(struct input_dev *input) { struct acpi_button *button = input_get_drvdata(input); - struct acpi_device *device = button->adev; - button->last_state = !!acpi_lid_evaluate_state(device); + button->last_state = !!acpi_lid_evaluate_state(button->adev->handle); button->last_time = ktime_get(); acpi_lid_initialize_state(button); return 0; } +static acpi_notify_handler acpi_button_notify_handler(struct acpi_button *button) +{ + if (button->type == ACPI_BUTTON_TYPE_LID) + return acpi_lid_notify; + + return acpi_button_notify; +} + +static void acpi_button_wakeup_cleanup(void *data) +{ + device_init_wakeup(data, false); +} + +static int devm_acpi_button_init_wakeup(struct device *dev) +{ + device_init_wakeup(dev, true); + return devm_add_action_or_reset(dev, acpi_button_wakeup_cleanup, dev); +} + +static void acpi_button_remove_event_handler(void *data) +{ + struct acpi_button *button = data; + struct acpi_device *adev = button->adev; + + switch (adev->device_type) { + case ACPI_BUS_TYPE_POWER_BUTTON: + acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_button_event); + break; + + case ACPI_BUS_TYPE_SLEEP_BUTTON: + acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_button_event); + break; + + default: + if (button->gpe_enabled) { + dev_dbg(button->dev, "Disabling ACPI GPE%02llx\n", + adev->wakeup.gpe_number); + acpi_disable_gpe(adev->wakeup.gpe_device, + adev->wakeup.gpe_number); + } + acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, + acpi_button_notify_handler(button)); + break; + } + acpi_os_wait_events_complete(); +} + +static int acpi_button_add_fixed_event_handler(u32 event, + struct acpi_button *button) +{ + acpi_status status; + + status = acpi_install_fixed_event_handler(event, acpi_button_event, button); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return 0; +} + +static int acpi_button_add_event_handler(struct acpi_button *button) +{ + struct acpi_device *adev = button->adev; + acpi_status status; + + if (adev->device_type == ACPI_BUS_TYPE_POWER_BUTTON) + return acpi_button_add_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + button); + + if (adev->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) + return acpi_button_add_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + button); + + status = acpi_install_notify_handler(adev->handle, ACPI_ALL_NOTIFY, + acpi_button_notify_handler(button), + button); + if (ACPI_FAILURE(status)) + return -ENODEV; + + if (!adev->wakeup.flags.valid) + return 0; + + /* + * If the wakeup GPE has a handler method, enable it in case it is also + * used for signaling runtime events. + */ + status = acpi_enable_gpe_cond(adev->wakeup.gpe_device, + adev->wakeup.gpe_number, + ACPI_GPE_DISPATCH_METHOD); + button->gpe_enabled = ACPI_SUCCESS(status); + if (button->gpe_enabled) + dev_dbg(button->dev, "Enabled ACPI GPE%02llx\n", + adev->wakeup.gpe_number); + + return 0; +} + +static int devm_acpi_button_add_event_handler(struct device *dev, + struct acpi_button *button) +{ + int ret; + + ret = acpi_button_add_event_handler(button); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, acpi_button_remove_event_handler, + button); +} + static int acpi_button_probe(struct platform_device *pdev) { - acpi_notify_handler handler; - struct acpi_device *device; + struct device *dev = &pdev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); + const struct acpi_device_id *id; struct acpi_button *button; struct input_dev *input; - acpi_status status; - char *name, *class; - const char *hid; + u8 button_type; int error = 0; - device = ACPI_COMPANION(&pdev->dev); - if (!device) - return -ENODEV; + id = acpi_match_acpi_device(button_device_ids, device); + if (!id || strcmp(acpi_device_hid(device), id->id)) + return dev_err_probe(dev, -ENODEV, "Unsupported device\n"); - hid = acpi_device_hid(device); - if (!strcmp(hid, ACPI_BUTTON_HID_LID) && - lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED) + button_type = id->driver_data; + if (button_type == ACPI_BUTTON_TYPE_LID && + lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED) return -ENODEV; - button = kzalloc_obj(struct acpi_button); + button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL); if (!button) return -ENOMEM; platform_set_drvdata(pdev, button); - button->dev = &pdev->dev; + button->dev = dev; button->adev = device; - button->input = input = input_allocate_device(); - if (!input) { - error = -ENOMEM; - goto err_free_button; - } + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; - class = acpi_device_class(device); - - if (!strcmp(hid, ACPI_BUTTON_HID_POWER) || - !strcmp(hid, ACPI_BUTTON_HID_POWERF)) { - button->type = ACPI_BUTTON_TYPE_POWER; - handler = acpi_button_notify; - name = ACPI_BUTTON_DEVICE_NAME_POWER; - sprintf(class, "%s/%s", - ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); - } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) || - !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) { - button->type = ACPI_BUTTON_TYPE_SLEEP; - handler = acpi_button_notify; - name = ACPI_BUTTON_DEVICE_NAME_SLEEP; - sprintf(class, "%s/%s", - ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); - } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) { - button->type = ACPI_BUTTON_TYPE_LID; - handler = acpi_lid_notify; - name = ACPI_BUTTON_DEVICE_NAME_LID; - sprintf(class, "%s/%s", - ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); - input->open = acpi_lid_input_open; - } else { - pr_info("Unsupported hid [%s]\n", hid); - error = -ENODEV; - } + button->input = input; + button->type = button_type; - if (!error) - error = acpi_button_add_fs(button); + switch (button_type) { + case ACPI_BUTTON_TYPE_LID: + button->class = ACPI_BUTTON_CLASS_LID; - if (error) { - input_free_device(input); - goto err_free_button; - } + input->name = ACPI_BUTTON_DEVICE_NAME_LID; + input_set_capability(input, EV_SW, SW_LID); + input->open = acpi_lid_input_open; - snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); + error = devm_acpi_lid_add_fs(dev, button); + if (error) + return error; - input->name = name; - input->phys = button->phys; - input->id.bustype = BUS_HOST; - input->id.product = button->type; - input->dev.parent = &pdev->dev; + break; - switch (button->type) { case ACPI_BUTTON_TYPE_POWER: + button->class = ACPI_BUTTON_CLASS_POWER; + + input->name = ACPI_BUTTON_DEVICE_NAME_POWER; input_set_capability(input, EV_KEY, KEY_POWER); input_set_capability(input, EV_KEY, KEY_WAKEUP); break; case ACPI_BUTTON_TYPE_SLEEP: + button->class = ACPI_BUTTON_CLASS_SLEEP; + + input->name = ACPI_BUTTON_DEVICE_NAME_SLEEP; input_set_capability(input, EV_KEY, KEY_SLEEP); break; - case ACPI_BUTTON_TYPE_LID: - input_set_capability(input, EV_SW, SW_LID); - break; + default: + return dev_err_probe(dev, -ENODEV, "Unrecognized button type\n"); } + snprintf(button->phys, sizeof(button->phys), "%s/button/input0", + acpi_device_hid(device)); + + input->phys = button->phys; + input->id.bustype = BUS_HOST; + input->id.product = button_type; + input_set_drvdata(input, button); error = input_register_device(input); - if (error) { - input_free_device(input); - goto err_remove_fs; - } + if (error) + return error; - device_init_wakeup(button->dev, true); + error = devm_acpi_button_init_wakeup(dev); + if (error) + return error; - switch (device->device_type) { - case ACPI_BUS_TYPE_POWER_BUTTON: - status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, - acpi_button_event, - button); - break; - case ACPI_BUS_TYPE_SLEEP_BUTTON: - status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, - acpi_button_event, - button); - break; - default: - status = acpi_install_notify_handler(device->handle, - ACPI_ALL_NOTIFY, handler, - button); - break; - } - if (ACPI_FAILURE(status)) { - error = -ENODEV; - goto err_input_unregister; - } + error = devm_acpi_button_add_event_handler(dev, button); + if (error) + return error; - if (button->type == ACPI_BUTTON_TYPE_LID) { + if (button_type == ACPI_BUTTON_TYPE_LID) { /* * This assumes there's only one lid device, or if there are * more we only care about the last one... */ - lid_device = device; + acpi_lid_save(device); } - pr_info("%s [%s]\n", name, acpi_device_bid(device)); - return 0; + pr_info("%s [%s]\n", input->name, acpi_device_bid(device)); -err_input_unregister: - device_init_wakeup(button->dev, false); - input_unregister_device(input); -err_remove_fs: - acpi_button_remove_fs(button); -err_free_button: - kfree(button); - return error; + return 0; } static void acpi_button_remove(struct platform_device *pdev) { struct acpi_button *button = platform_get_drvdata(pdev); - struct acpi_device *adev = button->adev; - - switch (adev->device_type) { - case ACPI_BUS_TYPE_POWER_BUTTON: - acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, - acpi_button_event); - break; - case ACPI_BUS_TYPE_SLEEP_BUTTON: - acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, - acpi_button_event); - break; - default: - acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - button->type == ACPI_BUTTON_TYPE_LID ? - acpi_lid_notify : - acpi_button_notify); - break; - } - acpi_os_wait_events_complete(); - - device_init_wakeup(button->dev, false); - - acpi_button_remove_fs(button); - input_unregister_device(button->input); - kfree(button); - memset(acpi_device_class(adev), 0, sizeof(acpi_device_class)); + if (button->type == ACPI_BUTTON_TYPE_LID) + acpi_lid_forget(button->adev); } static int param_set_lid_init_state(const char *val, diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index f370be8715ae..9f572f481241 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -134,7 +134,7 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); * cpc_regs[] with the corresponding index. 0 means mandatory and 1 * means optional. */ -#define REG_OPTIONAL (0x1FC7D0) +#define REG_OPTIONAL (0x7FC7D0) /* * Use the index of the register in per-cpu cpc_regs[] to check if @@ -185,8 +185,13 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time); -/* Check for valid access_width, otherwise, fallback to using bit_width */ -#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width) +/* + * PCC reuses the access_width field as the subspace id, so only decode access + * size for non-PCC registers. Otherwise, use the bit_width. + */ +#define GET_BIT_WIDTH(reg) (((reg)->access_width && \ + (reg)->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) ? \ + (8 << ((reg)->access_width - 1)) : (reg)->bit_width) /* Shift and apply the mask for CPC reads/writes */ #define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \ @@ -751,18 +756,19 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) /* * Disregard _CPC if the number of entries in the return package is not * as expected, but support future revisions being proper supersets of - * the v3 and only causing more entries to be returned by _CPC. + * the v4 and only causing more entries to be returned by _CPC. */ if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) || (cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) || - (cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) { + (cpc_rev == CPPC_V4_REV && num_ent != CPPC_V4_NUM_ENT) || + (cpc_rev > CPPC_V4_REV && num_ent <= CPPC_V4_NUM_ENT)) { pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n", num_ent, pr->id); goto out_free; } - if (cpc_rev > CPPC_V3_REV) { - num_ent = CPPC_V3_NUM_ENT; - cpc_rev = CPPC_V3_REV; + if (cpc_rev > CPPC_V4_REV) { + num_ent = CPPC_V4_NUM_ENT; + cpc_rev = CPPC_V4_REV; } cpc_ptr->num_entries = num_ent; @@ -845,6 +851,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); + } else if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) { + /* + * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a + * Package of Resource Priority Register Descriptor sub-packages. + * Parsing the full structure is not yet supported. + * Mark the register as unsupported for now. + */ + pr_debug("CPU:%d Resource Priority not supported\n", pr->id); + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; + cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0; } else { pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n", i, pr->id); @@ -1045,7 +1061,6 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val) * by the bit width field; the access size is used to indicate * the PCC subspace id. */ - size = reg->bit_width; vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); } else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) @@ -1118,7 +1133,6 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) * by the bit width field; the access size is used to indicate * the PCC subspace id. */ - size = reg->bit_width; vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); } else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index 060e8d670f5d..48562f53d3ab 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -22,7 +22,7 @@ static const struct acpi_device_id acpi_hed_ids[] = { }; MODULE_DEVICE_TABLE(acpi, acpi_hed_ids); -static acpi_handle hed_handle; +static bool hed_present; static BLOCKING_NOTIFIER_HEAD(acpi_hed_notify_list); @@ -50,33 +50,24 @@ static void acpi_hed_notify(acpi_handle handle, u32 event, void *data) static int acpi_hed_probe(struct platform_device *pdev) { - struct acpi_device *device; int err; - device = ACPI_COMPANION(&pdev->dev); - if (!device) - return -ENODEV; - /* Only one hardware error device */ - if (hed_handle) + if (hed_present) return -EINVAL; - hed_handle = device->handle; - err = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_hed_notify, device); + err = devm_acpi_install_notify_handler(&pdev->dev, ACPI_DEVICE_NOTIFY, + acpi_hed_notify, NULL); if (err) - hed_handle = NULL; + return err; - return err; + hed_present = true; + return 0; } static void acpi_hed_remove(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); - - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_hed_notify); - hed_handle = NULL; + hed_present = false; } static struct platform_driver acpi_hed_driver = { diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 9304ac996d41..cb771d9cadb2 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -56,6 +56,8 @@ MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods"); LIST_HEAD(acpi_descs); DEFINE_MUTEX(acpi_desc_lock); +DEFINE_MUTEX(acpi_notify_lock); + static struct workqueue_struct *nfit_wq; struct nfit_table_prev { @@ -1680,7 +1682,6 @@ static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc, void __acpi_nvdimm_notify(struct device *dev, u32 event) { struct nfit_mem *nfit_mem; - struct acpi_nfit_desc *acpi_desc; dev_dbg(dev->parent, "%s: event: %d\n", dev_name(dev), event); @@ -1691,12 +1692,11 @@ void __acpi_nvdimm_notify(struct device *dev, u32 event) return; } - acpi_desc = dev_get_drvdata(dev->parent); - if (!acpi_desc) + if (!dev_get_drvdata(dev->parent)) return; /* - * If we successfully retrieved acpi_desc, then we know nfit_mem data + * If the parent's driver data pointer is not NULL, then nfit_mem data * is still valid. */ nfit_mem = dev_get_drvdata(dev); @@ -1710,9 +1710,15 @@ static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data) struct acpi_device *adev = data; struct device *dev = &adev->dev; - device_lock(dev->parent); + /* + * Locking is needed here for synchronization with driver probe and + * removal and the parent's driver data pointer is NULL when teardown + * is in progress (while the parent here is expected to be the ACPI + * companion of the platform device used for driver binding). + */ + guard(mutex)(&acpi_notify_lock); + __acpi_nvdimm_notify(dev, event); - device_unlock(dev->parent); } static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method) @@ -3069,6 +3075,8 @@ static void acpi_nfit_unregister(void *data) struct acpi_nfit_desc *acpi_desc = data; nvdimm_bus_unregister(acpi_desc->nvdimm_bus); + /* The nvdimm_bus object may have been freed, so clear the pointer. */ + acpi_desc->nvdimm_bus = NULL; } int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz) @@ -3156,11 +3164,10 @@ EXPORT_SYMBOL_GPL(acpi_nfit_init); static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc) { struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); - struct device *dev = acpi_desc->dev; - /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */ - device_lock(dev); - device_unlock(dev); + /* Bounce the notify lock to flush acpi_nfit_probe / acpi_nfit_notify */ + mutex_lock(&acpi_notify_lock); + mutex_unlock(&acpi_notify_lock); /* Bounce the init_mutex to complete initial registration */ mutex_lock(&acpi_desc->init_mutex); @@ -3292,24 +3299,26 @@ static void acpi_nfit_put_table(void *table) static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data) { struct device *dev = data; + struct acpi_device *adev = ACPI_COMPANION(dev); - device_lock(dev); - __acpi_nfit_notify(dev, handle, event); - device_unlock(dev); -} - -static void acpi_nfit_remove_notify_handler(void *data) -{ - struct acpi_device *adev = data; + /* + * Locking is needed here for synchronization with driver probe and + * removal and the ACPI companion's driver data pointer is NULL when + * teardown is in progress. + */ + guard(mutex)(&acpi_notify_lock); - acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acpi_nfit_notify); + if (dev_get_drvdata(&adev->dev)) + __acpi_nfit_notify(dev, handle, event); } void acpi_nfit_shutdown(void *data) { struct acpi_nfit_desc *acpi_desc = data; - struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus); + struct device *bus_dev; + + if (!acpi_desc || !acpi_desc->nvdimm_bus) + return; /* * Destruct under acpi_desc_lock so that nfit_handle_mce does not @@ -3324,6 +3333,7 @@ void acpi_nfit_shutdown(void *data) mutex_unlock(&acpi_desc->init_mutex); cancel_delayed_work_sync(&acpi_desc->dwork); + bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus); /* * Bounce the nvdimm bus lock to make sure any in-flight * acpi_nfit_ars_rescan() submissions have had a chance to @@ -3341,23 +3351,20 @@ static int acpi_nfit_probe(struct platform_device *pdev) struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_nfit_desc *acpi_desc; struct device *dev = &pdev->dev; + struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_table_header *tbl; - struct acpi_device *adev; acpi_status status = AE_OK; acpi_size sz; int rc = 0; - adev = ACPI_COMPANION(&pdev->dev); - if (!adev) - return -ENODEV; - - rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acpi_nfit_notify, dev); - if (rc) - return rc; + /* + * Prevent acpi_nfit_notify() from progressing until the probe is + * complete in case there is a concurrent event to process. + */ + guard(mutex)(&acpi_notify_lock); - rc = devm_add_action_or_reset(dev, acpi_nfit_remove_notify_handler, - adev); + rc = devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY, + acpi_nfit_notify, dev); if (rc) return rc; @@ -3371,6 +3378,11 @@ static int acpi_nfit_probe(struct platform_device *pdev) * data in the format of a series of NFIT Structures. */ dev_dbg(dev, "failed to find NFIT at startup\n"); + /* + * Let acpi_nfit_update_notify() run in case it will need to + * allocate the acpi_desc object. + */ + dev_set_drvdata(&adev->dev, dev); return 0; } @@ -3405,10 +3417,28 @@ static int acpi_nfit_probe(struct platform_device *pdev) + sizeof(struct acpi_table_nfit), sz - sizeof(struct acpi_table_nfit)); - if (rc) + if (rc) { + acpi_nfit_shutdown(acpi_desc); return rc; + } - return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc); + /* + * Let notify handlers operate (the actual value of the ACPI companion's + * driver data pointer does not matter here so long as it is not NULL). + */ + dev_set_drvdata(&adev->dev, dev); + return 0; +} + +static void acpi_nfit_remove(struct platform_device *pdev) +{ + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + + guard(mutex)(&acpi_notify_lock); + + /* Make notify handlers bail out early going forward. */ + dev_set_drvdata(&adev->dev, NULL); + acpi_nfit_shutdown(platform_get_drvdata(pdev)); } static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle) @@ -3460,6 +3490,9 @@ static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle) { struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev); + if (!acpi_desc) + return; + if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG); else @@ -3489,6 +3522,7 @@ MODULE_DEVICE_TABLE(acpi, acpi_nfit_ids); static struct platform_driver acpi_nfit_driver = { .probe = acpi_nfit_probe, + .remove = acpi_nfit_remove, .driver = { .name = "acpi-nfit", .acpi_match_table = acpi_nfit_ids, diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index a0ba64e45e8a..4c06c3ffd0cb 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -755,6 +755,10 @@ static int acpi_pci_root_add(struct acpi_device *device, pci_lock_rescan_remove(); pci_bus_add_devices(root->bus); pci_unlock_rescan_remove(); + + /* Clear _DEP dependencies to allow consumers to enumerate */ + acpi_dev_clear_dependencies(device); + return 1; remove_dmar: diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index 134e9ca8eaa2..9bd46defc5a2 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c @@ -67,14 +67,12 @@ static acpi_status intel_pmic_power_handler(u32 function, if (result == -ENOENT) return AE_BAD_PARAMETER; - mutex_lock(&opregion->lock); + guard(mutex)(&opregion->lock); result = function == ACPI_READ ? d->get_power(regmap, reg, bit, value64) : d->update_power(regmap, reg, bit, *value64 == 1); - mutex_unlock(&opregion->lock); - return result ? AE_ERROR : AE_OK; } @@ -182,19 +180,16 @@ static acpi_status intel_pmic_thermal_handler(u32 function, if (result == -ENOENT) return AE_BAD_PARAMETER; - mutex_lock(&opregion->lock); - - if (pmic_thermal_is_temp(address)) - result = pmic_thermal_temp(opregion, reg, function, value64); - else if (pmic_thermal_is_aux(address)) - result = pmic_thermal_aux(opregion, reg, function, value64); - else if (pmic_thermal_is_pen(address)) - result = pmic_thermal_pen(opregion, reg, bit, - function, value64); - else - result = -EINVAL; - - mutex_unlock(&opregion->lock); + scoped_guard(mutex, &opregion->lock) { + if (pmic_thermal_is_temp(address)) + result = pmic_thermal_temp(opregion, reg, function, value64); + else if (pmic_thermal_is_aux(address)) + result = pmic_thermal_aux(opregion, reg, function, value64); + else if (pmic_thermal_is_pen(address)) + result = pmic_thermal_pen(opregion, reg, bit, function, value64); + else + result = -EINVAL; + } if (result < 0) { if (result == -EINVAL) @@ -354,13 +349,15 @@ int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address, d = intel_pmic_opregion->data; - mutex_lock(&intel_pmic_opregion->lock); + guard(mutex)(&intel_pmic_opregion->lock); if (d->exec_mipi_pmic_seq_element) { - ret = d->exec_mipi_pmic_seq_element(intel_pmic_opregion->regmap, + return d->exec_mipi_pmic_seq_element(intel_pmic_opregion->regmap, i2c_address, reg_address, value, mask); - } else if (d->pmic_i2c_address) { + } + + if (d->pmic_i2c_address) { if (i2c_address == d->pmic_i2c_address) { ret = regmap_update_bits(intel_pmic_opregion->regmap, reg_address, mask, value); @@ -376,8 +373,6 @@ int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address, ret = -EOPNOTSUPP; } - mutex_unlock(&intel_pmic_opregion->lock); - return ret; } EXPORT_SYMBOL_GPL(intel_soc_pmic_exec_mipi_pmic_seq_element); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ee5facccbe10..390ab5f1d313 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1355,6 +1355,15 @@ void acpi_processor_register_idle_driver(void) int ret = -ENODEV; int cpu; + /* + * If a cpuidle driver is already registered, there is no need to + * evaluate _CST or attempt to register the ACPI idle driver. + */ + if (cpuidle_get_driver()) { + pr_debug("cpuidle driver %pS already registered.\n", cpuidle_get_driver()); + return; + } + acpi_processor_update_max_cstate(); /* diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 530547cda8b2..9a7ac2eb9ce0 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -122,7 +122,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) mutex_lock_nested(&adev->physical_node_lock, SINGLE_DEPTH_NESTING); list_for_each_entry(pn, &adev->physical_node_list, node) - if (device_supports_offline(pn->dev) && !pn->dev->offline) { + if (device_supports_offline(pn->dev) && !dev_offline(pn->dev)) { if (uevent) kobject_uevent_env(&pn->dev->kobj, KOBJ_CHANGE, envp); @@ -848,8 +848,6 @@ static bool acpi_info_matches_ids(struct acpi_device_info *info, static const char * const acpi_ignore_dep_ids[] = { "PNP0D80", /* Windows-compatible System Power Management Controller */ "INT33BD", /* Intel Baytrail Mailbox Device */ - "INTC10DE", /* Intel CVS LNL */ - "INTC10E0", /* Intel CVS ARL */ "LATT2021", /* Lattice FW Update Client Driver */ NULL }; @@ -861,11 +859,15 @@ static const char * const acpi_honor_dep_ids[] = { "INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */ "INTC100A", /* IVSC (RPL) driver must be loaded to allow i2c access to camera sensors */ "INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */ + "INTC10DE", /* CVS (LNL) driver must be loaded to allow camera streaming */ + "INTC10E0", /* CVS (ARL) driver must be loaded to allow camera streaming */ + "INTC10E1", /* CVS (PTL) driver must be loaded to allow camera streaming */ "RSCV0001", /* RISC-V PLIC */ "RSCV0002", /* RISC-V APLIC */ "RSCV0005", /* RISC-V SBI MPXY MBOX */ "RSCV0006", /* RISC-V RPMI SYSMSI */ "PNP0C0F", /* PCI Link Device */ + "ACPI0016", /* CXL/PCIe host bridge: CXL root (ACPI0017) depends on PCI root attach */ NULL }; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index dfc7daa809b5..dd7666c176a0 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -655,8 +655,12 @@ unregister_tzd: return result; } -static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) +static void acpi_thermal_zone_unregister(void *data) { + struct acpi_thermal *tz = data; + + flush_workqueue(acpi_thermal_pm_queue); + thermal_zone_device_disable(tz->thermal_zone); acpi_thermal_zone_sysfs_remove(tz); thermal_zone_device_unregister(tz->thermal_zone); @@ -765,8 +769,9 @@ static void acpi_thermal_check_fn(struct work_struct *work) mutex_unlock(&tz->thermal_check_lock); } -static void acpi_thermal_free_thermal_zone(struct acpi_thermal *tz) +static void acpi_thermal_zone_free(void *data) { + struct acpi_thermal *tz = data; int i; acpi_handle_list_free(&tz->trips.passive.trip.devices); @@ -779,7 +784,8 @@ static void acpi_thermal_free_thermal_zone(struct acpi_thermal *tz) static int acpi_thermal_probe(struct platform_device *pdev) { struct thermal_trip trip_table[ACPI_THERMAL_MAX_NR_TRIPS] = { 0 }; - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct device *dev = &pdev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); struct acpi_thermal_trip *acpi_trip; struct thermal_trip *trip; struct acpi_thermal *tz; @@ -795,6 +801,10 @@ static int acpi_thermal_probe(struct platform_device *pdev) if (!tz) return -ENOMEM; + result = devm_add_action_or_reset(dev, acpi_thermal_zone_free, tz); + if (result) + return result; + platform_set_drvdata(pdev, tz); tz->device = device; @@ -817,7 +827,7 @@ static int acpi_thermal_probe(struct platform_device *pdev) /* Get temperature [_TMP] (required). */ result = acpi_thermal_get_temperature(tz); if (result) - goto free_memory; + return result; /* Determine the default polling frequency [_TZP]. */ if (tzp) @@ -870,7 +880,11 @@ static int acpi_thermal_probe(struct platform_device *pdev) trip - trip_table, passive_delay); if (result) - goto free_memory; + return result; + + result = devm_add_action_or_reset(dev, acpi_thermal_zone_unregister, tz); + if (result) + return result; refcount_set(&tz->thermal_check_count, 3); mutex_init(&tz->thermal_check_lock); @@ -879,32 +893,8 @@ static int acpi_thermal_probe(struct platform_device *pdev) pr_info("Thermal Zone [%s] (%ld C)\n", acpi_device_bid(device), deci_kelvin_to_celsius(tz->temp_dk)); - result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_thermal_notify, tz); - if (result) - goto flush_wq; - - return 0; - -flush_wq: - flush_workqueue(acpi_thermal_pm_queue); - acpi_thermal_unregister_thermal_zone(tz); -free_memory: - acpi_thermal_free_thermal_zone(tz); - - return result; -} - -static void acpi_thermal_remove(struct platform_device *pdev) -{ - struct acpi_thermal *tz = platform_get_drvdata(pdev); - - acpi_dev_remove_notify_handler(tz->device, ACPI_DEVICE_NOTIFY, - acpi_thermal_notify); - - flush_workqueue(acpi_thermal_pm_queue); - acpi_thermal_unregister_thermal_zone(tz); - acpi_thermal_free_thermal_zone(tz); + return devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY, + acpi_thermal_notify, tz); } #ifdef CONFIG_PM_SLEEP @@ -937,7 +927,6 @@ MODULE_DEVICE_TABLE(acpi, thermal_device_ids); static struct platform_driver acpi_thermal_driver = { .probe = acpi_thermal_probe, - .remove = acpi_thermal_remove, .driver = { .name = "acpi-thermal", .acpi_match_table = thermal_device_ids, diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 6d479caf89cb..d721d64a9858 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -82,33 +82,6 @@ static void amba_put_disable_pclk(struct amba_device *pcdev) } -static ssize_t driver_override_show(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct amba_device *dev = to_amba_device(_dev); - ssize_t len; - - device_lock(_dev); - len = sprintf(buf, "%s\n", dev->driver_override); - device_unlock(_dev); - return len; -} - -static ssize_t driver_override_store(struct device *_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct amba_device *dev = to_amba_device(_dev); - int ret; - - ret = driver_set_override(_dev, &dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} -static DEVICE_ATTR_RW(driver_override); - #define amba_attr_func(name,fmt,arg...) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ @@ -126,7 +99,6 @@ amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n", static struct attribute *amba_dev_attrs[] = { &dev_attr_id.attr, &dev_attr_resource.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(amba_dev); @@ -209,10 +181,11 @@ static int amba_match(struct device *dev, const struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); const struct amba_driver *pcdrv = to_amba_driver(drv); + int ret; mutex_lock(&pcdev->periphid_lock); if (!pcdev->periphid) { - int ret = amba_read_periphid(pcdev); + ret = amba_read_periphid(pcdev); /* * Returning any error other than -EPROBE_DEFER from bus match @@ -230,8 +203,9 @@ static int amba_match(struct device *dev, const struct device_driver *drv) mutex_unlock(&pcdev->periphid_lock); /* When driver_override is set, only bind to the matching driver */ - if (pcdev->driver_override) - return !strcmp(pcdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; return amba_lookup(pcdrv->id_table, pcdev) != NULL; } @@ -436,6 +410,7 @@ static const struct dev_pm_ops amba_pm = { const struct bus_type amba_bustype = { .name = "amba", .dev_groups = amba_dev_groups, + .driver_override = true, .match = amba_match, .uevent = amba_uevent, .probe = amba_probe, diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 0cab959e4b7e..b7b05e72970a 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -157,6 +157,14 @@ impl Allocation { self.get_or_init_info().target_node = Some(target_node); } + pub(crate) fn take_oneway_node(&mut self) -> Option<DArc<Node>> { + if let Some(info) = self.allocation_info.as_mut() { + info.oneway_node.take() + } else { + None + } + } + /// Reserve enough space to push at least `num_fds` fds. pub(crate) fn info_add_fd_reserve(&mut self, num_fds: usize) -> Result { self.get_or_init_info() diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 820cbd541435..96b8440ceac6 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1402,7 +1402,12 @@ impl Process { // Clear delivered_deaths list. // // Scope ensures that MutexGuard is dropped while executing the body. - while let Some(delivered_death) = { self.inner.lock().delivered_deaths.pop_front() } { + while let Some(delivered_death) = { + // Explicitly bind to avoid tail expression lifetime extension of the lockguard + // Can be removed when the kernel moves to edition 2024 + let maybe_death = self.inner.lock().delivered_deaths.pop_front(); + maybe_death + } { drop(delivered_death); } diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 47d5e4d88b07..1d9b66920a21 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -270,7 +270,8 @@ impl Transaction { /// Not used for replies. pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderResult { // Defined before `process_inner` so that the destructor runs after releasing the lock. - let mut _t_outdated; + let _t_outdated; + let _oneway_node; let oneway = self.flags & TF_ONE_WAY != 0; let process = self.to.clone(); @@ -287,6 +288,14 @@ impl Transaction { if let Some(t_outdated) = target_node.take_outdated_transaction(&self, &mut process_inner) { + let mut alloc_guard = t_outdated.allocation.lock(); + if let Some(alloc) = (*alloc_guard).as_mut() { + // Take the oneway node to prevent `Allocation::drop` from calling + // `pending_oneway_finished()`, which would be incorrect as this + // transaction is not being submitted. + _oneway_node = alloc.take_oneway_node(); + } + drop(alloc_guard); // Save the transaction to be dropped after locks are released. _t_outdated = t_outdated; } diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index bedc6133f970..1ea7c039160c 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -327,7 +327,7 @@ config PANEL_CHANGE_MESSAGE say 'N' and keep the default message with the version. config PANEL_BOOT_MESSAGE - depends on PANEL_CHANGE_MESSAGE="y" + depends on PANEL_CHANGE_MESSAGE string "New initialization message" default "" help diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c index fb6d9294140d..915eb5cd96b2 100644 --- a/drivers/auxdisplay/line-display.c +++ b/drivers/auxdisplay/line-display.c @@ -173,7 +173,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg, count = strlen(msg); /* if the string ends with a newline, trim it */ - if (msg[count - 1] == '\n') + if (count && msg[count - 1] == '\n') count--; if (!count) { diff --git a/drivers/auxdisplay/max6959.c b/drivers/auxdisplay/max6959.c index 6bbc8d48fb1b..3bdef099a225 100644 --- a/drivers/auxdisplay/max6959.c +++ b/drivers/auxdisplay/max6959.c @@ -86,10 +86,7 @@ static const struct linedisp_ops max6959_linedisp_ops = { static int max6959_enable(struct max6959_priv *priv, bool enable) { - u8 mask = REG_CONFIGURATION_S_BIT; - u8 value = enable ? mask : 0; - - return regmap_update_bits(priv->regmap, REG_CONFIGURATION, mask, value); + return regmap_assign_bits(priv->regmap, REG_CONFIGURATION, REG_CONFIGURATION_S_BIT, enable); } static void max6959_power_off(void *priv) diff --git a/drivers/base/base.h b/drivers/base/base.h index 30b416588617..a5b7abc10ff0 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -86,18 +86,6 @@ struct driver_private { }; #define to_driver(obj) container_of(obj, struct driver_private, kobj) -#ifdef CONFIG_RUST -/** - * struct driver_type - Representation of a Rust driver type. - */ -struct driver_type { - /** - * @id: Representation of core::any::TypeId. - */ - u8 id[16]; -} __packed; -#endif - /** * struct device_private - structure to hold the private to the driver core * portions of the device structure. @@ -115,7 +103,6 @@ struct driver_type { * dev_err_probe() for later retrieval via debugfs * @device: pointer back to the struct device that this structure is * associated with. - * @driver_type: The type of the bound Rust driver. * @dead: This device is currently either in the process of or has been * removed from the system. Any asynchronous events scheduled for this * device should exit without taking any action. @@ -132,9 +119,6 @@ struct device_private { const struct device_driver *async_driver; char *deferred_probe_reason; struct device *device; -#ifdef CONFIG_RUST - struct driver_type driver_type; -#endif u8 dead:1; }; #define to_device_private_parent(obj) \ @@ -188,6 +172,20 @@ static inline int driver_match_device(const struct device_driver *drv, return drv->bus->match ? drv->bus->match(dev, drv) : 1; } +static inline bool dev_has_sync_state(struct device *dev) +{ + struct device_driver *drv; + + if (!dev) + return false; + drv = READ_ONCE(dev->driver); + if (drv && drv->sync_state) + return true; + if (dev->bus && dev->bus->sync_state) + return true; + return false; +} + static inline void dev_sync_state(struct device *dev) { if (dev->bus->sync_state) @@ -196,8 +194,10 @@ static inline void dev_sync_state(struct device *dev) dev->driver->sync_state(dev); } -int driver_add_groups(const struct device_driver *drv, const struct attribute_group **groups); -void driver_remove_groups(const struct device_driver *drv, const struct attribute_group **groups); +int driver_add_groups(const struct device_driver *drv, + const struct attribute_group *const *groups); +void driver_remove_groups(const struct device_driver *drv, + const struct attribute_group *const *groups); void device_driver_detach(struct device *dev); static inline void device_set_driver(struct device *dev, const struct device_driver *drv) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 8b6722ff8590..d17bd91490ee 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -544,10 +544,10 @@ static const struct attribute_group driver_override_dev_group = { */ int bus_add_device(struct device *dev) { - struct subsys_private *sp = bus_to_subsys(dev->bus); + struct subsys_private *sp; int error; - if (!sp) { + if (!dev->bus) { /* * This is a normal operation for many devices that do not * have a bus assigned to them, just say that all went @@ -556,6 +556,13 @@ int bus_add_device(struct device *dev) return 0; } + sp = bus_to_subsys(dev->bus); + if (!sp) { + pr_err("%s: cannot add device '%s' to unregistered bus '%s'\n", + __func__, dev_name(dev), dev->bus->name); + return -EINVAL; + } + /* * Reference in sp is now incremented and will be dropped when * the device is removed from the bus diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 391ac5e3d2f5..70701d3bc81c 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/of.h> #include <linux/sched.h> +#include <linux/sched/topology.h> #include <linux/slab.h> #include <linux/smp.h> #include <linux/sysfs.h> @@ -68,6 +69,24 @@ bool last_level_cache_is_valid(unsigned int cpu) } +/* + * Get the cacheinfo of the LLC associated with @cpu. + * Derived from update_per_cpu_data_slice_size_cpu(). + */ +struct cacheinfo *get_cpu_cacheinfo_llc(unsigned int cpu) +{ + struct cacheinfo *llc; + + if (!last_level_cache_is_valid(cpu)) + return NULL; + + llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1); + if (llc->type != CACHE_TYPE_DATA && llc->type != CACHE_TYPE_UNIFIED) + return NULL; + + return llc; +} + bool last_level_cache_is_shared(unsigned int cpu_x, unsigned int cpu_y) { struct cacheinfo *llc_x, *llc_y; @@ -1018,6 +1037,7 @@ static int cacheinfo_cpu_online(unsigned int cpu) goto err; if (cpu_map_shared_cache(true, cpu, &cpu_map)) update_per_cpu_data_slice_size(true, cpu, cpu_map); + sched_update_llc_bytes(cpu); return 0; err: free_cache_attributes(cpu); @@ -1036,6 +1056,9 @@ static int cacheinfo_cpu_pre_down(unsigned int cpu) free_cache_attributes(cpu); if (nr_shared > 1) update_per_cpu_data_slice_size(false, cpu, cpu_map); + + sched_update_llc_bytes(cpu); + return 0; } diff --git a/drivers/base/core.c b/drivers/base/core.c index bd2ddf2aab50..4d026682944f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -235,6 +235,79 @@ static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode, __fw_devlink_pickup_dangling_consumers(child, new_sup); } +static void fw_devlink_pickup_dangling_consumers(struct device *dev) +{ + struct fwnode_handle *child; + + guard(mutex)(&fwnode_link_lock); + + fwnode_for_each_available_child_node(dev->fwnode, child) + __fw_devlink_pickup_dangling_consumers(child, dev->fwnode); + __fw_devlink_link_to_consumers(dev); +} + +/** + * fw_devlink_refresh_fwnode - Recheck the tree under this firmware node + * @fwnode: The fwnode under which the fwnode tree has changed + * + * This function is mainly meant to adjust the supplier/consumer dependencies + * after a fwnode tree overlay has occurred. + */ +void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode) +{ + struct device *dev; + + /* + * Find the closest ancestor fwnode that has been converted to a device + * that can bind to a driver (bus device). + */ + fwnode_handle_get(fwnode); + do { + if (fwnode_test_flag(fwnode, FWNODE_FLAG_NOT_DEVICE)) + continue; + + dev = get_dev_from_fwnode(fwnode); + if (!dev) + continue; + + if (dev->bus) + break; + + put_device(dev); + } while ((fwnode = fwnode_get_next_parent(fwnode))); + + /* + * If none of the ancestor fwnodes have (yet) been converted to a device + * that can bind to a driver, there's nothing to fix up. + */ + if (!fwnode) + return; + + WARN(device_is_bound(dev) && dev->links.status != DL_DEV_DRIVER_BOUND, + "Don't multithread overlaying and probing the same device!\n"); + + /* + * If the device has already bound to a driver, then we need to redo + * some of the work that was done after the device was bound to a + * driver. If the device hasn't bound to a driver, running things too + * soon would incorrectly pick up consumers that it shouldn't. + */ + if (dev->links.status == DL_DEV_DRIVER_BOUND) { + fw_devlink_pickup_dangling_consumers(dev); + /* + * Some of dangling consumers could have been put previously in + * the deferred probe list due to the unavailability of their + * suppliers. Those consumers have been picked up and some of + * their suppliers links have been updated. Time to re-try their + * probe sequence. + */ + driver_deferred_probe_trigger(); + } + + put_device(dev); + fwnode_handle_put(fwnode); +} + static DEFINE_MUTEX(device_links_lock); DEFINE_STATIC_SRCU(device_links_srcu); @@ -422,7 +495,7 @@ void device_pm_move_to_tail(struct device *dev) #define to_devlink(dev) container_of((dev), struct device_link, link_dev) static ssize_t status_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { const char *output; @@ -452,10 +525,10 @@ static ssize_t status_show(struct device *dev, return sysfs_emit(buf, "%s\n", output); } -static DEVICE_ATTR_RO(status); +static const DEVICE_ATTR_RO(status); static ssize_t auto_remove_on_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { struct device_link *link = to_devlink(dev); const char *output; @@ -469,27 +542,27 @@ static ssize_t auto_remove_on_show(struct device *dev, return sysfs_emit(buf, "%s\n", output); } -static DEVICE_ATTR_RO(auto_remove_on); +static const DEVICE_ATTR_RO(auto_remove_on); static ssize_t runtime_pm_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { struct device_link *link = to_devlink(dev); return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_PM_RUNTIME)); } -static DEVICE_ATTR_RO(runtime_pm); +static const DEVICE_ATTR_RO(runtime_pm); static ssize_t sync_state_only_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { struct device_link *link = to_devlink(dev); return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); } -static DEVICE_ATTR_RO(sync_state_only); +static const DEVICE_ATTR_RO(sync_state_only); -static struct attribute *devlink_attrs[] = { +static const struct attribute *const devlink_attrs[] = { &dev_attr_status.attr, &dev_attr_auto_remove_on.attr, &dev_attr_runtime_pm.attr, @@ -1011,7 +1084,7 @@ static void device_links_missing_supplier(struct device *dev) static bool dev_is_best_effort(struct device *dev) { - return (fw_devlink_best_effort && dev->can_match) || + return (fw_devlink_best_effort && dev_can_match(dev)) || (dev->fwnode && fwnode_test_flag(dev->fwnode, FWNODE_FLAG_BEST_EFFORT)); } @@ -1079,7 +1152,7 @@ int device_links_check_suppliers(struct device *dev) if (dev_is_best_effort(dev) && device_link_test(link, DL_FLAG_INFERRED) && - !link->supplier->can_match) { + !dev_can_match(link->supplier)) { ret = -EAGAIN; continue; } @@ -1123,7 +1196,7 @@ static void __device_links_queue_sync_state(struct device *dev, if (!dev_has_sync_state(dev)) return; - if (dev->state_synced) + if (dev_state_synced(dev)) return; list_for_each_entry(link, &dev->links.consumers, s_node) { @@ -1138,7 +1211,7 @@ static void __device_links_queue_sync_state(struct device *dev, * than once. This can happen if new consumers get added to the device * and probed before the list is flushed. */ - dev->state_synced = true; + dev_set_state_synced(dev); if (WARN_ON(!list_empty(&dev->links.defer_sync))) return; @@ -1233,7 +1306,7 @@ static void device_link_drop_managed(struct device_link *link) } static ssize_t waiting_for_supplier_show(struct device *dev, - struct device_attribute *attr, + const struct device_attribute *attr, char *buf) { bool val; @@ -1244,7 +1317,7 @@ static ssize_t waiting_for_supplier_show(struct device *dev, device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } -static DEVICE_ATTR_RO(waiting_for_supplier); +static const DEVICE_ATTR_RO(waiting_for_supplier); /** * device_links_force_bind - Prepares device to be force bound @@ -1312,16 +1385,8 @@ void device_links_driver_bound(struct device *dev) * child firmware node. */ if (dev->fwnode && dev->fwnode->dev == dev) { - struct fwnode_handle *child; - fwnode_links_purge_suppliers(dev->fwnode); - - guard(mutex)(&fwnode_link_lock); - - fwnode_for_each_available_child_node(dev->fwnode, child) - __fw_devlink_pickup_dangling_consumers(child, - dev->fwnode); - __fw_devlink_link_to_consumers(dev); + fw_devlink_pickup_dangling_consumers(dev); } device_remove_file(dev, &dev_attr_waiting_for_supplier); @@ -1370,7 +1435,7 @@ void device_links_driver_bound(struct device *dev) } else if (dev_is_best_effort(dev) && device_link_test(link, DL_FLAG_INFERRED) && link->status != DL_STATE_CONSUMER_PROBE && - !link->supplier->can_match) { + !dev_can_match(link->supplier)) { /* * When dev_is_best_effort() is true, we ignore device * links to suppliers that don't have a driver. If the @@ -1435,7 +1500,8 @@ static void __device_links_no_driver(struct device *dev) if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) { WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } else { - WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); + WARN_ON(link->supplier->links.status != DL_DEV_UNBINDING && + !device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); WRITE_ONCE(link->status, DL_STATE_DORMANT); } } @@ -1758,7 +1824,7 @@ static int fw_devlink_no_driver(struct device *dev, void *data) { struct device_link *link = to_devlink(dev); - if (!link->supplier->can_match) + if (!dev_can_match(link->supplier)) fw_devlink_relax_link(link); return 0; @@ -1779,7 +1845,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data) struct device *sup = link->supplier; if (!device_link_test(link, DL_FLAG_MANAGED) || - link->status == DL_STATE_ACTIVE || sup->state_synced || + link->status == DL_STATE_ACTIVE || dev_state_synced(sup) || !dev_has_sync_state(sup)) return 0; @@ -1793,7 +1859,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data) return 0; dev_warn(sup, "Timed out. Forcing sync_state()\n"); - sup->state_synced = true; + dev_set_state_synced(sup); get_device(sup); list_add_tail(&sup->links.defer_sync, data); @@ -2419,9 +2485,11 @@ static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, if (dev_attr->show) ret = dev_attr->show(dev, dev_attr, buf); + else if (dev_attr->show_const) + ret = dev_attr->show_const(dev, dev_attr, buf); if (ret >= (ssize_t)PAGE_SIZE) { - printk("dev_attr_show: %pS returned bad count\n", - dev_attr->show); + printk("dev_attr_show: %pS/%pS returned bad count\n", + dev_attr->show, dev_attr->show_const); } return ret; } @@ -2435,6 +2503,8 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, if (dev_attr->store) ret = dev_attr->store(dev, dev_attr, buf, count); + else if (dev_attr->store_const) + ret = dev_attr->store_const(dev, dev_attr, buf, count); return ret; } @@ -2722,7 +2792,7 @@ static const struct kset_uevent_ops device_uevent_ops = { .uevent = dev_uevent, }; -static ssize_t uevent_show(struct device *dev, struct device_attribute *attr, +static ssize_t uevent_show(struct device *dev, const struct device_attribute *attr, char *buf) { struct kobject *top_kobj; @@ -2765,7 +2835,7 @@ out: return len; } -static ssize_t uevent_store(struct device *dev, struct device_attribute *attr, +static ssize_t uevent_store(struct device *dev, const struct device_attribute *attr, const char *buf, size_t count) { int rc; @@ -2779,20 +2849,20 @@ static ssize_t uevent_store(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR_RW(uevent); +static const DEVICE_ATTR_RW(uevent); -static ssize_t online_show(struct device *dev, struct device_attribute *attr, +static ssize_t online_show(struct device *dev, const struct device_attribute *attr, char *buf) { bool val; device_lock(dev); - val = !dev->offline; + val = !dev_offline(dev); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } -static ssize_t online_store(struct device *dev, struct device_attribute *attr, +static ssize_t online_store(struct device *dev, const struct device_attribute *attr, const char *buf, size_t count) { bool val; @@ -2810,9 +2880,9 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr, unlock_device_hotplug(); return ret < 0 ? ret : count; } -static DEVICE_ATTR_RW(online); +static const DEVICE_ATTR_RW(online); -static ssize_t removable_show(struct device *dev, struct device_attribute *attr, +static ssize_t removable_show(struct device *dev, const struct device_attribute *attr, char *buf) { const char *loc; @@ -2829,7 +2899,7 @@ static ssize_t removable_show(struct device *dev, struct device_attribute *attr, } return sysfs_emit(buf, "%s\n", loc); } -static DEVICE_ATTR_RO(removable); +static const DEVICE_ATTR_RO(removable); int device_add_groups(struct device *dev, const struct attribute_group *const *groups) @@ -2913,7 +2983,7 @@ static int device_add_attrs(struct device *dev) if (error) goto err_remove_type_groups; - if (device_supports_offline(dev) && !dev->offline_disabled) { + if (device_supports_offline(dev) && !dev_offline_disabled(dev)) { error = device_create_file(dev, &dev_attr_online); if (error) goto err_remove_dev_groups; @@ -2980,12 +3050,12 @@ static void device_remove_attrs(struct device *dev) device_remove_groups(dev, class->dev_groups); } -static ssize_t dev_show(struct device *dev, struct device_attribute *attr, +static ssize_t dev_show(struct device *dev, const struct device_attribute *attr, char *buf) { return print_dev_t(buf, dev->devt); } -static DEVICE_ATTR_RO(dev); +static const DEVICE_ATTR_RO(dev); /* /sys/devices/ */ struct kset *devices_kset; @@ -3047,10 +3117,10 @@ int device_create_file(struct device *dev, int error = 0; if (dev) { - WARN(((attr->attr.mode & S_IWUGO) && !attr->store), + WARN(((attr->attr.mode & S_IWUGO) && !(attr->store || attr->store_const)), "Attribute %s: write permission without 'store'\n", attr->attr.name); - WARN(((attr->attr.mode & S_IRUGO) && !attr->show), + WARN(((attr->attr.mode & S_IRUGO) && !(attr->show || attr->show_const)), "Attribute %s: read permission without 'show'\n", attr->attr.name); error = sysfs_create_file(&dev->kobj, &attr->attr); @@ -3170,11 +3240,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; -#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ - defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ - defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) - dev->dma_coherent = dma_default_coherent; -#endif + dev_assign_dma_coherent(dev, dma_default_coherent); swiotlb_dev_init(dev); } EXPORT_SYMBOL_GPL(device_initialize); @@ -3710,7 +3776,7 @@ int device_add(struct device *dev) * match with any driver, don't block its consumers from probing in * case the consumer device is able to operate without this supplier. */ - if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match) + if (dev->fwnode && fw_devlink_drv_reg_done && !dev_can_match(dev)) fw_devlink_unblock_consumers(dev); if (parent) @@ -4180,7 +4246,7 @@ static int device_check_offline(struct device *dev, void *not_used) if (ret) return ret; - return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0; + return device_supports_offline(dev) && !dev_offline(dev) ? -EBUSY : 0; } /** @@ -4198,7 +4264,7 @@ int device_offline(struct device *dev) { int ret; - if (dev->offline_disabled) + if (dev_offline_disabled(dev)) return -EPERM; ret = device_for_each_child(dev, NULL, device_check_offline); @@ -4207,13 +4273,13 @@ int device_offline(struct device *dev) device_lock(dev); if (device_supports_offline(dev)) { - if (dev->offline) { + if (dev_offline(dev)) { ret = 1; } else { ret = dev->bus->offline(dev); if (!ret) { kobject_uevent(&dev->kobj, KOBJ_OFFLINE); - dev->offline = true; + dev_set_offline(dev); } } } @@ -4238,11 +4304,11 @@ int device_online(struct device *dev) device_lock(dev); if (device_supports_offline(dev)) { - if (dev->offline) { + if (dev_offline(dev)) { ret = dev->bus->online(dev); if (!ret) { kobject_uevent(&dev->kobj, KOBJ_ONLINE); - dev->offline = false; + dev_clear_offline(dev); } } else { ret = 1; @@ -4716,7 +4782,7 @@ static int device_attrs_change_owner(struct device *dev, kuid_t kuid, if (error) return error; - if (device_supports_offline(dev) && !dev->offline_disabled) { + if (device_supports_offline(dev) && !dev_offline_disabled(dev)) { /* Change online device attributes of @dev to @kuid/@kgid. */ error = sysfs_file_change_owner(kobj, dev_attr_online.attr.name, kuid, kgid); @@ -5283,7 +5349,7 @@ void device_set_of_node_from_dev(struct device *dev, const struct device *dev2) { of_node_put(dev->of_node); dev->of_node = of_node_get(dev2->of_node); - dev->of_node_reused = true; + dev_set_of_node_reused(dev); } EXPORT_SYMBOL_GPL(device_set_of_node_from_dev); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 875abdc9942e..19d288a3c80c 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -422,8 +422,8 @@ int register_cpu(struct cpu *cpu, int num) cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; cpu->dev.release = cpu_device_release; - cpu->dev.offline_disabled = !cpu->hotpluggable; - cpu->dev.offline = !cpu_online(num); + dev_assign_offline_disabled(&cpu->dev, !cpu->hotpluggable); + dev_assign_offline(&cpu->dev, !cpu_online(num)); cpu->dev.of_node = of_get_cpu_node(num, NULL); cpu->dev.groups = common_cpu_attr_groups; if (cpu->hotpluggable) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 1dc1e3528043..60c005223844 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -132,7 +132,7 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); void driver_deferred_probe_add(struct device *dev) { - if (!dev->can_match) + if (!dev_can_match(dev)) return; mutex_lock(&deferred_probe_mutex); @@ -323,12 +323,11 @@ void deferred_probe_extend_timeout(void) * If the work hasn't been queued yet or if the work expired, don't * start a new one. */ - if (cancel_delayed_work(&deferred_probe_timeout_work)) { - schedule_delayed_work(&deferred_probe_timeout_work, - driver_deferred_probe_timeout * HZ); + if (delayed_work_pending(&deferred_probe_timeout_work) && + mod_delayed_work(system_percpu_wq, &deferred_probe_timeout_work, + secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); - } } /** @@ -569,12 +568,10 @@ static ssize_t state_synced_store(struct device *dev, return -EINVAL; device_lock(dev); - if (!dev->state_synced) { - dev->state_synced = true; + if (!dev_test_and_set_state_synced(dev)) dev_sync_state(dev); - } else { + else ret = -EINVAL; - } device_unlock(dev); return ret ? ret : count; @@ -586,7 +583,7 @@ static ssize_t state_synced_show(struct device *dev, bool val; device_lock(dev); - val = dev->state_synced; + val = dev_state_synced(dev); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); @@ -595,9 +592,9 @@ static DEVICE_ATTR_RW(state_synced); static void device_unbind_cleanup(struct device *dev) { - devres_release_all(dev); if (dev->driver->p_cb.post_unbind_rust) dev->driver->p_cb.post_unbind_rust(dev); + devres_release_all(dev); arch_teardown_dma_ops(dev); kfree(dev->dma_range_map); dev->dma_range_map = NULL; @@ -849,14 +846,14 @@ static int __driver_probe_device(const struct device_driver *drv, struct device return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n"); /* - * Set can_match = true after calling dev_ready_to_probe(), so + * Call dev_set_can_match() after calling dev_ready_to_probe(), so * driver_deferred_probe_add() won't actually add the device to the * deferred probe list when dev_ready_to_probe() returns false. * * When dev_ready_to_probe() returns false, it means that device_add() * will do another probe() attempt for us. */ - dev->can_match = true; + dev_set_can_match(dev); dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n", drv->bus->name, __func__, drv->name); @@ -1002,7 +999,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); - dev->can_match = true; + dev_set_can_match(dev); driver_deferred_probe_add(dev); /* * Device can't match with a driver right now, so don't attempt @@ -1254,7 +1251,7 @@ static int __driver_attach(struct device *dev, void *data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); - dev->can_match = true; + dev_set_can_match(dev); driver_deferred_probe_add(dev); /* * Driver could not match with device, but may match with diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 7e4a491bf15e..8bb1763083dd 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -471,10 +471,3 @@ static int __init devcoredump_init(void) return class_register(&devcd_class); } __initcall(devcoredump_init); - -static void __exit devcoredump_exit(void) -{ - class_for_each_device(&devcd_class, NULL, NULL, devcd_free); - class_unregister(&devcd_class); -} -__exitcall(devcoredump_exit); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 8ab010ddf709..5d9c39081339 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -31,81 +31,6 @@ static struct device *next_device(struct klist_iter *i) } /** - * driver_set_override() - Helper to set or clear driver override. - * @dev: Device to change - * @override: Address of string to change (e.g. &device->driver_override); - * The contents will be freed and hold newly allocated override. - * @s: NUL-terminated string, new driver name to force a match, pass empty - * string to clear it ("" or "\n", where the latter is only for sysfs - * interface). - * @len: length of @s - * - * Helper to set or clear driver override in a device, intended for the cases - * when the driver_override field is allocated by driver/bus code. - * - * Returns: 0 on success or a negative error code on failure. - */ -int driver_set_override(struct device *dev, const char **override, - const char *s, size_t len) -{ - const char *new, *old; - char *cp; - - if (!override || !s) - return -EINVAL; - - /* - * The stored value will be used in sysfs show callback (sysfs_emit()), - * which has a length limit of PAGE_SIZE and adds a trailing newline. - * Thus we can store one character less to avoid truncation during sysfs - * show. - */ - if (len >= (PAGE_SIZE - 1)) - return -EINVAL; - - /* - * Compute the real length of the string in case userspace sends us a - * bunch of \0 characters like python likes to do. - */ - len = strlen(s); - - if (!len) { - /* Empty string passed - clear override */ - device_lock(dev); - old = *override; - *override = NULL; - device_unlock(dev); - kfree(old); - - return 0; - } - - cp = strnchr(s, len, '\n'); - if (cp) - len = cp - s; - - new = kstrndup(s, len, GFP_KERNEL); - if (!new) - return -ENOMEM; - - device_lock(dev); - old = *override; - if (cp != s) { - *override = new; - } else { - /* "\n" passed - clear override */ - kfree(new); - *override = NULL; - } - device_unlock(dev); - - kfree(old); - - return 0; -} -EXPORT_SYMBOL_GPL(driver_set_override); - -/** * driver_for_each_device - Iterator for devices bound to a driver. * @drv: Driver we're iterating. * @start: Device to begin with @@ -203,13 +128,13 @@ void driver_remove_file(const struct device_driver *drv, EXPORT_SYMBOL_GPL(driver_remove_file); int driver_add_groups(const struct device_driver *drv, - const struct attribute_group **groups) + const struct attribute_group *const *groups) { return sysfs_create_groups(&drv->p->kobj, groups); } void driver_remove_groups(const struct device_driver *drv, - const struct attribute_group **groups) + const struct attribute_group *const *groups) { sysfs_remove_groups(&drv->p->kobj, groups); } diff --git a/drivers/base/faux.c b/drivers/base/faux.c index fb3e42f21362..a8329f88222e 100644 --- a/drivers/base/faux.c +++ b/drivers/base/faux.c @@ -133,6 +133,9 @@ struct faux_device *faux_device_create_with_groups(const char *name, struct device *dev; int ret; + if (!faux_bus_root) + return NULL; + faux_obj = kzalloc_obj(*faux_obj); if (!faux_obj) return NULL; @@ -232,34 +235,29 @@ EXPORT_SYMBOL_GPL(faux_device_destroy); int __init faux_bus_init(void) { + struct device *root; int ret; - faux_bus_root = kzalloc_obj(*faux_bus_root); - if (!faux_bus_root) - return -ENOMEM; - - dev_set_name(faux_bus_root, "faux"); - - ret = device_register(faux_bus_root); - if (ret) { - put_device(faux_bus_root); - return ret; - } + root = root_device_register("faux"); + if (IS_ERR(root)) + return PTR_ERR(root); ret = bus_register(&faux_bus_type); if (ret) - goto error_bus; + goto err_deregister_root; ret = driver_register(&faux_driver); if (ret) - goto error_driver; + goto err_deregister_bus; - return ret; + faux_bus_root = root; + + return 0; -error_driver: +err_deregister_bus: bus_unregister(&faux_bus_type); +err_deregister_root: + root_device_unregister(root); -error_bus: - device_unregister(faux_bus_root); return ret; } diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index a11b30dda23b..c96312ac2be7 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -1503,9 +1503,10 @@ static void device_cache_fw_images(void) mutex_lock(&fw_lock); fwc->state = FW_LOADER_START_CACHE; - dpm_for_each_dev(NULL, dev_cache_fw_image); mutex_unlock(&fw_lock); + dpm_for_each_dev(NULL, dev_cache_fw_image); + /* wait for completion of caching firmware for all devices */ async_synchronize_full_domain(&fw_cache_domain); diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c index f59a7856934c..efc33294212f 100644 --- a/drivers/base/firmware_loader/sysfs_upload.c +++ b/drivers/base/firmware_loader/sysfs_upload.c @@ -343,7 +343,6 @@ firmware_upload_register(struct module *module, struct device *parent, goto free_fw_upload_priv; } fw_upload->priv = fw_sysfs; - fw_sysfs->fw_upload_priv = fw_upload_priv; fw_dev = &fw_sysfs->dev; ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, NULL, 0, 0, @@ -351,10 +350,12 @@ firmware_upload_register(struct module *module, struct device *parent, if (ret != 0) { if (ret > 0) ret = -EINVAL; - goto free_fw_sysfs; + put_device(fw_dev); + goto free_fw_upload_priv; } fw_priv->is_paged_buf = true; fw_sysfs->fw_priv = fw_priv; + fw_sysfs->fw_upload_priv = fw_upload_priv; ret = device_add(fw_dev); if (ret) { @@ -365,9 +366,6 @@ firmware_upload_register(struct module *module, struct device *parent, return fw_upload; -free_fw_sysfs: - kfree(fw_sysfs); - free_fw_upload_priv: kfree(fw_upload_priv); diff --git a/drivers/base/isa.c b/drivers/base/isa.c index fd076cc63cb6..5887e4211f80 100644 --- a/drivers/base/isa.c +++ b/drivers/base/isa.c @@ -11,9 +11,7 @@ #include <linux/dma-mapping.h> #include <linux/isa.h> -static struct device isa_bus = { - .init_name = "isa" -}; +static struct device *isa_bus; struct isa_dev { struct device dev; @@ -131,7 +129,7 @@ int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev) break; } - isa_dev->dev.parent = &isa_bus; + isa_dev->dev.parent = isa_bus; isa_dev->dev.bus = &isa_bus_type; dev_set_name(&isa_dev->dev, "%s.%u", @@ -169,9 +167,11 @@ static int __init isa_bus_init(void) error = bus_register(&isa_bus_type); if (!error) { - error = device_register(&isa_bus); - if (error) + isa_bus = root_device_register("isa"); + if (IS_ERR(isa_bus)) { + error = PTR_ERR(isa_bus); bus_unregister(&isa_bus_type); + } } return error; } diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 6981b55d582a..11d57cfa8d72 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -697,7 +697,7 @@ static int __add_memory_block(struct memory_block *memory) memory->dev.id = memory->start_section_nr / sections_per_block; memory->dev.release = memory_block_release; memory->dev.groups = memory_memblk_attr_groups; - memory->dev.offline = memory->state == MEM_OFFLINE; + dev_assign_offline(&memory->dev, memory->state == MEM_OFFLINE); ret = device_register(&memory->dev); if (ret) { diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c index 6e250272c843..0bbc83231234 100644 --- a/drivers/base/pinctrl.c +++ b/drivers/base/pinctrl.c @@ -24,7 +24,7 @@ int pinctrl_bind_pins(struct device *dev) { int ret; - if (dev->of_node_reused) + if (dev_of_node_reused(dev)) return 0; dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a19dd22deef2..fb9120b0bcfe 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -870,7 +870,7 @@ struct platform_device *platform_device_register_full(const struct platform_devi pdev->dev.parent = pdevinfo->parent; pdev->dev.fwnode = pdevinfo->fwnode; pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode)); - pdev->dev.of_node_reused = pdevinfo->of_node_reused; + dev_assign_of_node_reused(&pdev->dev, pdevinfo->of_node_reused); if (pdevinfo->dma_mask) { pdev->platform_dma_mask = pdevinfo->dma_mask; @@ -915,11 +915,14 @@ EXPORT_SYMBOL_GPL(platform_device_register_full); * __platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure * @owner: owning module/driver + * @mod_name: module name string */ -int __platform_driver_register(struct platform_driver *drv, struct module *owner) +int __platform_driver_register(struct platform_driver *drv, struct module *owner, + const char *mod_name) { drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; + drv->driver.mod_name = mod_name; return driver_register(&drv->driver); } @@ -952,6 +955,7 @@ static int is_bound_to_driver(struct device *dev, void *driver) * @drv: platform driver structure * @probe: the driver probe routine, probably from an __init section * @module: module which will be the owner of the driver + * @mod_name: module name string * * Use this instead of platform_driver_register() when you know the device * is not hotpluggable and has already been registered, and you want to @@ -969,7 +973,8 @@ static int is_bound_to_driver(struct device *dev, void *driver) */ int __init_or_module __platform_driver_probe(struct platform_driver *drv, int (*probe)(struct platform_device *), - struct module *module) + struct module *module, + const char *mod_name) { int retval; @@ -997,7 +1002,7 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, /* temporary section violation during probe() */ drv->probe = probe; - retval = __platform_driver_register(drv, module); + retval = __platform_driver_register(drv, module, mod_name); if (retval) return retval; @@ -1025,6 +1030,7 @@ EXPORT_SYMBOL_GPL(__platform_driver_probe); * @data: platform specific data for this platform device * @size: size of platform specific data * @module: module which will be the owner of the driver + * @mod_name: module name string * * Use this in legacy-style modules that probe hardware directly and * register a single platform device and corresponding platform driver. @@ -1035,7 +1041,7 @@ struct platform_device * __init_or_module __platform_create_bundle(struct platform_driver *driver, int (*probe)(struct platform_device *), struct resource *res, unsigned int n_res, - const void *data, size_t size, struct module *module) + const void *data, size_t size, struct module *module, const char *mod_name) { struct platform_device *pdev; int error; @@ -1058,7 +1064,7 @@ __platform_create_bundle(struct platform_driver *driver, if (error) goto err_pdev_put; - error = __platform_driver_probe(driver, probe, module); + error = __platform_driver_probe(driver, probe, module, mod_name); if (error) goto err_pdev_del; @@ -1078,6 +1084,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle); * @drivers: an array of drivers to register * @count: the number of drivers to register * @owner: module owning the drivers + * @mod_name: module name string * * Registers platform drivers specified by an array. On failure to register a * driver, all previously registered drivers will be unregistered. Callers of @@ -1087,7 +1094,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle); * Returns: 0 on success or a negative error code on failure. */ int __platform_register_drivers(struct platform_driver * const *drivers, - unsigned int count, struct module *owner) + unsigned int count, struct module *owner, const char *mod_name) { unsigned int i; int err; @@ -1095,7 +1102,7 @@ int __platform_register_drivers(struct platform_driver * const *drivers, for (i = 0; i < count; i++) { pr_debug("registering platform driver %ps\n", drivers[i]); - err = __platform_driver_register(drivers[i], owner); + err = __platform_driver_register(drivers[i], owner, mod_name); if (err < 0) { pr_err("failed to register platform driver %ps: %d\n", drivers[i], err); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e1b550664bab..f71467f6ada4 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -28,6 +28,7 @@ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/sched/debug.h> +#include <linux/sysctl.h> #include <linux/async.h> #include <linux/suspend.h> #include <trace/events/power.h> @@ -115,7 +116,7 @@ void device_pm_sleep_init(struct device *dev) dev->power.is_noirq_suspended = false; dev->power.is_late_suspended = false; init_completion(&dev->power.completion); - complete_all(&dev->power.completion); + complete(&dev->power.completion); dev->power.wakeup = NULL; INIT_LIST_HEAD(&dev->power.entry); } @@ -252,6 +253,10 @@ static void dpm_wait(struct device *dev, bool async) if (!dev) return; + /* Devices with no PM support don't use the completion. */ + if (dev->power.no_pm) + return; + if (async || (pm_async_enabled && dev->power.async_suspend)) wait_for_completion(&dev->power.completion); } @@ -527,6 +532,58 @@ module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644); MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace, "Backtrace all CPUs on DPM watchdog timeout"); +static unsigned int __read_mostly dpm_watchdog_timeout = CONFIG_DPM_WATCHDOG_TIMEOUT; +static unsigned int __read_mostly dpm_watchdog_warning_timeout = + CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT; +static const unsigned int dpm_watchdog_timeout_max = CONFIG_DPM_WATCHDOG_TIMEOUT; + +static int proc_dodpm_watchdog_timeout_secs(const struct ctl_table *table, + int write, void *buffer, + size_t *lenp, loff_t *ppos) +{ + struct ctl_table ctl = *table; + unsigned int val = dpm_watchdog_timeout; + int ret; + + ctl.data = &val; + ret = proc_douintvec_minmax(&ctl, write, buffer, lenp, ppos); + if (ret || !write) + return ret; + + if (val < dpm_watchdog_warning_timeout) + dpm_watchdog_warning_timeout = val; + dpm_watchdog_timeout = val; + + return 0; +} + +static const struct ctl_table dpm_watchdog_sysctls[] = { + { + .procname = "dpm_watchdog_timeout_secs", + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dodpm_watchdog_timeout_secs, + .extra1 = SYSCTL_ONE, + .extra2 = (void *)&dpm_watchdog_timeout_max, + }, + { + .procname = "dpm_watchdog_warning_timeout_secs", + .data = &dpm_watchdog_warning_timeout, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_douintvec_minmax, + .extra1 = SYSCTL_ONE, + .extra2 = (void *)&dpm_watchdog_timeout, + }, +}; + +static int __init dpm_watchdog_sysctl_init(void) +{ + register_sysctl_init("kernel", dpm_watchdog_sysctls); + return 0; +} +subsys_initcall(dpm_watchdog_sysctl_init); + /** * dpm_watchdog_handler - Driver suspend / resume watchdog handler. * @t: The timer that PM watchdog depends on. @@ -552,9 +609,9 @@ static void dpm_watchdog_handler(struct timer_list *t) dev_driver_string(wd->dev), dev_name(wd->dev)); } - time_left = CONFIG_DPM_WATCHDOG_TIMEOUT - CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT; + time_left = dpm_watchdog_timeout - dpm_watchdog_warning_timeout; dev_warn(wd->dev, "**** DPM device timeout after %u seconds; %u seconds until panic ****\n", - CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT, time_left); + dpm_watchdog_warning_timeout, time_left); show_stack(wd->tsk, NULL, KERN_WARNING); wd->fatal = true; @@ -572,11 +629,11 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev) wd->dev = dev; wd->tsk = current; - wd->fatal = CONFIG_DPM_WATCHDOG_TIMEOUT == CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT; + wd->fatal = dpm_watchdog_timeout == dpm_watchdog_warning_timeout; timer_setup_on_stack(timer, dpm_watchdog_handler, 0); /* use same timeout value for both suspend and resume */ - timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT; + timer->expires = jiffies + HZ * dpm_watchdog_warning_timeout; add_timer(timer); } diff --git a/drivers/base/property.c b/drivers/base/property.c index 8e0148a37fff..e08eadd66f4f 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1277,8 +1277,10 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, if (fwnode_ep.port != port) continue; - if (fwnode_ep.id == endpoint) + if (fwnode_ep.id == endpoint) { + fwnode_handle_put(best_ep); return ep; + } if (!endpoint_next) continue; diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 31e30dfced19..51a04961faf7 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -337,7 +337,7 @@ static int regmap_smbus_word_write_reg16(void *context, const void *data, val = ((u8 *)data)[2]; return i2c_smbus_write_word_data(i2c, addr_hi, - cpu_to_le16(((u16)val << 8) | addr_lo)); + ((u16)val << 8) | addr_lo); } static const struct regmap_bus regmap_smbus_byte_word_reg16 = { diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b2b26f07f4e3..e6e022b02637 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -3257,6 +3257,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, *change = false; if (regmap_volatile(map, reg) && map->reg_update_bits) { + if (map->cache_only) + return -EBUSY; + reg = regmap_reg_addr(map, reg); ret = map->reg_update_bits(map->bus_context, reg, mask, val); if (ret == 0 && change) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index a19f8f722bc8..869228a65cb3 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -374,20 +374,28 @@ EXPORT_SYMBOL_GPL(property_entries_free); /* -------------------------------------------------------------------------- */ /* fwnode operations */ -static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) +static struct swnode *swnode_get(struct swnode *swnode) { - struct swnode *swnode = to_swnode(fwnode); - kobject_get(&swnode->kobj); + return swnode; +} + +static void swnode_put(struct swnode *swnode) +{ + kobject_put(&swnode->kobj); +} + +static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) +{ + struct swnode *swnode = swnode_get(to_swnode(fwnode)); + return &swnode->fwnode; } static void software_node_put(struct fwnode_handle *fwnode) { - struct swnode *swnode = to_swnode(fwnode); - - kobject_put(&swnode->kobj); + swnode_put(to_swnode(fwnode)); } static bool software_node_property_present(const struct fwnode_handle *fwnode, @@ -493,7 +501,7 @@ software_node_get_named_child_node(const struct fwnode_handle *fwnode, list_for_each_entry(child, &swnode->children, entry) { if (!strcmp(childname, kobject_name(&child->kobj))) { - kobject_get(&child->kobj); + swnode_get(child); return &child->fwnode; } } @@ -737,7 +745,7 @@ software_node_find_by_name(const struct software_node *parent, const char *name) swnode = kobj_to_swnode(k); if (parent == swnode->node->parent && swnode->node->name && !strcmp(name, swnode->node->name)) { - kobject_get(&swnode->kobj); + swnode_get(swnode); break; } swnode = NULL; @@ -835,13 +843,13 @@ swnode_register(const struct software_node *node, struct swnode *parent, parent ? &parent->kobj : NULL, "node%d", swnode->id); if (ret) { - kobject_put(&swnode->kobj); + swnode_put(swnode); return ERR_PTR(ret); } /* * Assign the flag only in the successful case, so - * the above kobject_put() won't mess up with properties. + * the above swnode_put() won't mess up with properties. */ swnode->allocated = allocated; @@ -978,7 +986,7 @@ void fwnode_remove_software_node(struct fwnode_handle *fwnode) if (!swnode) return; - kobject_put(&swnode->kobj); + swnode_put(swnode); } EXPORT_SYMBOL_GPL(fwnode_remove_software_node); @@ -1002,7 +1010,7 @@ int device_add_software_node(struct device *dev, const struct software_node *nod swnode = software_node_to_swnode(node); if (swnode) { - kobject_get(&swnode->kobj); + swnode_get(swnode); } else { ret = software_node_register(node); if (ret) @@ -1044,7 +1052,7 @@ void device_remove_software_node(struct device *dev) software_node_notify_remove(dev); set_secondary_fwnode(dev, NULL); - kobject_put(&swnode->kobj); + swnode_put(swnode); } EXPORT_SYMBOL_GPL(device_remove_software_node); @@ -1097,7 +1105,7 @@ void software_node_notify(struct device *dev) if (!swnode) return; - kobject_get(&swnode->kobj); + swnode_get(swnode); ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node"); if (ret) return; @@ -1119,11 +1127,11 @@ void software_node_notify_remove(struct device *dev) sysfs_remove_link(&swnode->kobj, dev_name(dev)); sysfs_remove_link(&dev->kobj, "software_node"); - kobject_put(&swnode->kobj); + swnode_put(swnode); if (swnode->managed) { set_secondary_fwnode(dev, NULL); - kobject_put(&swnode->kobj); + swnode_put(swnode); } } diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile index 187eaf81f0f8..5faaa8a8e7f0 100644 --- a/drivers/block/drbd/Makefile +++ b/drivers/block/drbd/Makefile @@ -3,6 +3,7 @@ drbd-y := drbd_buildtag.o drbd_bitmap.o drbd_proc.o drbd-y += drbd_worker.o drbd_receiver.o drbd_req.o drbd_actlog.o drbd-y += drbd_main.o drbd_strings.o drbd_nl.o drbd-y += drbd_interval.o drbd_state.o +drbd-y += drbd_nl_gen.o drbd-$(CONFIG_DEBUG_FS) += drbd_debugfs.o obj-$(CONFIG_BLK_DEV_DRBD) += drbd.o diff --git a/drivers/block/drbd/drbd_buildtag.c b/drivers/block/drbd/drbd_buildtag.c index cb1aa66d7d5d..cd0389488f63 100644 --- a/drivers/block/drbd/drbd_buildtag.c +++ b/drivers/block/drbd/drbd_buildtag.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -#include <linux/drbd_config.h> +#include "drbd_config.h" #include <linux/module.h> const char *drbd_buildtag(void) diff --git a/drivers/block/drbd/drbd_config.h b/drivers/block/drbd/drbd_config.h new file mode 100644 index 000000000000..d215365c6bb1 --- /dev/null +++ b/drivers/block/drbd/drbd_config.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drbd_config.h + * DRBD's compile time configuration. + */ + +#ifndef DRBD_CONFIG_H +#define DRBD_CONFIG_H + +extern const char *drbd_buildtag(void); + +#define REL_VERSION "8.4.11" +#define PRO_VERSION_MIN 86 +#define PRO_VERSION_MAX 101 + +#endif diff --git a/drivers/block/drbd/drbd_debugfs.c b/drivers/block/drbd/drbd_debugfs.c index 12460b584bcb..371abcd7e880 100644 --- a/drivers/block/drbd/drbd_debugfs.c +++ b/drivers/block/drbd/drbd_debugfs.c @@ -844,7 +844,7 @@ static int drbd_version_show(struct seq_file *m, void *ignored) { seq_printf(m, "# %s\n", drbd_buildtag()); seq_printf(m, "VERSION=%s\n", REL_VERSION); - seq_printf(m, "API_VERSION=%u\n", GENL_MAGIC_VERSION); + seq_printf(m, "API_VERSION=%u\n", DRBD_FAMILY_VERSION); seq_printf(m, "PRO_VERSION_MIN=%u\n", PRO_VERSION_MIN); seq_printf(m, "PRO_VERSION_MAX=%u\n", PRO_VERSION_MAX); return 0; diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index f6d6276974ee..48b45c3142f7 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -32,14 +32,16 @@ #include <net/tcp.h> #include <linux/lru_cache.h> #include <linux/prefetch.h> -#include <linux/drbd_genl_api.h> #include <linux/drbd.h> -#include <linux/drbd_config.h> +#include "drbd_config.h" +#include "drbd_nl_gen.h" #include "drbd_strings.h" #include "drbd_state.h" #include "drbd_protocol.h" #include "drbd_polymorph_printk.h" +extern struct genl_family drbd_nl_family; + /* shared module parameters, defined in drbd_main.c */ #ifdef CONFIG_DRBD_FAULT_INJECTION extern int drbd_enable_faults; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index b1a721dd0496..a2a841c89201 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2324,7 +2324,7 @@ static void drbd_cleanup(void) if (retry.wq) destroy_workqueue(retry.wq); - drbd_genl_unregister(); + genl_unregister_family(&drbd_nl_family); idr_for_each_entry(&drbd_devices, device, i) drbd_delete_device(device); @@ -2846,7 +2846,7 @@ static int __init drbd_init(void) mutex_init(&resources_mutex); INIT_LIST_HEAD(&drbd_resources); - err = drbd_genl_register(); + err = genl_register_family(&drbd_nl_family); if (err) { pr_err("unable to register generic netlink family\n"); goto fail; @@ -2876,7 +2876,7 @@ static int __init drbd_init(void) pr_info("initialized. " "Version: " REL_VERSION " (api:%d/proto:%d-%d)\n", - GENL_MAGIC_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX); + DRBD_FAMILY_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX); pr_info("%s\n", drbd_buildtag()); pr_info("registered as block device major %d\n", DRBD_MAJOR); return 0; /* Success! */ diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index c2ac555473e7..f9ffcd67607b 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -31,59 +31,13 @@ #include <net/genetlink.h> -/* .doit */ -// int drbd_adm_create_resource(struct sk_buff *skb, struct genl_info *info); -// int drbd_adm_delete_resource(struct sk_buff *skb, struct genl_info *info); - -int drbd_adm_new_minor(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_del_minor(struct sk_buff *skb, struct genl_info *info); - -int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_down(struct sk_buff *skb, struct genl_info *info); - -int drbd_adm_set_role(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_detach(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_resize(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_start_ov(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_new_c_uuid(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_pause_sync(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_resume_sync(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_suspend_io(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_resume_io(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_resource_opts(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_get_status(struct sk_buff *skb, struct genl_info *info); -int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info); -/* .dumpit */ -int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb); -int drbd_adm_dump_resources(struct sk_buff *skb, struct netlink_callback *cb); -int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb); -int drbd_adm_dump_devices_done(struct netlink_callback *cb); -int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb); -int drbd_adm_dump_connections_done(struct netlink_callback *cb); -int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb); -int drbd_adm_dump_peer_devices_done(struct netlink_callback *cb); -int drbd_adm_get_initial_state(struct sk_buff *skb, struct netlink_callback *cb); - -#include <linux/drbd_genl_api.h> - -static int drbd_pre_doit(const struct genl_split_ops *ops, - struct sk_buff *skb, struct genl_info *info); -static void drbd_post_doit(const struct genl_split_ops *ops, - struct sk_buff *skb, struct genl_info *info); - -#define GENL_MAGIC_FAMILY_PRE_DOIT drbd_pre_doit -#define GENL_MAGIC_FAMILY_POST_DOIT drbd_post_doit - -#include <linux/genl_magic_func.h> +#include "drbd_nl_gen.h" + +static int drbd_genl_multicast_events(struct sk_buff *skb, gfp_t flags) +{ + return genlmsg_multicast(&drbd_nl_family, skb, 0, + DRBD_NLGRP_EVENTS, flags); +} static atomic_t drbd_genl_seq = ATOMIC_INIT(2); /* two. */ static atomic_t notify_genl_seq = ATOMIC_INIT(2); /* two. */ @@ -114,7 +68,7 @@ static int drbd_msg_put_info(struct sk_buff *skb, const char *info) if (!nla) return err; - err = nla_put_string(skb, T_info_text, info); + err = nla_put_string(skb, DRBD_A_DRBD_CFG_REPLY_INFO_TEXT, info); if (err) { nla_nest_cancel(skb, nla); return err; @@ -135,7 +89,7 @@ static int drbd_msg_sprintf_info(struct sk_buff *skb, const char *fmt, ...) if (!nla) return err; - txt = nla_reserve(skb, T_info_text, 256); + txt = nla_reserve(skb, DRBD_A_DRBD_CFG_REPLY_INFO_TEXT, 256); if (!txt) { nla_nest_cancel(skb, nla); return err; @@ -187,6 +141,15 @@ static const unsigned int drbd_genl_cmd_flags[] = { [DRBD_ADM_DOWN] = DRBD_ADM_NEED_RESOURCE, }; +/* Detect attempts to change invariant attributes in a _change_ handler. */ +#define has_invariant(ntb, attr) \ +({ \ + bool __found = !!(ntb)[attr]; \ + if (__found) \ + pr_info("must not change invariant attr: %s\n", #attr); \ + __found; \ +}) + /* * At this point, we still rely on the global genl_lock(). * If we want to avoid that, and allow "genl_family.parallel_ops", we may need @@ -210,7 +173,7 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx, } adm_ctx->reply_dh = genlmsg_put_reply(adm_ctx->reply_skb, - info, &drbd_genl_family, 0, cmd); + info, &drbd_nl_family, 0, cmd); /* put of a few bytes into a fresh skb of >= 4k will always succeed. * but anyways */ if (!adm_ctx->reply_dh) { @@ -223,9 +186,11 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx, adm_ctx->volume = VOLUME_UNSPECIFIED; if (info->attrs[DRBD_NLA_CFG_CONTEXT]) { + struct nlattr **ntb; struct nlattr *nla; - /* parse and validate only */ - err = drbd_cfg_context_from_attrs(NULL, info); + + /* parse and validate, get nested attribute table */ + err = drbd_cfg_context_ntb_from_attrs(&ntb, info); if (err) goto fail; @@ -234,18 +199,21 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx, err = nla_put_nohdr(adm_ctx->reply_skb, info->attrs[DRBD_NLA_CFG_CONTEXT]->nla_len, info->attrs[DRBD_NLA_CFG_CONTEXT]); - if (err) + if (err) { + kfree(ntb); goto fail; + } /* and assign stuff to the adm_ctx */ - nla = nested_attr_tb[T_ctx_volume]; + nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME]; if (nla) adm_ctx->volume = nla_get_u32(nla); - nla = nested_attr_tb[T_ctx_resource_name]; + nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME]; if (nla) adm_ctx->resource_name = nla_data(nla); - adm_ctx->my_addr = nested_attr_tb[T_ctx_my_addr]; - adm_ctx->peer_addr = nested_attr_tb[T_ctx_peer_addr]; + adm_ctx->my_addr = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR]; + adm_ctx->peer_addr = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR]; + kfree(ntb); if ((adm_ctx->my_addr && nla_len(adm_ctx->my_addr) > sizeof(adm_ctx->connection->my_addr)) || (adm_ctx->peer_addr && @@ -259,7 +227,7 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx, adm_ctx->device = minor_to_device(d_in->minor); /* We are protected by the global genl_lock(). - * But we may explicitly drop it/retake it in drbd_adm_set_role(), + * But we may explicitly drop it/retake it in drbd_nl_set_role(), * so make sure this object stays around. */ if (adm_ctx->device) kref_get(&adm_ctx->device->kref); @@ -334,8 +302,8 @@ fail: return err; } -static int drbd_pre_doit(const struct genl_split_ops *ops, - struct sk_buff *skb, struct genl_info *info) +int drbd_pre_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx; u8 cmd = info->genlhdr->cmd; @@ -362,8 +330,8 @@ static int drbd_pre_doit(const struct genl_split_ops *ops, return 0; } -static void drbd_post_doit(const struct genl_split_ops *ops, - struct sk_buff *skb, struct genl_info *info) +void drbd_post_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; @@ -828,7 +796,7 @@ static const char *from_attrs_err_to_txt(int err) "invalid attribute value"; } -int drbd_adm_set_role(struct sk_buff *skb, struct genl_info *info) +static int drbd_nl_set_role(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct set_role_parms parms; @@ -868,6 +836,16 @@ out: return 0; } +int drbd_nl_primary_doit(struct sk_buff *skb, struct genl_info *info) +{ + return drbd_nl_set_role(skb, info); +} + +int drbd_nl_secondary_doit(struct sk_buff *skb, struct genl_info *info) +{ + return drbd_nl_set_role(skb, info); +} + /* Initializes the md.*_offset members, so we are able to find * the on disk meta data. * @@ -962,7 +940,7 @@ char *ppsize(char *buf, unsigned long long size) * peer may not initiate a resize. */ /* Note these are not to be confused with - * drbd_adm_suspend_io/drbd_adm_resume_io, + * drbd_nl_suspend_io_doit/drbd_nl_resume_io_doit, * which are (sub) state changes triggered by admin (drbdsetup), * and can be long lived. * This changes an device->flag, is triggered by drbd internals, @@ -1574,13 +1552,14 @@ out: return err; } -int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_chg_disk_opts_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; struct drbd_device *device; struct disk_conf *new_disk_conf, *old_disk_conf; struct fifo_buffer *old_plan = NULL, *new_plan = NULL; + struct nlattr **ntb; int err; unsigned int fifo_size; @@ -1612,13 +1591,29 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info) if (should_set_defaults(info)) set_disk_conf_defaults(new_disk_conf); - err = disk_conf_from_attrs_for_change(new_disk_conf, info); + err = disk_conf_from_attrs(new_disk_conf, info); if (err && err != -ENOMSG) { retcode = ERR_MANDATORY_TAG; drbd_msg_put_info(adm_ctx->reply_skb, from_attrs_err_to_txt(err)); goto fail_unlock; } + err = disk_conf_ntb_from_attrs(&ntb, info); + if (!err) { + if (has_invariant(ntb, DRBD_A_DISK_CONF_BACKING_DEV) || + has_invariant(ntb, DRBD_A_DISK_CONF_META_DEV) || + has_invariant(ntb, DRBD_A_DISK_CONF_META_DEV_IDX) || + has_invariant(ntb, DRBD_A_DISK_CONF_DISK_SIZE) || + has_invariant(ntb, DRBD_A_DISK_CONF_MAX_BIO_BVECS)) { + retcode = ERR_MANDATORY_TAG; + drbd_msg_put_info(adm_ctx->reply_skb, + "cannot change invariant setting"); + kfree(ntb); + goto fail_unlock; + } + kfree(ntb); + } + if (!expect(device, new_disk_conf->resync_rate >= 1)) new_disk_conf->resync_rate = 1; @@ -1796,7 +1791,7 @@ void drbd_backing_dev_free(struct drbd_device *device, struct drbd_backing_dev * kfree(ldev); } -int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_attach_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_device *device; @@ -2236,7 +2231,7 @@ static int adm_detach(struct drbd_device *device, int force) * Then we transition to D_DISKLESS, and wait for put_ldev() to return all * internal references as well. * Only then we have finally detached. */ -int drbd_adm_detach(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_detach_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; @@ -2434,12 +2429,13 @@ static void free_crypto(struct crypto *crypto) crypto_free_shash(crypto->verify_tfm); } -int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_chg_net_opts_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; struct drbd_connection *connection; struct net_conf *old_net_conf, *new_net_conf = NULL; + struct nlattr **ntb; int err; int ovr; /* online verify running */ int rsr; /* re-sync running */ @@ -2476,13 +2472,26 @@ int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info) if (should_set_defaults(info)) set_net_conf_defaults(new_net_conf); - err = net_conf_from_attrs_for_change(new_net_conf, info); + err = net_conf_from_attrs(new_net_conf, info); if (err && err != -ENOMSG) { retcode = ERR_MANDATORY_TAG; drbd_msg_put_info(adm_ctx->reply_skb, from_attrs_err_to_txt(err)); goto fail; } + err = net_conf_ntb_from_attrs(&ntb, info); + if (!err) { + if (has_invariant(ntb, DRBD_A_NET_CONF_DISCARD_MY_DATA) || + has_invariant(ntb, DRBD_A_NET_CONF_TENTATIVE)) { + retcode = ERR_MANDATORY_TAG; + drbd_msg_put_info(adm_ctx->reply_skb, + "cannot change invariant setting"); + kfree(ntb); + goto fail; + } + kfree(ntb); + } + retcode = check_net_options(connection, new_net_conf); if (retcode != NO_ERROR) goto fail; @@ -2575,7 +2584,7 @@ static void peer_device_to_info(struct peer_device_info *info, info->peer_resync_susp_dependency = device->state.aftr_isp; } -int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_connect_doit(struct sk_buff *skb, struct genl_info *info) { struct connection_info connection_info; enum drbd_notification_type flags; @@ -2790,7 +2799,7 @@ repeat: return rv; } -int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_disconnect_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct disconnect_parms parms; @@ -2845,7 +2854,7 @@ void resync_after_online_grow(struct drbd_device *device) _drbd_request_state(device, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE + CS_SERIALIZE); } -int drbd_adm_resize(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_resize_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct disk_conf *old_disk_conf, *new_disk_conf = NULL; @@ -2981,7 +2990,7 @@ int drbd_adm_resize(struct sk_buff *skb, struct genl_info *info) goto fail; } -int drbd_adm_resource_opts(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_resource_opts_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; @@ -3019,7 +3028,7 @@ fail: return 0; } -int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_invalidate_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_device *device; @@ -3097,7 +3106,7 @@ static int drbd_bmio_set_susp_al(struct drbd_device *device, return rv; } -int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_inval_peer_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; int retcode; /* drbd_ret_code, drbd_state_rv */ @@ -3148,7 +3157,7 @@ out: return 0; } -int drbd_adm_pause_sync(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_pause_sync_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; @@ -3168,7 +3177,7 @@ out: return 0; } -int drbd_adm_resume_sync(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_resume_sync_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; union drbd_dev_state s; @@ -3196,12 +3205,12 @@ out: return 0; } -int drbd_adm_suspend_io(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_suspend_io_doit(struct sk_buff *skb, struct genl_info *info) { return drbd_adm_simple_request_state(skb, info, NS(susp, 1)); } -int drbd_adm_resume_io(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_resume_io_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_device *device; @@ -3257,7 +3266,7 @@ out: return 0; } -int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_outdate_doit(struct sk_buff *skb, struct genl_info *info) { return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED)); } @@ -3272,16 +3281,20 @@ static int nla_put_drbd_cfg_context(struct sk_buff *skb, if (!nla) goto nla_put_failure; if (device && - nla_put_u32(skb, T_ctx_volume, device->vnr)) + nla_put_u32(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME, device->vnr)) goto nla_put_failure; - if (nla_put_string(skb, T_ctx_resource_name, resource->name)) + if (nla_put_string(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME, resource->name)) goto nla_put_failure; if (connection) { if (connection->my_addr_len && - nla_put(skb, T_ctx_my_addr, connection->my_addr_len, &connection->my_addr)) + nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR, + connection->my_addr_len, + &connection->my_addr)) goto nla_put_failure; if (connection->peer_addr_len && - nla_put(skb, T_ctx_peer_addr, connection->peer_addr_len, &connection->peer_addr)) + nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR, + connection->peer_addr_len, + &connection->peer_addr)) goto nla_put_failure; } nla_nest_end(skb, nla); @@ -3300,7 +3313,7 @@ nla_put_failure: */ static struct nlattr *find_cfg_context_attr(const struct nlmsghdr *nlh, int attr) { - const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ; + const unsigned int hdrlen = GENL_HDRLEN + sizeof(struct drbd_genlmsghdr); struct nlattr *nla; nla = nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), @@ -3312,7 +3325,7 @@ static struct nlattr *find_cfg_context_attr(const struct nlmsghdr *nlh, int attr static void resource_to_info(struct resource_info *, struct drbd_resource *); -int drbd_adm_dump_resources(struct sk_buff *skb, struct netlink_callback *cb) +int drbd_nl_get_resources_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct drbd_genlmsghdr *dh; struct drbd_resource *resource; @@ -3340,7 +3353,7 @@ found_resource: put_result: dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, &drbd_genl_family, + cb->nlh->nlmsg_seq, &drbd_nl_family, NLM_F_MULTI, DRBD_ADM_GET_RESOURCES); err = -ENOMEM; if (!dh) @@ -3350,15 +3363,15 @@ put_result: err = nla_put_drbd_cfg_context(skb, resource, NULL, NULL); if (err) goto out; - err = res_opts_to_skb(skb, &resource->res_opts, !capable(CAP_SYS_ADMIN)); + err = res_opts_to_skb(skb, &resource->res_opts); if (err) goto out; resource_to_info(&resource_info, resource); - err = resource_info_to_skb(skb, &resource_info, !capable(CAP_SYS_ADMIN)); + err = resource_info_to_skb(skb, &resource_info); if (err) goto out; resource_statistics.res_stat_write_ordering = resource->write_ordering; - err = resource_statistics_to_skb(skb, &resource_statistics, !capable(CAP_SYS_ADMIN)); + err = resource_statistics_to_skb(skb, &resource_statistics); if (err) goto out; cb->args[0] = (long)resource; @@ -3423,7 +3436,7 @@ int drbd_adm_dump_devices_done(struct netlink_callback *cb) { static void device_to_info(struct device_info *, struct drbd_device *); -int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb) +int drbd_nl_get_devices_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct nlattr *resource_filter; struct drbd_resource *resource; @@ -3436,7 +3449,8 @@ int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb) resource = (struct drbd_resource *)cb->args[0]; if (!cb->args[0] && !cb->args[1]) { - resource_filter = find_cfg_context_attr(cb->nlh, T_ctx_resource_name); + resource_filter = find_cfg_context_attr(cb->nlh, + DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME); if (resource_filter) { retcode = ERR_RES_NOT_KNOWN; resource = drbd_find_resource(nla_data(resource_filter)); @@ -3465,7 +3479,7 @@ int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb) put_result: dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, &drbd_genl_family, + cb->nlh->nlmsg_seq, &drbd_nl_family, NLM_F_MULTI, DRBD_ADM_GET_DEVICES); err = -ENOMEM; if (!dh) @@ -3481,18 +3495,18 @@ put_result: struct disk_conf *disk_conf = rcu_dereference(device->ldev->disk_conf); - err = disk_conf_to_skb(skb, disk_conf, !capable(CAP_SYS_ADMIN)); + err = disk_conf_to_skb(skb, disk_conf); put_ldev(device); if (err) goto out; } device_to_info(&device_info, device); - err = device_info_to_skb(skb, &device_info, !capable(CAP_SYS_ADMIN)); + err = device_info_to_skb(skb, &device_info); if (err) goto out; device_to_statistics(&device_statistics, device); - err = device_statistics_to_skb(skb, &device_statistics, !capable(CAP_SYS_ADMIN)); + err = device_statistics_to_skb(skb, &device_statistics); if (err) goto out; cb->args[1] = minor + 1; @@ -3514,7 +3528,7 @@ int drbd_adm_dump_connections_done(struct netlink_callback *cb) enum { SINGLE_RESOURCE, ITERATE_RESOURCES }; -int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb) +int drbd_nl_get_connections_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct nlattr *resource_filter; struct drbd_resource *resource = NULL, *next_resource; @@ -3527,7 +3541,8 @@ int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); resource = (struct drbd_resource *)cb->args[0]; if (!cb->args[0]) { - resource_filter = find_cfg_context_attr(cb->nlh, T_ctx_resource_name); + resource_filter = find_cfg_context_attr(cb->nlh, + DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME); if (resource_filter) { retcode = ERR_RES_NOT_KNOWN; resource = drbd_find_resource(nla_data(resource_filter)); @@ -3591,7 +3606,7 @@ found_resource: put_result: dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, &drbd_genl_family, + cb->nlh->nlmsg_seq, &drbd_nl_family, NLM_F_MULTI, DRBD_ADM_GET_CONNECTIONS); err = -ENOMEM; if (!dh) @@ -3606,16 +3621,16 @@ put_result: goto out; net_conf = rcu_dereference(connection->net_conf); if (net_conf) { - err = net_conf_to_skb(skb, net_conf, !capable(CAP_SYS_ADMIN)); + err = net_conf_to_skb(skb, net_conf); if (err) goto out; } connection_to_info(&connection_info, connection); - err = connection_info_to_skb(skb, &connection_info, !capable(CAP_SYS_ADMIN)); + err = connection_info_to_skb(skb, &connection_info); if (err) goto out; connection_statistics.conn_congested = test_bit(NET_CONGESTED, &connection->flags); - err = connection_statistics_to_skb(skb, &connection_statistics, !capable(CAP_SYS_ADMIN)); + err = connection_statistics_to_skb(skb, &connection_statistics); if (err) goto out; cb->args[2] = (long)connection; @@ -3676,7 +3691,7 @@ int drbd_adm_dump_peer_devices_done(struct netlink_callback *cb) return put_resource_in_arg0(cb, 9); } -int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb) +int drbd_nl_get_peer_devices_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct nlattr *resource_filter; struct drbd_resource *resource; @@ -3688,7 +3703,8 @@ int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb) resource = (struct drbd_resource *)cb->args[0]; if (!cb->args[0] && !cb->args[1]) { - resource_filter = find_cfg_context_attr(cb->nlh, T_ctx_resource_name); + resource_filter = find_cfg_context_attr(cb->nlh, + DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME); if (resource_filter) { retcode = ERR_RES_NOT_KNOWN; resource = drbd_find_resource(nla_data(resource_filter)); @@ -3735,7 +3751,7 @@ found_peer_device: put_result: dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, &drbd_genl_family, + cb->nlh->nlmsg_seq, &drbd_nl_family, NLM_F_MULTI, DRBD_ADM_GET_PEER_DEVICES); err = -ENOMEM; if (!dh) @@ -3751,11 +3767,11 @@ put_result: if (err) goto out; peer_device_to_info(&peer_device_info, peer_device); - err = peer_device_info_to_skb(skb, &peer_device_info, !capable(CAP_SYS_ADMIN)); + err = peer_device_info_to_skb(skb, &peer_device_info); if (err) goto out; peer_device_to_statistics(&peer_device_statistics, peer_device); - err = peer_device_statistics_to_skb(skb, &peer_device_statistics, !capable(CAP_SYS_ADMIN)); + err = peer_device_statistics_to_skb(skb, &peer_device_statistics); if (err) goto out; cb->args[1] = minor; @@ -3795,11 +3811,11 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device, /* If sib != NULL, this is drbd_bcast_event, which anyone can listen * to. So we better exclude_sensitive information. * - * If sib == NULL, this is drbd_adm_get_status, executed synchronously + * If sib == NULL, this is drbd_nl_get_status_doit, executed synchronously * in the context of the requesting user process. Exclude sensitive * information, unless current has superuser. * - * NOTE: for drbd_adm_get_status_all(), this is a netlink dump, and + * NOTE: for drbd_nl_get_status_dumpit(), this is a netlink dump, and * relies on the current implementation of netlink_dump(), which * executes the dump callback successively from netlink_recvmsg(), * always in the context of the receiving process */ @@ -3812,7 +3828,7 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device, if (nla_put_drbd_cfg_context(skb, resource, the_only_connection(resource), device)) goto nla_put_failure; - if (res_opts_to_skb(skb, &device->resource->res_opts, exclude_sensitive)) + if (res_opts_to_skb(skb, &device->resource->res_opts)) goto nla_put_failure; rcu_read_lock(); @@ -3820,14 +3836,24 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device, struct disk_conf *disk_conf; disk_conf = rcu_dereference(device->ldev->disk_conf); - err = disk_conf_to_skb(skb, disk_conf, exclude_sensitive); + err = disk_conf_to_skb(skb, disk_conf); } if (!err) { struct net_conf *nc; nc = rcu_dereference(first_peer_device(device)->connection->net_conf); - if (nc) - err = net_conf_to_skb(skb, nc, exclude_sensitive); + if (nc) { + if (exclude_sensitive) { + struct net_conf nc_clean = *nc; + + memset(nc_clean.shared_secret, 0, + sizeof(nc_clean.shared_secret)); + nc_clean.shared_secret_len = 0; + err = net_conf_to_skb(skb, &nc_clean); + } else { + err = net_conf_to_skb(skb, nc); + } + } } rcu_read_unlock(); if (err) @@ -3836,42 +3862,57 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device, nla = nla_nest_start_noflag(skb, DRBD_NLA_STATE_INFO); if (!nla) goto nla_put_failure; - if (nla_put_u32(skb, T_sib_reason, sib ? sib->sib_reason : SIB_GET_STATUS_REPLY) || - nla_put_u32(skb, T_current_state, device->state.i) || - nla_put_u64_0pad(skb, T_ed_uuid, device->ed_uuid) || - nla_put_u64_0pad(skb, T_capacity, get_capacity(device->vdisk)) || - nla_put_u64_0pad(skb, T_send_cnt, device->send_cnt) || - nla_put_u64_0pad(skb, T_recv_cnt, device->recv_cnt) || - nla_put_u64_0pad(skb, T_read_cnt, device->read_cnt) || - nla_put_u64_0pad(skb, T_writ_cnt, device->writ_cnt) || - nla_put_u64_0pad(skb, T_al_writ_cnt, device->al_writ_cnt) || - nla_put_u64_0pad(skb, T_bm_writ_cnt, device->bm_writ_cnt) || - nla_put_u32(skb, T_ap_bio_cnt, atomic_read(&device->ap_bio_cnt)) || - nla_put_u32(skb, T_ap_pending_cnt, atomic_read(&device->ap_pending_cnt)) || - nla_put_u32(skb, T_rs_pending_cnt, atomic_read(&device->rs_pending_cnt))) + if (nla_put_u32(skb, DRBD_A_STATE_INFO_SIB_REASON, + sib ? sib->sib_reason : SIB_GET_STATUS_REPLY) || + nla_put_u32(skb, DRBD_A_STATE_INFO_CURRENT_STATE, + device->state.i) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_ED_UUID, + device->ed_uuid, 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_CAPACITY, + get_capacity(device->vdisk), 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_SEND_CNT, + device->send_cnt, 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_RECV_CNT, + device->recv_cnt, 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_READ_CNT, + device->read_cnt, 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_WRIT_CNT, + device->writ_cnt, 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_AL_WRIT_CNT, + device->al_writ_cnt, 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BM_WRIT_CNT, + device->bm_writ_cnt, 0) || + nla_put_u32(skb, DRBD_A_STATE_INFO_AP_BIO_CNT, + atomic_read(&device->ap_bio_cnt)) || + nla_put_u32(skb, DRBD_A_STATE_INFO_AP_PENDING_CNT, + atomic_read(&device->ap_pending_cnt)) || + nla_put_u32(skb, DRBD_A_STATE_INFO_RS_PENDING_CNT, + atomic_read(&device->rs_pending_cnt))) goto nla_put_failure; if (got_ldev) { int err; spin_lock_irq(&device->ldev->md.uuid_lock); - err = nla_put(skb, T_uuids, sizeof(si->uuids), device->ldev->md.uuid); + err = nla_put(skb, DRBD_A_STATE_INFO_UUIDS, + sizeof(si->uuids), + device->ldev->md.uuid); spin_unlock_irq(&device->ldev->md.uuid_lock); if (err) goto nla_put_failure; - if (nla_put_u32(skb, T_disk_flags, device->ldev->md.flags) || - nla_put_u64_0pad(skb, T_bits_total, drbd_bm_bits(device)) || - nla_put_u64_0pad(skb, T_bits_oos, - drbd_bm_total_weight(device))) + if (nla_put_u32(skb, DRBD_A_STATE_INFO_DISK_FLAGS, device->ldev->md.flags) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_TOTAL, drbd_bm_bits(device), 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_OOS, + drbd_bm_total_weight(device), 0)) goto nla_put_failure; if (C_SYNC_SOURCE <= device->state.conn && C_PAUSED_SYNC_T >= device->state.conn) { - if (nla_put_u64_0pad(skb, T_bits_rs_total, - device->rs_total) || - nla_put_u64_0pad(skb, T_bits_rs_failed, - device->rs_failed)) + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_TOTAL, + device->rs_total, 0) || + nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_FAILED, + device->rs_failed, 0)) goto nla_put_failure; } } @@ -3882,17 +3923,17 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device, case SIB_GET_STATUS_REPLY: break; case SIB_STATE_CHANGE: - if (nla_put_u32(skb, T_prev_state, sib->os.i) || - nla_put_u32(skb, T_new_state, sib->ns.i)) + if (nla_put_u32(skb, DRBD_A_STATE_INFO_PREV_STATE, sib->os.i) || + nla_put_u32(skb, DRBD_A_STATE_INFO_NEW_STATE, sib->ns.i)) goto nla_put_failure; break; case SIB_HELPER_POST: - if (nla_put_u32(skb, T_helper_exit_code, + if (nla_put_u32(skb, DRBD_A_STATE_INFO_HELPER_EXIT_CODE, sib->helper_exit_code)) goto nla_put_failure; fallthrough; case SIB_HELPER_PRE: - if (nla_put_string(skb, T_helper, sib->helper_name)) + if (nla_put_string(skb, DRBD_A_STATE_INFO_HELPER, sib->helper_name)) goto nla_put_failure; break; } @@ -3907,7 +3948,7 @@ nla_put_failure: return err; } -int drbd_adm_get_status(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_get_status_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; @@ -3997,7 +4038,7 @@ next_resource: } dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, &drbd_genl_family, + cb->nlh->nlmsg_seq, &drbd_nl_family, NLM_F_MULTI, DRBD_ADM_GET_STATUS); if (!dh) goto out; @@ -4017,7 +4058,7 @@ next_resource: struct net_conf *nc; nc = rcu_dereference(connection->net_conf); - if (nc && net_conf_to_skb(skb, nc, 1) != 0) + if (nc && net_conf_to_skb(skb, nc) != 0) goto cancel; } goto done; @@ -4059,9 +4100,9 @@ out: * * Once things are setup properly, we call into get_one_status(). */ -int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb) +int drbd_nl_get_status_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { - const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ; + const unsigned int hdrlen = GENL_HDRLEN + sizeof(struct drbd_genlmsghdr); struct nlattr *nla; const char *resource_name; struct drbd_resource *resource; @@ -4084,7 +4125,7 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb) /* No explicit context given. Dump all. */ if (!nla) goto dump; - nla = nla_find_nested(nla, T_ctx_resource_name); + nla = nla_find_nested(nla, DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME); /* context given, but no name present? */ if (!nla) return -EINVAL; @@ -4107,7 +4148,7 @@ dump: return get_one_status(skb, cb); } -int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_get_timeout_type_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; @@ -4125,7 +4166,7 @@ int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info) test_bit(USE_DEGR_WFC_T, &adm_ctx->device->flags) ? UT_DEGRADED : UT_DEFAULT; - err = timeout_parms_to_priv_skb(adm_ctx->reply_skb, &tp); + err = timeout_parms_to_skb(adm_ctx->reply_skb, &tp); if (err) { nlmsg_free(adm_ctx->reply_skb); adm_ctx->reply_skb = NULL; @@ -4136,7 +4177,7 @@ out: return 0; } -int drbd_adm_start_ov(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_start_ov_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_device *device; @@ -4182,7 +4223,7 @@ out: } -int drbd_adm_new_c_uuid(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_new_c_uuid_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_device *device; @@ -4285,7 +4326,7 @@ static void resource_to_info(struct resource_info *info, info->res_susp_fen = resource->susp_fen; } -int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_new_resource_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_connection *connection; struct drbd_config_context *adm_ctx = info->user_ptr[0]; @@ -4348,7 +4389,7 @@ static void device_to_info(struct device_info *info, } -int drbd_adm_new_minor(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_new_minor_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_genlmsghdr *dh = genl_info_userhdr(info); @@ -4455,7 +4496,7 @@ static enum drbd_ret_code adm_del_minor(struct drbd_device *device) return ERR_MINOR_CONFIGURED; } -int drbd_adm_del_minor(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_del_minor_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; enum drbd_ret_code retcode; @@ -4504,7 +4545,7 @@ static int adm_del_resource(struct drbd_resource *resource) return NO_ERROR; } -int drbd_adm_down(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_down_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_resource *resource; @@ -4567,7 +4608,7 @@ finish: return 0; } -int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info) +int drbd_nl_del_resource_doit(struct sk_buff *skb, struct genl_info *info) { struct drbd_config_context *adm_ctx = info->user_ptr[0]; struct drbd_resource *resource; @@ -4601,7 +4642,7 @@ void drbd_bcast_event(struct drbd_device *device, const struct sib_info *sib) goto failed; err = -EMSGSIZE; - d_out = genlmsg_put(msg, 0, seq, &drbd_genl_family, 0, DRBD_EVENT); + d_out = genlmsg_put(msg, 0, seq, &drbd_nl_family, 0, DRBD_ADM_EVENT); if (!d_out) /* cannot happen, but anyways. */ goto nla_put_failure; d_out->minor = device_to_minor(device); @@ -4632,7 +4673,7 @@ static int nla_put_notification_header(struct sk_buff *msg, .nh_type = type, }; - return drbd_notification_header_to_skb(msg, &nh, true); + return drbd_notification_header_to_skb(msg, &nh); } int notify_resource_state(struct sk_buff *skb, @@ -4656,7 +4697,7 @@ int notify_resource_state(struct sk_buff *skb, } err = -EMSGSIZE; - dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_RESOURCE_STATE); + dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_RESOURCE_STATE); if (!dh) goto nla_put_failure; dh->minor = -1U; @@ -4664,10 +4705,10 @@ int notify_resource_state(struct sk_buff *skb, if (nla_put_drbd_cfg_context(skb, resource, NULL, NULL) || nla_put_notification_header(skb, type) || ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY && - resource_info_to_skb(skb, resource_info, true))) + resource_info_to_skb(skb, resource_info))) goto nla_put_failure; resource_statistics.res_stat_write_ordering = resource->write_ordering; - err = resource_statistics_to_skb(skb, &resource_statistics, !capable(CAP_SYS_ADMIN)); + err = resource_statistics_to_skb(skb, &resource_statistics); if (err) goto nla_put_failure; genlmsg_end(skb, dh); @@ -4708,7 +4749,7 @@ int notify_device_state(struct sk_buff *skb, } err = -EMSGSIZE; - dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_DEVICE_STATE); + dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_DEVICE_STATE); if (!dh) goto nla_put_failure; dh->minor = device->minor; @@ -4716,10 +4757,10 @@ int notify_device_state(struct sk_buff *skb, if (nla_put_drbd_cfg_context(skb, device->resource, NULL, device) || nla_put_notification_header(skb, type) || ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY && - device_info_to_skb(skb, device_info, true))) + device_info_to_skb(skb, device_info))) goto nla_put_failure; device_to_statistics(&device_statistics, device); - device_statistics_to_skb(skb, &device_statistics, !capable(CAP_SYS_ADMIN)); + device_statistics_to_skb(skb, &device_statistics); genlmsg_end(skb, dh); if (multicast) { err = drbd_genl_multicast_events(skb, GFP_NOWAIT); @@ -4758,7 +4799,7 @@ int notify_connection_state(struct sk_buff *skb, } err = -EMSGSIZE; - dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_CONNECTION_STATE); + dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_CONNECTION_STATE); if (!dh) goto nla_put_failure; dh->minor = -1U; @@ -4766,10 +4807,10 @@ int notify_connection_state(struct sk_buff *skb, if (nla_put_drbd_cfg_context(skb, connection->resource, connection, NULL) || nla_put_notification_header(skb, type) || ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY && - connection_info_to_skb(skb, connection_info, true))) + connection_info_to_skb(skb, connection_info))) goto nla_put_failure; connection_statistics.conn_congested = test_bit(NET_CONGESTED, &connection->flags); - connection_statistics_to_skb(skb, &connection_statistics, !capable(CAP_SYS_ADMIN)); + connection_statistics_to_skb(skb, &connection_statistics); genlmsg_end(skb, dh); if (multicast) { err = drbd_genl_multicast_events(skb, GFP_NOWAIT); @@ -4809,7 +4850,7 @@ int notify_peer_device_state(struct sk_buff *skb, } err = -EMSGSIZE; - dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_PEER_DEVICE_STATE); + dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_PEER_DEVICE_STATE); if (!dh) goto nla_put_failure; dh->minor = -1U; @@ -4817,10 +4858,10 @@ int notify_peer_device_state(struct sk_buff *skb, if (nla_put_drbd_cfg_context(skb, resource, peer_device->connection, peer_device->device) || nla_put_notification_header(skb, type) || ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY && - peer_device_info_to_skb(skb, peer_device_info, true))) + peer_device_info_to_skb(skb, peer_device_info))) goto nla_put_failure; peer_device_to_statistics(&peer_device_statistics, peer_device); - peer_device_statistics_to_skb(skb, &peer_device_statistics, !capable(CAP_SYS_ADMIN)); + peer_device_statistics_to_skb(skb, &peer_device_statistics); genlmsg_end(skb, dh); if (multicast) { err = drbd_genl_multicast_events(skb, GFP_NOWAIT); @@ -4859,7 +4900,7 @@ void notify_helper(enum drbd_notification_type type, goto fail; err = -EMSGSIZE; - dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_HELPER); + dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_HELPER); if (!dh) goto fail; dh->minor = device ? device->minor : -1; @@ -4867,7 +4908,7 @@ void notify_helper(enum drbd_notification_type type, mutex_lock(¬ification_mutex); if (nla_put_drbd_cfg_context(skb, resource, connection, device) || nla_put_notification_header(skb, type) || - drbd_helper_info_to_skb(skb, &helper_info, true)) + drbd_helper_info_to_skb(skb, &helper_info)) goto unlock_fail; genlmsg_end(skb, dh); err = drbd_genl_multicast_events(skb, GFP_NOWAIT); @@ -4892,7 +4933,7 @@ static int notify_initial_state_done(struct sk_buff *skb, unsigned int seq) int err; err = -EMSGSIZE; - dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_INITIAL_STATE_DONE); + dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_INITIAL_STATE_DONE); if (!dh) goto nla_put_failure; dh->minor = -1U; @@ -4987,7 +5028,7 @@ out: return skb->len; } -int drbd_adm_get_initial_state(struct sk_buff *skb, struct netlink_callback *cb) +int drbd_nl_get_initial_state_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct drbd_resource *resource; LIST_HEAD(head); @@ -5035,3 +5076,20 @@ int drbd_adm_get_initial_state(struct sk_buff *skb, struct netlink_callback *cb) cb->args[2] = cb->nlh->nlmsg_seq; return get_initial_state(skb, cb); } + +static const struct genl_multicast_group drbd_nl_mcgrps[] = { + [DRBD_NLGRP_EVENTS] = { .name = "events", }, +}; + +struct genl_family drbd_nl_family __ro_after_init = { + .name = "drbd", + .version = DRBD_FAMILY_VERSION, + .hdrsize = NLA_ALIGN(sizeof(struct drbd_genlmsghdr)), + .split_ops = drbd_nl_ops, + .n_split_ops = ARRAY_SIZE(drbd_nl_ops), + .mcgrps = drbd_nl_mcgrps, + .n_mcgrps = ARRAY_SIZE(drbd_nl_mcgrps), + .resv_start_op = 42, + .module = THIS_MODULE, + .netnsok = true, +}; diff --git a/drivers/block/drbd/drbd_nl_gen.c b/drivers/block/drbd/drbd_nl_gen.c new file mode 100644 index 000000000000..fb44b948cec8 --- /dev/null +++ b/drivers/block/drbd/drbd_nl_gen.c @@ -0,0 +1,2606 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) + +#include <net/netlink.h> +#include <net/genetlink.h> + +#include <linux/kernel.h> +#include <linux/slab.h> +#include "drbd_nl_gen.h" + +#include <uapi/linux/drbd_genl.h> +#include <linux/drbd.h> +#include <linux/drbd_limits.h> + +/* Common nested types */ +const struct nla_policy drbd_connection_info_nl_policy[DRBD_A_CONNECTION_INFO_CONN_ROLE + 1] = { + [DRBD_A_CONNECTION_INFO_CONN_CONNECTION_STATE] = { .type = NLA_U32, }, + [DRBD_A_CONNECTION_INFO_CONN_ROLE] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_connection_statistics_nl_policy[DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1] = { + [DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED] = { .type = NLA_U8, }, +}; + +const struct nla_policy drbd_detach_parms_nl_policy[DRBD_A_DETACH_PARMS_FORCE_DETACH + 1] = { + [DRBD_A_DETACH_PARMS_FORCE_DETACH] = { .type = NLA_U8, }, +}; + +const struct nla_policy drbd_device_info_nl_policy[DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1] = { + [DRBD_A_DEVICE_INFO_DEV_DISK_STATE] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_device_statistics_nl_policy[DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1] = { + [DRBD_A_DEVICE_STATISTICS_DEV_SIZE] = { .type = NLA_U64, }, + [DRBD_A_DEVICE_STATISTICS_DEV_READ] = { .type = NLA_U64, }, + [DRBD_A_DEVICE_STATISTICS_DEV_WRITE] = { .type = NLA_U64, }, + [DRBD_A_DEVICE_STATISTICS_DEV_AL_WRITES] = { .type = NLA_U64, }, + [DRBD_A_DEVICE_STATISTICS_DEV_BM_WRITES] = { .type = NLA_U64, }, + [DRBD_A_DEVICE_STATISTICS_DEV_UPPER_PENDING] = { .type = NLA_U32, }, + [DRBD_A_DEVICE_STATISTICS_DEV_LOWER_PENDING] = { .type = NLA_U32, }, + [DRBD_A_DEVICE_STATISTICS_DEV_UPPER_BLOCKED] = { .type = NLA_U8, }, + [DRBD_A_DEVICE_STATISTICS_DEV_LOWER_BLOCKED] = { .type = NLA_U8, }, + [DRBD_A_DEVICE_STATISTICS_DEV_AL_SUSPENDED] = { .type = NLA_U8, }, + [DRBD_A_DEVICE_STATISTICS_DEV_EXPOSED_DATA_UUID] = { .type = NLA_U64, }, + [DRBD_A_DEVICE_STATISTICS_DEV_CURRENT_UUID] = { .type = NLA_U64, }, + [DRBD_A_DEVICE_STATISTICS_DEV_DISK_FLAGS] = { .type = NLA_U32, }, + [DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS] = NLA_POLICY_MAX_LEN(DRBD_NL_HISTORY_UUIDS_SIZE), +}; + +const struct nla_policy drbd_disconnect_parms_nl_policy[DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1] = { + [DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT] = { .type = NLA_U8, }, +}; + +const struct nla_policy drbd_disk_conf_nl_policy[DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1] = { + [DRBD_A_DISK_CONF_BACKING_DEV] = { .type = NLA_NUL_STRING, .len = 128, }, + [DRBD_A_DISK_CONF_META_DEV] = { .type = NLA_NUL_STRING, .len = 128, }, + [DRBD_A_DISK_CONF_META_DEV_IDX] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_DISK_SIZE] = { .type = NLA_U64, }, + [DRBD_A_DISK_CONF_MAX_BIO_BVECS] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_ON_IO_ERROR] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_FENCING] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_RESYNC_RATE] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_RESYNC_AFTER] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_AL_EXTENTS] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_C_PLAN_AHEAD] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_C_DELAY_TARGET] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_C_FILL_TARGET] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_C_MAX_RATE] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_C_MIN_RATE] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_DISK_BARRIER] = { .type = NLA_U8, }, + [DRBD_A_DISK_CONF_DISK_FLUSHES] = { .type = NLA_U8, }, + [DRBD_A_DISK_CONF_DISK_DRAIN] = { .type = NLA_U8, }, + [DRBD_A_DISK_CONF_MD_FLUSHES] = { .type = NLA_U8, }, + [DRBD_A_DISK_CONF_DISK_TIMEOUT] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_READ_BALANCING] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_AL_UPDATES] = { .type = NLA_U8, }, + [DRBD_A_DISK_CONF_DISCARD_ZEROES_IF_ALIGNED] = { .type = NLA_U8, }, + [DRBD_A_DISK_CONF_RS_DISCARD_GRANULARITY] = { .type = NLA_U32, }, + [DRBD_A_DISK_CONF_DISABLE_WRITE_SAME] = { .type = NLA_U8, }, +}; + +const struct nla_policy drbd_drbd_cfg_context_nl_policy[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1] = { + [DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME] = { .type = NLA_U32, }, + [DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME] = { .type = NLA_NUL_STRING, .len = 128, }, + [DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR] = NLA_POLICY_MAX_LEN(128), + [DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR] = NLA_POLICY_MAX_LEN(128), +}; + +const struct nla_policy drbd_net_conf_nl_policy[DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1] = { + [DRBD_A_NET_CONF_SHARED_SECRET] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, }, + [DRBD_A_NET_CONF_CRAM_HMAC_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, }, + [DRBD_A_NET_CONF_INTEGRITY_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, }, + [DRBD_A_NET_CONF_VERIFY_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, }, + [DRBD_A_NET_CONF_CSUMS_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, }, + [DRBD_A_NET_CONF_WIRE_PROTOCOL] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_CONNECT_INT] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_TIMEOUT] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_PING_INT] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_PING_TIMEO] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_SNDBUF_SIZE] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_RCVBUF_SIZE] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_KO_COUNT] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_MAX_BUFFERS] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_MAX_EPOCH_SIZE] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_UNPLUG_WATERMARK] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_AFTER_SB_0P] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_AFTER_SB_1P] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_AFTER_SB_2P] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_RR_CONFLICT] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_ON_CONGESTION] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_CONG_FILL] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_CONG_EXTENTS] = { .type = NLA_U32, }, + [DRBD_A_NET_CONF_TWO_PRIMARIES] = { .type = NLA_U8, }, + [DRBD_A_NET_CONF_DISCARD_MY_DATA] = { .type = NLA_U8, }, + [DRBD_A_NET_CONF_TCP_CORK] = { .type = NLA_U8, }, + [DRBD_A_NET_CONF_ALWAYS_ASBP] = { .type = NLA_U8, }, + [DRBD_A_NET_CONF_TENTATIVE] = { .type = NLA_U8, }, + [DRBD_A_NET_CONF_USE_RLE] = { .type = NLA_U8, }, + [DRBD_A_NET_CONF_CSUMS_AFTER_CRASH_ONLY] = { .type = NLA_U8, }, + [DRBD_A_NET_CONF_SOCK_CHECK_TIMEO] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_new_c_uuid_parms_nl_policy[DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1] = { + [DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM] = { .type = NLA_U8, }, +}; + +const struct nla_policy drbd_peer_device_info_nl_policy[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1] = { + [DRBD_A_PEER_DEVICE_INFO_PEER_REPL_STATE] = { .type = NLA_U32, }, + [DRBD_A_PEER_DEVICE_INFO_PEER_DISK_STATE] = { .type = NLA_U32, }, + [DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_USER] = { .type = NLA_U32, }, + [DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_PEER] = { .type = NLA_U32, }, + [DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_peer_device_statistics_nl_policy[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1] = { + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RECEIVED] = { .type = NLA_U64, }, + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_SENT] = { .type = NLA_U64, }, + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_PENDING] = { .type = NLA_U32, }, + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_UNACKED] = { .type = NLA_U32, }, + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_OUT_OF_SYNC] = { .type = NLA_U64, }, + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RESYNC_FAILED] = { .type = NLA_U64, }, + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_BITMAP_UUID] = { .type = NLA_U64, }, + [DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_res_opts_nl_policy[DRBD_A_RES_OPTS_ON_NO_DATA + 1] = { + [DRBD_A_RES_OPTS_CPU_MASK] = { .type = NLA_NUL_STRING, .len = DRBD_CPU_MASK_SIZE, }, + [DRBD_A_RES_OPTS_ON_NO_DATA] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_resize_parms_nl_policy[DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1] = { + [DRBD_A_RESIZE_PARMS_RESIZE_SIZE] = { .type = NLA_U64, }, + [DRBD_A_RESIZE_PARMS_RESIZE_FORCE] = { .type = NLA_U8, }, + [DRBD_A_RESIZE_PARMS_NO_RESYNC] = { .type = NLA_U8, }, + [DRBD_A_RESIZE_PARMS_AL_STRIPES] = { .type = NLA_U32, }, + [DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_resource_info_nl_policy[DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1] = { + [DRBD_A_RESOURCE_INFO_RES_ROLE] = { .type = NLA_U32, }, + [DRBD_A_RESOURCE_INFO_RES_SUSP] = { .type = NLA_U8, }, + [DRBD_A_RESOURCE_INFO_RES_SUSP_NOD] = { .type = NLA_U8, }, + [DRBD_A_RESOURCE_INFO_RES_SUSP_FEN] = { .type = NLA_U8, }, +}; + +const struct nla_policy drbd_resource_statistics_nl_policy[DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1] = { + [DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING] = { .type = NLA_U32, }, +}; + +const struct nla_policy drbd_set_role_parms_nl_policy[DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1] = { + [DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE] = { .type = NLA_U8, }, +}; + +const struct nla_policy drbd_start_ov_parms_nl_policy[DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1] = { + [DRBD_A_START_OV_PARMS_OV_START_SECTOR] = { .type = NLA_U64, }, + [DRBD_A_START_OV_PARMS_OV_STOP_SECTOR] = { .type = NLA_U64, }, +}; + +/* DRBD_ADM_GET_STATUS - do */ +static const struct nla_policy drbd_get_status_do_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_GET_STATUS - dump */ +static const struct nla_policy drbd_get_status_dump_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_NEW_MINOR - do */ +static const struct nla_policy drbd_new_minor_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_DEL_MINOR - do */ +static const struct nla_policy drbd_del_minor_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_NEW_RESOURCE - do */ +static const struct nla_policy drbd_new_resource_nl_policy[DRBD_NLA_RESOURCE_OPTS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_RESOURCE_OPTS] = NLA_POLICY_NESTED(drbd_res_opts_nl_policy), +}; + +/* DRBD_ADM_DEL_RESOURCE - do */ +static const struct nla_policy drbd_del_resource_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_RESOURCE_OPTS - do */ +static const struct nla_policy drbd_resource_opts_nl_policy[DRBD_NLA_RESOURCE_OPTS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_RESOURCE_OPTS] = NLA_POLICY_NESTED(drbd_res_opts_nl_policy), +}; + +/* DRBD_ADM_CONNECT - do */ +static const struct nla_policy drbd_connect_nl_policy[DRBD_NLA_NET_CONF + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_NET_CONF] = NLA_POLICY_NESTED(drbd_net_conf_nl_policy), +}; + +/* DRBD_ADM_DISCONNECT - do */ +static const struct nla_policy drbd_disconnect_nl_policy[DRBD_NLA_DISCONNECT_PARMS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_DISCONNECT_PARMS] = NLA_POLICY_NESTED(drbd_disconnect_parms_nl_policy), +}; + +/* DRBD_ADM_ATTACH - do */ +static const struct nla_policy drbd_attach_nl_policy[DRBD_NLA_DISK_CONF + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_DISK_CONF] = NLA_POLICY_NESTED(drbd_disk_conf_nl_policy), +}; + +/* DRBD_ADM_RESIZE - do */ +static const struct nla_policy drbd_resize_nl_policy[DRBD_NLA_RESIZE_PARMS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_RESIZE_PARMS] = NLA_POLICY_NESTED(drbd_resize_parms_nl_policy), +}; + +/* DRBD_ADM_PRIMARY - do */ +static const struct nla_policy drbd_primary_nl_policy[DRBD_NLA_SET_ROLE_PARMS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_SET_ROLE_PARMS] = NLA_POLICY_NESTED(drbd_set_role_parms_nl_policy), +}; + +/* DRBD_ADM_SECONDARY - do */ +static const struct nla_policy drbd_secondary_nl_policy[DRBD_NLA_SET_ROLE_PARMS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_SET_ROLE_PARMS] = NLA_POLICY_NESTED(drbd_set_role_parms_nl_policy), +}; + +/* DRBD_ADM_NEW_C_UUID - do */ +static const struct nla_policy drbd_new_c_uuid_nl_policy[DRBD_NLA_NEW_C_UUID_PARMS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_NEW_C_UUID_PARMS] = NLA_POLICY_NESTED(drbd_new_c_uuid_parms_nl_policy), +}; + +/* DRBD_ADM_START_OV - do */ +static const struct nla_policy drbd_start_ov_nl_policy[DRBD_NLA_START_OV_PARMS + 1] = { + [DRBD_NLA_START_OV_PARMS] = NLA_POLICY_NESTED(drbd_start_ov_parms_nl_policy), +}; + +/* DRBD_ADM_DETACH - do */ +static const struct nla_policy drbd_detach_nl_policy[DRBD_NLA_DETACH_PARMS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_DETACH_PARMS] = NLA_POLICY_NESTED(drbd_detach_parms_nl_policy), +}; + +/* DRBD_ADM_INVALIDATE - do */ +static const struct nla_policy drbd_invalidate_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_INVAL_PEER - do */ +static const struct nla_policy drbd_inval_peer_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_PAUSE_SYNC - do */ +static const struct nla_policy drbd_pause_sync_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_RESUME_SYNC - do */ +static const struct nla_policy drbd_resume_sync_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_SUSPEND_IO - do */ +static const struct nla_policy drbd_suspend_io_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_RESUME_IO - do */ +static const struct nla_policy drbd_resume_io_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_OUTDATE - do */ +static const struct nla_policy drbd_outdate_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_GET_TIMEOUT_TYPE - do */ +static const struct nla_policy drbd_get_timeout_type_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_DOWN - do */ +static const struct nla_policy drbd_down_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* DRBD_ADM_CHG_DISK_OPTS - do */ +static const struct nla_policy drbd_chg_disk_opts_nl_policy[DRBD_NLA_DISK_CONF + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_DISK_CONF] = NLA_POLICY_NESTED(drbd_disk_conf_nl_policy), +}; + +/* DRBD_ADM_CHG_NET_OPTS - do */ +static const struct nla_policy drbd_chg_net_opts_nl_policy[DRBD_NLA_NET_CONF + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_NET_CONF] = NLA_POLICY_NESTED(drbd_net_conf_nl_policy), +}; + +/* DRBD_ADM_GET_RESOURCES - dump */ +static const struct nla_policy drbd_get_resources_nl_policy[DRBD_NLA_RESOURCE_STATISTICS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_RESOURCE_INFO] = NLA_POLICY_NESTED(drbd_resource_info_nl_policy), + [DRBD_NLA_RESOURCE_STATISTICS] = NLA_POLICY_NESTED(drbd_resource_statistics_nl_policy), +}; + +/* DRBD_ADM_GET_DEVICES - dump */ +static const struct nla_policy drbd_get_devices_nl_policy[DRBD_NLA_DEVICE_STATISTICS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_DEVICE_INFO] = NLA_POLICY_NESTED(drbd_device_info_nl_policy), + [DRBD_NLA_DEVICE_STATISTICS] = NLA_POLICY_NESTED(drbd_device_statistics_nl_policy), +}; + +/* DRBD_ADM_GET_CONNECTIONS - dump */ +static const struct nla_policy drbd_get_connections_nl_policy[DRBD_NLA_CONNECTION_STATISTICS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_CONNECTION_INFO] = NLA_POLICY_NESTED(drbd_connection_info_nl_policy), + [DRBD_NLA_CONNECTION_STATISTICS] = NLA_POLICY_NESTED(drbd_connection_statistics_nl_policy), +}; + +/* DRBD_ADM_GET_PEER_DEVICES - dump */ +static const struct nla_policy drbd_get_peer_devices_nl_policy[DRBD_NLA_PEER_DEVICE_STATISTICS + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), + [DRBD_NLA_PEER_DEVICE_INFO] = NLA_POLICY_NESTED(drbd_peer_device_info_nl_policy), + [DRBD_NLA_PEER_DEVICE_STATISTICS] = NLA_POLICY_NESTED(drbd_peer_device_statistics_nl_policy), +}; + +/* DRBD_ADM_GET_INITIAL_STATE - dump */ +static const struct nla_policy drbd_get_initial_state_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = { + [DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy), +}; + +/* Ops table for drbd */ +const struct genl_split_ops drbd_nl_ops[32] = { + { + .cmd = DRBD_ADM_GET_STATUS, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_get_status_doit, + .post_doit = drbd_post_doit, + .policy = drbd_get_status_do_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_GET_STATUS, + .dumpit = drbd_nl_get_status_dumpit, + .policy = drbd_get_status_dump_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_CMD_CAP_DUMP, + }, + { + .cmd = DRBD_ADM_NEW_MINOR, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_new_minor_doit, + .post_doit = drbd_post_doit, + .policy = drbd_new_minor_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_DEL_MINOR, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_del_minor_doit, + .post_doit = drbd_post_doit, + .policy = drbd_del_minor_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_NEW_RESOURCE, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_new_resource_doit, + .post_doit = drbd_post_doit, + .policy = drbd_new_resource_nl_policy, + .maxattr = DRBD_NLA_RESOURCE_OPTS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_DEL_RESOURCE, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_del_resource_doit, + .post_doit = drbd_post_doit, + .policy = drbd_del_resource_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_RESOURCE_OPTS, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_resource_opts_doit, + .post_doit = drbd_post_doit, + .policy = drbd_resource_opts_nl_policy, + .maxattr = DRBD_NLA_RESOURCE_OPTS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_CONNECT, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_connect_doit, + .post_doit = drbd_post_doit, + .policy = drbd_connect_nl_policy, + .maxattr = DRBD_NLA_NET_CONF, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_DISCONNECT, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_disconnect_doit, + .post_doit = drbd_post_doit, + .policy = drbd_disconnect_nl_policy, + .maxattr = DRBD_NLA_DISCONNECT_PARMS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_ATTACH, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_attach_doit, + .post_doit = drbd_post_doit, + .policy = drbd_attach_nl_policy, + .maxattr = DRBD_NLA_DISK_CONF, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_RESIZE, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_resize_doit, + .post_doit = drbd_post_doit, + .policy = drbd_resize_nl_policy, + .maxattr = DRBD_NLA_RESIZE_PARMS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_PRIMARY, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_primary_doit, + .post_doit = drbd_post_doit, + .policy = drbd_primary_nl_policy, + .maxattr = DRBD_NLA_SET_ROLE_PARMS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_SECONDARY, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_secondary_doit, + .post_doit = drbd_post_doit, + .policy = drbd_secondary_nl_policy, + .maxattr = DRBD_NLA_SET_ROLE_PARMS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_NEW_C_UUID, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_new_c_uuid_doit, + .post_doit = drbd_post_doit, + .policy = drbd_new_c_uuid_nl_policy, + .maxattr = DRBD_NLA_NEW_C_UUID_PARMS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_START_OV, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_start_ov_doit, + .post_doit = drbd_post_doit, + .policy = drbd_start_ov_nl_policy, + .maxattr = DRBD_NLA_START_OV_PARMS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_DETACH, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_detach_doit, + .post_doit = drbd_post_doit, + .policy = drbd_detach_nl_policy, + .maxattr = DRBD_NLA_DETACH_PARMS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_INVALIDATE, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_invalidate_doit, + .post_doit = drbd_post_doit, + .policy = drbd_invalidate_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_INVAL_PEER, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_inval_peer_doit, + .post_doit = drbd_post_doit, + .policy = drbd_inval_peer_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_PAUSE_SYNC, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_pause_sync_doit, + .post_doit = drbd_post_doit, + .policy = drbd_pause_sync_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_RESUME_SYNC, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_resume_sync_doit, + .post_doit = drbd_post_doit, + .policy = drbd_resume_sync_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_SUSPEND_IO, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_suspend_io_doit, + .post_doit = drbd_post_doit, + .policy = drbd_suspend_io_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_RESUME_IO, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_resume_io_doit, + .post_doit = drbd_post_doit, + .policy = drbd_resume_io_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_OUTDATE, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_outdate_doit, + .post_doit = drbd_post_doit, + .policy = drbd_outdate_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_GET_TIMEOUT_TYPE, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_get_timeout_type_doit, + .post_doit = drbd_post_doit, + .policy = drbd_get_timeout_type_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_DOWN, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_down_doit, + .post_doit = drbd_post_doit, + .policy = drbd_down_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_CHG_DISK_OPTS, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_chg_disk_opts_doit, + .post_doit = drbd_post_doit, + .policy = drbd_chg_disk_opts_nl_policy, + .maxattr = DRBD_NLA_DISK_CONF, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_CHG_NET_OPTS, + .pre_doit = drbd_pre_doit, + .doit = drbd_nl_chg_net_opts_doit, + .post_doit = drbd_post_doit, + .policy = drbd_chg_net_opts_nl_policy, + .maxattr = DRBD_NLA_NET_CONF, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRBD_ADM_GET_RESOURCES, + .dumpit = drbd_nl_get_resources_dumpit, + .policy = drbd_get_resources_nl_policy, + .maxattr = DRBD_NLA_RESOURCE_STATISTICS, + .flags = GENL_CMD_CAP_DUMP, + }, + { + .cmd = DRBD_ADM_GET_DEVICES, + .dumpit = drbd_nl_get_devices_dumpit, + .done = drbd_adm_dump_devices_done, + .policy = drbd_get_devices_nl_policy, + .maxattr = DRBD_NLA_DEVICE_STATISTICS, + .flags = GENL_CMD_CAP_DUMP, + }, + { + .cmd = DRBD_ADM_GET_CONNECTIONS, + .dumpit = drbd_nl_get_connections_dumpit, + .done = drbd_adm_dump_connections_done, + .policy = drbd_get_connections_nl_policy, + .maxattr = DRBD_NLA_CONNECTION_STATISTICS, + .flags = GENL_CMD_CAP_DUMP, + }, + { + .cmd = DRBD_ADM_GET_PEER_DEVICES, + .dumpit = drbd_nl_get_peer_devices_dumpit, + .done = drbd_adm_dump_peer_devices_done, + .policy = drbd_get_peer_devices_nl_policy, + .maxattr = DRBD_NLA_PEER_DEVICE_STATISTICS, + .flags = GENL_CMD_CAP_DUMP, + }, + { + .cmd = DRBD_ADM_GET_INITIAL_STATE, + .dumpit = drbd_nl_get_initial_state_dumpit, + .policy = drbd_get_initial_state_nl_policy, + .maxattr = DRBD_NLA_CFG_CONTEXT, + .flags = GENL_CMD_CAP_DUMP, + }, +}; + +static const struct genl_multicast_group drbd_nl_mcgrps[] = { + [DRBD_NLGRP_EVENTS] = { "events", }, +}; + +static int __drbd_cfg_context_from_attrs(struct drbd_cfg_context *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR; + struct nlattr *tla = info->attrs[DRBD_NLA_CFG_CONTEXT]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_drbd_cfg_context_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME]; + if (nla && s) + s->ctx_volume = nla_get_u32(nla); + + nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME]; + if (nla && s) + s->ctx_resource_name_len = nla_strscpy(s->ctx_resource_name, nla, 128); + + nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR]; + if (nla && s) + s->ctx_my_addr_len = nla_memcpy(s->ctx_my_addr, nla, 128); + + nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR]; + if (nla && s) + s->ctx_peer_addr_len = nla_memcpy(s->ctx_peer_addr, nla, 128); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int drbd_cfg_context_from_attrs(struct drbd_cfg_context *s, + struct genl_info *info) +{ + return __drbd_cfg_context_from_attrs(s, NULL, info); +} + +int drbd_cfg_context_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __drbd_cfg_context_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __disk_conf_from_attrs(struct disk_conf *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_DISK_CONF_DISABLE_WRITE_SAME; + struct nlattr *tla = info->attrs[DRBD_NLA_DISK_CONF]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_disk_conf_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_DISK_CONF_BACKING_DEV]; + if (nla) { + if (s) + s->backing_dev_len = nla_strscpy(s->backing_dev, nla, 128); + } else { + pr_info("<< missing required attr: backing_dev\n"); + err = -ENOMSG; + } + + nla = ntb[DRBD_A_DISK_CONF_META_DEV]; + if (nla) { + if (s) + s->meta_dev_len = nla_strscpy(s->meta_dev, nla, 128); + } else { + pr_info("<< missing required attr: meta_dev\n"); + err = -ENOMSG; + } + + nla = ntb[DRBD_A_DISK_CONF_META_DEV_IDX]; + if (nla) { + if (s) + s->meta_dev_idx = nla_get_s32(nla); + } else { + pr_info("<< missing required attr: meta_dev_idx\n"); + err = -ENOMSG; + } + + nla = ntb[DRBD_A_DISK_CONF_DISK_SIZE]; + if (nla && s) + s->disk_size = nla_get_u64(nla); + + nla = ntb[DRBD_A_DISK_CONF_MAX_BIO_BVECS]; + if (nla && s) + s->max_bio_bvecs = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_ON_IO_ERROR]; + if (nla && s) + s->on_io_error = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_FENCING]; + if (nla && s) + s->fencing = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_RESYNC_RATE]; + if (nla && s) + s->resync_rate = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_RESYNC_AFTER]; + if (nla && s) + s->resync_after = nla_get_s32(nla); + + nla = ntb[DRBD_A_DISK_CONF_AL_EXTENTS]; + if (nla && s) + s->al_extents = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_C_PLAN_AHEAD]; + if (nla && s) + s->c_plan_ahead = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_C_DELAY_TARGET]; + if (nla && s) + s->c_delay_target = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_C_FILL_TARGET]; + if (nla && s) + s->c_fill_target = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_C_MAX_RATE]; + if (nla && s) + s->c_max_rate = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_C_MIN_RATE]; + if (nla && s) + s->c_min_rate = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_DISK_BARRIER]; + if (nla && s) + s->disk_barrier = nla_get_u8(nla); + + nla = ntb[DRBD_A_DISK_CONF_DISK_FLUSHES]; + if (nla && s) + s->disk_flushes = nla_get_u8(nla); + + nla = ntb[DRBD_A_DISK_CONF_DISK_DRAIN]; + if (nla && s) + s->disk_drain = nla_get_u8(nla); + + nla = ntb[DRBD_A_DISK_CONF_MD_FLUSHES]; + if (nla && s) + s->md_flushes = nla_get_u8(nla); + + nla = ntb[DRBD_A_DISK_CONF_DISK_TIMEOUT]; + if (nla && s) + s->disk_timeout = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_READ_BALANCING]; + if (nla && s) + s->read_balancing = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_AL_UPDATES]; + if (nla && s) + s->al_updates = nla_get_u8(nla); + + nla = ntb[DRBD_A_DISK_CONF_DISCARD_ZEROES_IF_ALIGNED]; + if (nla && s) + s->discard_zeroes_if_aligned = nla_get_u8(nla); + + nla = ntb[DRBD_A_DISK_CONF_RS_DISCARD_GRANULARITY]; + if (nla && s) + s->rs_discard_granularity = nla_get_u32(nla); + + nla = ntb[DRBD_A_DISK_CONF_DISABLE_WRITE_SAME]; + if (nla && s) + s->disable_write_same = nla_get_u8(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int disk_conf_from_attrs(struct disk_conf *s, + struct genl_info *info) +{ + return __disk_conf_from_attrs(s, NULL, info); +} + +int disk_conf_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __disk_conf_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __res_opts_from_attrs(struct res_opts *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_RES_OPTS_ON_NO_DATA; + struct nlattr *tla = info->attrs[DRBD_NLA_RESOURCE_OPTS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_RES_OPTS_ON_NO_DATA + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_res_opts_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_RES_OPTS_CPU_MASK]; + if (nla && s) + s->cpu_mask_len = nla_strscpy(s->cpu_mask, nla, DRBD_CPU_MASK_SIZE); + + nla = ntb[DRBD_A_RES_OPTS_ON_NO_DATA]; + if (nla && s) + s->on_no_data = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int res_opts_from_attrs(struct res_opts *s, + struct genl_info *info) +{ + return __res_opts_from_attrs(s, NULL, info); +} + +int res_opts_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __res_opts_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __net_conf_from_attrs(struct net_conf *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_NET_CONF_SOCK_CHECK_TIMEO; + struct nlattr *tla = info->attrs[DRBD_NLA_NET_CONF]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_net_conf_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_NET_CONF_SHARED_SECRET]; + if (nla && s) + s->shared_secret_len = nla_strscpy(s->shared_secret, nla, SHARED_SECRET_MAX); + + nla = ntb[DRBD_A_NET_CONF_CRAM_HMAC_ALG]; + if (nla && s) + s->cram_hmac_alg_len = nla_strscpy(s->cram_hmac_alg, nla, SHARED_SECRET_MAX); + + nla = ntb[DRBD_A_NET_CONF_INTEGRITY_ALG]; + if (nla && s) + s->integrity_alg_len = nla_strscpy(s->integrity_alg, nla, SHARED_SECRET_MAX); + + nla = ntb[DRBD_A_NET_CONF_VERIFY_ALG]; + if (nla && s) + s->verify_alg_len = nla_strscpy(s->verify_alg, nla, SHARED_SECRET_MAX); + + nla = ntb[DRBD_A_NET_CONF_CSUMS_ALG]; + if (nla && s) + s->csums_alg_len = nla_strscpy(s->csums_alg, nla, SHARED_SECRET_MAX); + + nla = ntb[DRBD_A_NET_CONF_WIRE_PROTOCOL]; + if (nla && s) + s->wire_protocol = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_CONNECT_INT]; + if (nla && s) + s->connect_int = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_TIMEOUT]; + if (nla && s) + s->timeout = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_PING_INT]; + if (nla && s) + s->ping_int = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_PING_TIMEO]; + if (nla && s) + s->ping_timeo = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_SNDBUF_SIZE]; + if (nla && s) + s->sndbuf_size = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_RCVBUF_SIZE]; + if (nla && s) + s->rcvbuf_size = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_KO_COUNT]; + if (nla && s) + s->ko_count = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_MAX_BUFFERS]; + if (nla && s) + s->max_buffers = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_MAX_EPOCH_SIZE]; + if (nla && s) + s->max_epoch_size = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_UNPLUG_WATERMARK]; + if (nla && s) + s->unplug_watermark = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_AFTER_SB_0P]; + if (nla && s) + s->after_sb_0p = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_AFTER_SB_1P]; + if (nla && s) + s->after_sb_1p = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_AFTER_SB_2P]; + if (nla && s) + s->after_sb_2p = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_RR_CONFLICT]; + if (nla && s) + s->rr_conflict = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_ON_CONGESTION]; + if (nla && s) + s->on_congestion = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_CONG_FILL]; + if (nla && s) + s->cong_fill = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_CONG_EXTENTS]; + if (nla && s) + s->cong_extents = nla_get_u32(nla); + + nla = ntb[DRBD_A_NET_CONF_TWO_PRIMARIES]; + if (nla && s) + s->two_primaries = nla_get_u8(nla); + + nla = ntb[DRBD_A_NET_CONF_DISCARD_MY_DATA]; + if (nla && s) + s->discard_my_data = nla_get_u8(nla); + + nla = ntb[DRBD_A_NET_CONF_TCP_CORK]; + if (nla && s) + s->tcp_cork = nla_get_u8(nla); + + nla = ntb[DRBD_A_NET_CONF_ALWAYS_ASBP]; + if (nla && s) + s->always_asbp = nla_get_u8(nla); + + nla = ntb[DRBD_A_NET_CONF_TENTATIVE]; + if (nla && s) + s->tentative = nla_get_u8(nla); + + nla = ntb[DRBD_A_NET_CONF_USE_RLE]; + if (nla && s) + s->use_rle = nla_get_u8(nla); + + nla = ntb[DRBD_A_NET_CONF_CSUMS_AFTER_CRASH_ONLY]; + if (nla && s) + s->csums_after_crash_only = nla_get_u8(nla); + + nla = ntb[DRBD_A_NET_CONF_SOCK_CHECK_TIMEO]; + if (nla && s) + s->sock_check_timeo = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int net_conf_from_attrs(struct net_conf *s, + struct genl_info *info) +{ + return __net_conf_from_attrs(s, NULL, info); +} + +int net_conf_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __net_conf_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __set_role_parms_from_attrs(struct set_role_parms *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE; + struct nlattr *tla = info->attrs[DRBD_NLA_SET_ROLE_PARMS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_set_role_parms_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE]; + if (nla && s) + s->assume_uptodate = nla_get_u8(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int set_role_parms_from_attrs(struct set_role_parms *s, + struct genl_info *info) +{ + return __set_role_parms_from_attrs(s, NULL, info); +} + +int set_role_parms_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __set_role_parms_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __resize_parms_from_attrs(struct resize_parms *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE; + struct nlattr *tla = info->attrs[DRBD_NLA_RESIZE_PARMS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resize_parms_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_RESIZE_PARMS_RESIZE_SIZE]; + if (nla && s) + s->resize_size = nla_get_u64(nla); + + nla = ntb[DRBD_A_RESIZE_PARMS_RESIZE_FORCE]; + if (nla && s) + s->resize_force = nla_get_u8(nla); + + nla = ntb[DRBD_A_RESIZE_PARMS_NO_RESYNC]; + if (nla && s) + s->no_resync = nla_get_u8(nla); + + nla = ntb[DRBD_A_RESIZE_PARMS_AL_STRIPES]; + if (nla && s) + s->al_stripes = nla_get_u32(nla); + + nla = ntb[DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE]; + if (nla && s) + s->al_stripe_size = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int resize_parms_from_attrs(struct resize_parms *s, + struct genl_info *info) +{ + return __resize_parms_from_attrs(s, NULL, info); +} + +int resize_parms_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __resize_parms_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __start_ov_parms_from_attrs(struct start_ov_parms *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_START_OV_PARMS_OV_STOP_SECTOR; + struct nlattr *tla = info->attrs[DRBD_NLA_START_OV_PARMS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_start_ov_parms_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_START_OV_PARMS_OV_START_SECTOR]; + if (nla && s) + s->ov_start_sector = nla_get_u64(nla); + + nla = ntb[DRBD_A_START_OV_PARMS_OV_STOP_SECTOR]; + if (nla && s) + s->ov_stop_sector = nla_get_u64(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int start_ov_parms_from_attrs(struct start_ov_parms *s, + struct genl_info *info) +{ + return __start_ov_parms_from_attrs(s, NULL, info); +} + +int start_ov_parms_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __start_ov_parms_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __new_c_uuid_parms_from_attrs(struct new_c_uuid_parms *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM; + struct nlattr *tla = info->attrs[DRBD_NLA_NEW_C_UUID_PARMS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_new_c_uuid_parms_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM]; + if (nla && s) + s->clear_bm = nla_get_u8(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int new_c_uuid_parms_from_attrs(struct new_c_uuid_parms *s, + struct genl_info *info) +{ + return __new_c_uuid_parms_from_attrs(s, NULL, info); +} + +int new_c_uuid_parms_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __new_c_uuid_parms_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __disconnect_parms_from_attrs(struct disconnect_parms *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT; + struct nlattr *tla = info->attrs[DRBD_NLA_DISCONNECT_PARMS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_disconnect_parms_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT]; + if (nla && s) + s->force_disconnect = nla_get_u8(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int disconnect_parms_from_attrs(struct disconnect_parms *s, + struct genl_info *info) +{ + return __disconnect_parms_from_attrs(s, NULL, info); +} + +int disconnect_parms_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __disconnect_parms_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __detach_parms_from_attrs(struct detach_parms *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_DETACH_PARMS_FORCE_DETACH; + struct nlattr *tla = info->attrs[DRBD_NLA_DETACH_PARMS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_DETACH_PARMS_FORCE_DETACH + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_detach_parms_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_DETACH_PARMS_FORCE_DETACH]; + if (nla && s) + s->force_detach = nla_get_u8(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int detach_parms_from_attrs(struct detach_parms *s, + struct genl_info *info) +{ + return __detach_parms_from_attrs(s, NULL, info); +} + +int detach_parms_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __detach_parms_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __resource_info_from_attrs(struct resource_info *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_RESOURCE_INFO_RES_SUSP_FEN; + struct nlattr *tla = info->attrs[DRBD_NLA_RESOURCE_INFO]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resource_info_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_RESOURCE_INFO_RES_ROLE]; + if (nla && s) + s->res_role = nla_get_u32(nla); + + nla = ntb[DRBD_A_RESOURCE_INFO_RES_SUSP]; + if (nla && s) + s->res_susp = nla_get_u8(nla); + + nla = ntb[DRBD_A_RESOURCE_INFO_RES_SUSP_NOD]; + if (nla && s) + s->res_susp_nod = nla_get_u8(nla); + + nla = ntb[DRBD_A_RESOURCE_INFO_RES_SUSP_FEN]; + if (nla && s) + s->res_susp_fen = nla_get_u8(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int resource_info_from_attrs(struct resource_info *s, + struct genl_info *info) +{ + return __resource_info_from_attrs(s, NULL, info); +} + +int resource_info_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __resource_info_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __device_info_from_attrs(struct device_info *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_DEVICE_INFO_DEV_DISK_STATE; + struct nlattr *tla = info->attrs[DRBD_NLA_DEVICE_INFO]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_device_info_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_DEVICE_INFO_DEV_DISK_STATE]; + if (nla && s) + s->dev_disk_state = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int device_info_from_attrs(struct device_info *s, + struct genl_info *info) +{ + return __device_info_from_attrs(s, NULL, info); +} + +int device_info_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __device_info_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __connection_info_from_attrs(struct connection_info *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_CONNECTION_INFO_CONN_ROLE; + struct nlattr *tla = info->attrs[DRBD_NLA_CONNECTION_INFO]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_CONNECTION_INFO_CONN_ROLE + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_connection_info_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_CONNECTION_INFO_CONN_CONNECTION_STATE]; + if (nla && s) + s->conn_connection_state = nla_get_u32(nla); + + nla = ntb[DRBD_A_CONNECTION_INFO_CONN_ROLE]; + if (nla && s) + s->conn_role = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int connection_info_from_attrs(struct connection_info *s, + struct genl_info *info) +{ + return __connection_info_from_attrs(s, NULL, info); +} + +int connection_info_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __connection_info_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __peer_device_info_from_attrs(struct peer_device_info *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY; + struct nlattr *tla = info->attrs[DRBD_NLA_PEER_DEVICE_INFO]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_peer_device_info_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_REPL_STATE]; + if (nla && s) + s->peer_repl_state = nla_get_u32(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_DISK_STATE]; + if (nla && s) + s->peer_disk_state = nla_get_u32(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_USER]; + if (nla && s) + s->peer_resync_susp_user = nla_get_u32(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_PEER]; + if (nla && s) + s->peer_resync_susp_peer = nla_get_u32(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY]; + if (nla && s) + s->peer_resync_susp_dependency = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int peer_device_info_from_attrs(struct peer_device_info *s, + struct genl_info *info) +{ + return __peer_device_info_from_attrs(s, NULL, info); +} + +int peer_device_info_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __peer_device_info_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __resource_statistics_from_attrs(struct resource_statistics *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING; + struct nlattr *tla = info->attrs[DRBD_NLA_RESOURCE_STATISTICS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resource_statistics_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING]; + if (nla && s) + s->res_stat_write_ordering = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int resource_statistics_from_attrs(struct resource_statistics *s, + struct genl_info *info) +{ + return __resource_statistics_from_attrs(s, NULL, info); +} + +int resource_statistics_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __resource_statistics_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __device_statistics_from_attrs(struct device_statistics *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS; + struct nlattr *tla = info->attrs[DRBD_NLA_DEVICE_STATISTICS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_device_statistics_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_SIZE]; + if (nla && s) + s->dev_size = nla_get_u64(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_READ]; + if (nla && s) + s->dev_read = nla_get_u64(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_WRITE]; + if (nla && s) + s->dev_write = nla_get_u64(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_AL_WRITES]; + if (nla && s) + s->dev_al_writes = nla_get_u64(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_BM_WRITES]; + if (nla && s) + s->dev_bm_writes = nla_get_u64(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_UPPER_PENDING]; + if (nla && s) + s->dev_upper_pending = nla_get_u32(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_LOWER_PENDING]; + if (nla && s) + s->dev_lower_pending = nla_get_u32(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_UPPER_BLOCKED]; + if (nla && s) + s->dev_upper_blocked = nla_get_u8(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_LOWER_BLOCKED]; + if (nla && s) + s->dev_lower_blocked = nla_get_u8(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_AL_SUSPENDED]; + if (nla && s) + s->dev_al_suspended = nla_get_u8(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_EXPOSED_DATA_UUID]; + if (nla && s) + s->dev_exposed_data_uuid = nla_get_u64(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_CURRENT_UUID]; + if (nla && s) + s->dev_current_uuid = nla_get_u64(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_DISK_FLAGS]; + if (nla && s) + s->dev_disk_flags = nla_get_u32(nla); + + nla = ntb[DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS]; + if (nla && s) + s->history_uuids_len = nla_memcpy(s->history_uuids, nla, DRBD_NL_HISTORY_UUIDS_SIZE); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int device_statistics_from_attrs(struct device_statistics *s, + struct genl_info *info) +{ + return __device_statistics_from_attrs(s, NULL, info); +} + +int device_statistics_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __device_statistics_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __connection_statistics_from_attrs(struct connection_statistics *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED; + struct nlattr *tla = info->attrs[DRBD_NLA_CONNECTION_STATISTICS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_connection_statistics_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED]; + if (nla && s) + s->conn_congested = nla_get_u8(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int connection_statistics_from_attrs(struct connection_statistics *s, + struct genl_info *info) +{ + return __connection_statistics_from_attrs(s, NULL, info); +} + +int connection_statistics_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __connection_statistics_from_attrs(NULL, ret_nested_attribute_table, info); +} + +static int __peer_device_statistics_from_attrs(struct peer_device_statistics *s, + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + const int maxtype = DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS; + struct nlattr *tla = info->attrs[DRBD_NLA_PEER_DEVICE_STATISTICS]; + struct nlattr **ntb; + struct nlattr *nla; + int err = 0; + + if (ret_nested_attribute_table) + *ret_nested_attribute_table = NULL; + if (!tla) + return -ENOMSG; + ntb = kcalloc(DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_peer_device_statistics_nl_policy, NULL); + if (err) + goto out; + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RECEIVED]; + if (nla && s) + s->peer_dev_received = nla_get_u64(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_SENT]; + if (nla && s) + s->peer_dev_sent = nla_get_u64(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_PENDING]; + if (nla && s) + s->peer_dev_pending = nla_get_u32(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_UNACKED]; + if (nla && s) + s->peer_dev_unacked = nla_get_u32(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_OUT_OF_SYNC]; + if (nla && s) + s->peer_dev_out_of_sync = nla_get_u64(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RESYNC_FAILED]; + if (nla && s) + s->peer_dev_resync_failed = nla_get_u64(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_BITMAP_UUID]; + if (nla && s) + s->peer_dev_bitmap_uuid = nla_get_u64(nla); + + nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS]; + if (nla && s) + s->peer_dev_flags = nla_get_u32(nla); + +out: + if (ret_nested_attribute_table && (!err || err == -ENOMSG)) + *ret_nested_attribute_table = ntb; + else + kfree(ntb); + return err; +} + +int peer_device_statistics_from_attrs(struct peer_device_statistics *s, + struct genl_info *info) +{ + return __peer_device_statistics_from_attrs(s, NULL, info); +} + +int peer_device_statistics_ntb_from_attrs( + struct nlattr ***ret_nested_attribute_table, + struct genl_info *info) +{ + return __peer_device_statistics_from_attrs(NULL, ret_nested_attribute_table, info); +} + +int drbd_cfg_reply_to_skb(struct sk_buff *skb, struct drbd_cfg_reply *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CFG_REPLY); + + if (!tla) + goto nla_put_failure; + + if (nla_put(skb, DRBD_A_DRBD_CFG_REPLY_INFO_TEXT, min_t(int, 0, + s->info_text_len + (s->info_text_len < 0)), s->info_text)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int drbd_cfg_context_to_skb(struct sk_buff *skb, struct drbd_cfg_context *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME, s->ctx_volume)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME, min_t(int, 128, + s->ctx_resource_name_len + (s->ctx_resource_name_len < 128)), s->ctx_resource_name)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR, min_t(int, 128, + s->ctx_my_addr_len), s->ctx_my_addr)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR, min_t(int, 128, + s->ctx_peer_addr_len), s->ctx_peer_addr)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int disk_conf_to_skb(struct sk_buff *skb, struct disk_conf *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DISK_CONF); + + if (!tla) + goto nla_put_failure; + + if (nla_put(skb, DRBD_A_DISK_CONF_BACKING_DEV, min_t(int, 128, + s->backing_dev_len + (s->backing_dev_len < 128)), s->backing_dev)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_DISK_CONF_META_DEV, min_t(int, 128, + s->meta_dev_len + (s->meta_dev_len < 128)), s->meta_dev)) + goto nla_put_failure; + if (nla_put_s32(skb, DRBD_A_DISK_CONF_META_DEV_IDX, s->meta_dev_idx)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_DISK_CONF_DISK_SIZE, s->disk_size, 0)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_MAX_BIO_BVECS, s->max_bio_bvecs)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_ON_IO_ERROR, s->on_io_error)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_FENCING, s->fencing)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_RESYNC_RATE, s->resync_rate)) + goto nla_put_failure; + if (nla_put_s32(skb, DRBD_A_DISK_CONF_RESYNC_AFTER, s->resync_after)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_AL_EXTENTS, s->al_extents)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_PLAN_AHEAD, s->c_plan_ahead)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_DELAY_TARGET, s->c_delay_target)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_FILL_TARGET, s->c_fill_target)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_MAX_RATE, s->c_max_rate)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_MIN_RATE, s->c_min_rate)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISK_BARRIER, s->disk_barrier)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISK_FLUSHES, s->disk_flushes)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISK_DRAIN, s->disk_drain)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DISK_CONF_MD_FLUSHES, s->md_flushes)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_DISK_TIMEOUT, s->disk_timeout)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_READ_BALANCING, s->read_balancing)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DISK_CONF_AL_UPDATES, s->al_updates)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISCARD_ZEROES_IF_ALIGNED, s->discard_zeroes_if_aligned)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DISK_CONF_RS_DISCARD_GRANULARITY, s->rs_discard_granularity)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISABLE_WRITE_SAME, s->disable_write_same)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int res_opts_to_skb(struct sk_buff *skb, struct res_opts *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESOURCE_OPTS); + + if (!tla) + goto nla_put_failure; + + if (nla_put(skb, DRBD_A_RES_OPTS_CPU_MASK, min_t(int, DRBD_CPU_MASK_SIZE, + s->cpu_mask_len + (s->cpu_mask_len < DRBD_CPU_MASK_SIZE)), s->cpu_mask)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_RES_OPTS_ON_NO_DATA, s->on_no_data)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int net_conf_to_skb(struct sk_buff *skb, struct net_conf *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_NET_CONF); + + if (!tla) + goto nla_put_failure; + + if (nla_put(skb, DRBD_A_NET_CONF_SHARED_SECRET, min_t(int, SHARED_SECRET_MAX, + s->shared_secret_len + (s->shared_secret_len < SHARED_SECRET_MAX)), s->shared_secret)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_NET_CONF_CRAM_HMAC_ALG, min_t(int, SHARED_SECRET_MAX, + s->cram_hmac_alg_len + (s->cram_hmac_alg_len < SHARED_SECRET_MAX)), s->cram_hmac_alg)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_NET_CONF_INTEGRITY_ALG, min_t(int, SHARED_SECRET_MAX, + s->integrity_alg_len + (s->integrity_alg_len < SHARED_SECRET_MAX)), s->integrity_alg)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_NET_CONF_VERIFY_ALG, min_t(int, SHARED_SECRET_MAX, + s->verify_alg_len + (s->verify_alg_len < SHARED_SECRET_MAX)), s->verify_alg)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_NET_CONF_CSUMS_ALG, min_t(int, SHARED_SECRET_MAX, + s->csums_alg_len + (s->csums_alg_len < SHARED_SECRET_MAX)), s->csums_alg)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_WIRE_PROTOCOL, s->wire_protocol)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_CONNECT_INT, s->connect_int)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_TIMEOUT, s->timeout)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_PING_INT, s->ping_int)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_PING_TIMEO, s->ping_timeo)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_SNDBUF_SIZE, s->sndbuf_size)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_RCVBUF_SIZE, s->rcvbuf_size)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_KO_COUNT, s->ko_count)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_MAX_BUFFERS, s->max_buffers)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_MAX_EPOCH_SIZE, s->max_epoch_size)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_UNPLUG_WATERMARK, s->unplug_watermark)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_AFTER_SB_0P, s->after_sb_0p)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_AFTER_SB_1P, s->after_sb_1p)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_AFTER_SB_2P, s->after_sb_2p)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_RR_CONFLICT, s->rr_conflict)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_ON_CONGESTION, s->on_congestion)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_CONG_FILL, s->cong_fill)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_CONG_EXTENTS, s->cong_extents)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_NET_CONF_TWO_PRIMARIES, s->two_primaries)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_NET_CONF_DISCARD_MY_DATA, s->discard_my_data)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_NET_CONF_TCP_CORK, s->tcp_cork)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_NET_CONF_ALWAYS_ASBP, s->always_asbp)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_NET_CONF_TENTATIVE, s->tentative)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_NET_CONF_USE_RLE, s->use_rle)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_NET_CONF_CSUMS_AFTER_CRASH_ONLY, s->csums_after_crash_only)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_NET_CONF_SOCK_CHECK_TIMEO, s->sock_check_timeo)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int set_role_parms_to_skb(struct sk_buff *skb, struct set_role_parms *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_SET_ROLE_PARMS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u8(skb, DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE, s->assume_uptodate)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int resize_parms_to_skb(struct sk_buff *skb, struct resize_parms *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESIZE_PARMS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u64_64bit(skb, DRBD_A_RESIZE_PARMS_RESIZE_SIZE, s->resize_size, 0)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_RESIZE_PARMS_RESIZE_FORCE, s->resize_force)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_RESIZE_PARMS_NO_RESYNC, s->no_resync)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_RESIZE_PARMS_AL_STRIPES, s->al_stripes)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE, s->al_stripe_size)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int state_info_to_skb(struct sk_buff *skb, struct state_info *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_STATE_INFO); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_STATE_INFO_SIB_REASON, s->sib_reason)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_CURRENT_STATE, s->current_state)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_CAPACITY, s->capacity, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_ED_UUID, s->ed_uuid, 0)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_PREV_STATE, s->prev_state)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_NEW_STATE, s->new_state)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_STATE_INFO_UUIDS, min_t(int, DRBD_NL_UUIDS_SIZE, + s->uuids_len), s->uuids)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_DISK_FLAGS, s->disk_flags)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_TOTAL, s->bits_total, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_OOS, s->bits_oos, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_TOTAL, s->bits_rs_total, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_FAILED, s->bits_rs_failed, 0)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_STATE_INFO_HELPER, min_t(int, 32, + s->helper_len + (s->helper_len < 32)), s->helper)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_HELPER_EXIT_CODE, s->helper_exit_code)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_SEND_CNT, s->send_cnt, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_RECV_CNT, s->recv_cnt, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_READ_CNT, s->read_cnt, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_WRIT_CNT, s->writ_cnt, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_AL_WRIT_CNT, s->al_writ_cnt, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BM_WRIT_CNT, s->bm_writ_cnt, 0)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_AP_BIO_CNT, s->ap_bio_cnt)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_AP_PENDING_CNT, s->ap_pending_cnt)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_STATE_INFO_RS_PENDING_CNT, s->rs_pending_cnt)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int start_ov_parms_to_skb(struct sk_buff *skb, struct start_ov_parms *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_START_OV_PARMS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u64_64bit(skb, DRBD_A_START_OV_PARMS_OV_START_SECTOR, s->ov_start_sector, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_START_OV_PARMS_OV_STOP_SECTOR, s->ov_stop_sector, 0)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int new_c_uuid_parms_to_skb(struct sk_buff *skb, struct new_c_uuid_parms *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_NEW_C_UUID_PARMS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u8(skb, DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM, s->clear_bm)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int timeout_parms_to_skb(struct sk_buff *skb, struct timeout_parms *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_TIMEOUT_PARMS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_TIMEOUT_PARMS_TIMEOUT_TYPE, s->timeout_type)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int disconnect_parms_to_skb(struct sk_buff *skb, struct disconnect_parms *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DISCONNECT_PARMS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u8(skb, DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT, s->force_disconnect)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int detach_parms_to_skb(struct sk_buff *skb, struct detach_parms *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DETACH_PARMS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u8(skb, DRBD_A_DETACH_PARMS_FORCE_DETACH, s->force_detach)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int resource_info_to_skb(struct sk_buff *skb, struct resource_info *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESOURCE_INFO); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_RESOURCE_INFO_RES_ROLE, s->res_role)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_RESOURCE_INFO_RES_SUSP, s->res_susp)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_RESOURCE_INFO_RES_SUSP_NOD, s->res_susp_nod)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_RESOURCE_INFO_RES_SUSP_FEN, s->res_susp_fen)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int device_info_to_skb(struct sk_buff *skb, struct device_info *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DEVICE_INFO); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_DEVICE_INFO_DEV_DISK_STATE, s->dev_disk_state)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int connection_info_to_skb(struct sk_buff *skb, struct connection_info *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CONNECTION_INFO); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_CONNECTION_INFO_CONN_CONNECTION_STATE, s->conn_connection_state)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_CONNECTION_INFO_CONN_ROLE, s->conn_role)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int peer_device_info_to_skb(struct sk_buff *skb, struct peer_device_info *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_PEER_DEVICE_INFO); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_REPL_STATE, s->peer_repl_state)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_DISK_STATE, s->peer_disk_state)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_USER, s->peer_resync_susp_user)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_PEER, s->peer_resync_susp_peer)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY, s->peer_resync_susp_dependency)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int resource_statistics_to_skb(struct sk_buff *skb, struct resource_statistics *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESOURCE_STATISTICS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING, s->res_stat_write_ordering)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int device_statistics_to_skb(struct sk_buff *skb, struct device_statistics *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DEVICE_STATISTICS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_SIZE, s->dev_size, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_READ, s->dev_read, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_WRITE, s->dev_write, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_AL_WRITES, s->dev_al_writes, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_BM_WRITES, s->dev_bm_writes, 0)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DEVICE_STATISTICS_DEV_UPPER_PENDING, s->dev_upper_pending)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DEVICE_STATISTICS_DEV_LOWER_PENDING, s->dev_lower_pending)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DEVICE_STATISTICS_DEV_UPPER_BLOCKED, s->dev_upper_blocked)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DEVICE_STATISTICS_DEV_LOWER_BLOCKED, s->dev_lower_blocked)) + goto nla_put_failure; + if (nla_put_u8(skb, DRBD_A_DEVICE_STATISTICS_DEV_AL_SUSPENDED, s->dev_al_suspended)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_EXPOSED_DATA_UUID, s->dev_exposed_data_uuid, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_CURRENT_UUID, s->dev_current_uuid, 0)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DEVICE_STATISTICS_DEV_DISK_FLAGS, s->dev_disk_flags)) + goto nla_put_failure; + if (nla_put(skb, DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS, min_t(int, DRBD_NL_HISTORY_UUIDS_SIZE, + s->history_uuids_len), s->history_uuids)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int connection_statistics_to_skb(struct sk_buff *skb, struct connection_statistics *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CONNECTION_STATISTICS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u8(skb, DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED, s->conn_congested)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int peer_device_statistics_to_skb(struct sk_buff *skb, struct peer_device_statistics *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_PEER_DEVICE_STATISTICS); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RECEIVED, s->peer_dev_received, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_SENT, s->peer_dev_sent, 0)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_PENDING, s->peer_dev_pending)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_UNACKED, s->peer_dev_unacked)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_OUT_OF_SYNC, s->peer_dev_out_of_sync, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RESYNC_FAILED, s->peer_dev_resync_failed, 0)) + goto nla_put_failure; + if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_BITMAP_UUID, s->peer_dev_bitmap_uuid, 0)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS, s->peer_dev_flags)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int drbd_notification_header_to_skb(struct sk_buff *skb, struct drbd_notification_header *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_NOTIFICATION_HEADER); + + if (!tla) + goto nla_put_failure; + + if (nla_put_u32(skb, DRBD_A_DRBD_NOTIFICATION_HEADER_NH_TYPE, s->nh_type)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +int drbd_helper_info_to_skb(struct sk_buff *skb, struct drbd_helper_info *s) +{ + struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_HELPER); + + if (!tla) + goto nla_put_failure; + + if (nla_put(skb, DRBD_A_DRBD_HELPER_INFO_HELPER_NAME, min_t(int, 32, + s->helper_name_len + (s->helper_name_len < 32)), s->helper_name)) + goto nla_put_failure; + if (nla_put_u32(skb, DRBD_A_DRBD_HELPER_INFO_HELPER_STATUS, s->helper_status)) + goto nla_put_failure; + + nla_nest_end(skb, tla); + return 0; + +nla_put_failure: + if (tla) + nla_nest_cancel(skb, tla); + return -EMSGSIZE; +} + +void set_disk_conf_defaults(struct disk_conf *x) +{ + x->on_io_error = DRBD_ON_IO_ERROR_DEF; + x->fencing = DRBD_FENCING_DEF; + x->resync_rate = DRBD_RESYNC_RATE_DEF; + x->resync_after = DRBD_MINOR_NUMBER_DEF; + x->al_extents = DRBD_AL_EXTENTS_DEF; + x->c_plan_ahead = DRBD_C_PLAN_AHEAD_DEF; + x->c_delay_target = DRBD_C_DELAY_TARGET_DEF; + x->c_fill_target = DRBD_C_FILL_TARGET_DEF; + x->c_max_rate = DRBD_C_MAX_RATE_DEF; + x->c_min_rate = DRBD_C_MIN_RATE_DEF; + x->disk_barrier = DRBD_DISK_BARRIER_DEF; + x->disk_flushes = DRBD_DISK_FLUSHES_DEF; + x->disk_drain = DRBD_DISK_DRAIN_DEF; + x->md_flushes = DRBD_MD_FLUSHES_DEF; + x->disk_timeout = DRBD_DISK_TIMEOUT_DEF; + x->read_balancing = DRBD_READ_BALANCING_DEF; + x->al_updates = DRBD_AL_UPDATES_DEF; + x->discard_zeroes_if_aligned = DRBD_DISCARD_ZEROES_IF_ALIGNED_DEF; + x->rs_discard_granularity = DRBD_RS_DISCARD_GRANULARITY_DEF; + x->disable_write_same = DRBD_DISABLE_WRITE_SAME_DEF; +} + +void set_res_opts_defaults(struct res_opts *x) +{ + memset(x->cpu_mask, 0, sizeof(x->cpu_mask)); + x->cpu_mask_len = 0; + x->on_no_data = DRBD_ON_NO_DATA_DEF; +} + +void set_net_conf_defaults(struct net_conf *x) +{ + memset(x->shared_secret, 0, sizeof(x->shared_secret)); + x->shared_secret_len = 0; + memset(x->cram_hmac_alg, 0, sizeof(x->cram_hmac_alg)); + x->cram_hmac_alg_len = 0; + memset(x->integrity_alg, 0, sizeof(x->integrity_alg)); + x->integrity_alg_len = 0; + memset(x->verify_alg, 0, sizeof(x->verify_alg)); + x->verify_alg_len = 0; + memset(x->csums_alg, 0, sizeof(x->csums_alg)); + x->csums_alg_len = 0; + x->wire_protocol = DRBD_PROTOCOL_DEF; + x->connect_int = DRBD_CONNECT_INT_DEF; + x->timeout = DRBD_TIMEOUT_DEF; + x->ping_int = DRBD_PING_INT_DEF; + x->ping_timeo = DRBD_PING_TIMEO_DEF; + x->sndbuf_size = DRBD_SNDBUF_SIZE_DEF; + x->rcvbuf_size = DRBD_RCVBUF_SIZE_DEF; + x->ko_count = DRBD_KO_COUNT_DEF; + x->max_buffers = DRBD_MAX_BUFFERS_DEF; + x->max_epoch_size = DRBD_MAX_EPOCH_SIZE_DEF; + x->unplug_watermark = DRBD_UNPLUG_WATERMARK_DEF; + x->after_sb_0p = DRBD_AFTER_SB_0P_DEF; + x->after_sb_1p = DRBD_AFTER_SB_1P_DEF; + x->after_sb_2p = DRBD_AFTER_SB_2P_DEF; + x->rr_conflict = DRBD_RR_CONFLICT_DEF; + x->on_congestion = DRBD_ON_CONGESTION_DEF; + x->cong_fill = DRBD_CONG_FILL_DEF; + x->cong_extents = DRBD_CONG_EXTENTS_DEF; + x->two_primaries = DRBD_ALLOW_TWO_PRIMARIES_DEF; + x->tcp_cork = DRBD_TCP_CORK_DEF; + x->always_asbp = DRBD_ALWAYS_ASBP_DEF; + x->use_rle = DRBD_USE_RLE_DEF; + x->csums_after_crash_only = DRBD_CSUMS_AFTER_CRASH_ONLY_DEF; + x->sock_check_timeo = DRBD_SOCKET_CHECK_TIMEO_DEF; +} + +void set_resize_parms_defaults(struct resize_parms *x) +{ + x->al_stripes = DRBD_AL_STRIPES_DEF; + x->al_stripe_size = DRBD_AL_STRIPE_SIZE_DEF; +} diff --git a/drivers/block/drbd/drbd_nl_gen.h b/drivers/block/drbd/drbd_nl_gen.h new file mode 100644 index 000000000000..f2140dd1ac4e --- /dev/null +++ b/drivers/block/drbd/drbd_nl_gen.h @@ -0,0 +1,395 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ + +#ifndef _LINUX_DRBD_GEN_H +#define _LINUX_DRBD_GEN_H + +#include <net/netlink.h> +#include <net/genetlink.h> + +#include <uapi/linux/drbd_genl.h> +#include <linux/drbd.h> +#include <linux/drbd_limits.h> + +/* Common nested types */ +extern const struct nla_policy drbd_connection_info_nl_policy[DRBD_A_CONNECTION_INFO_CONN_ROLE + 1]; +extern const struct nla_policy drbd_connection_statistics_nl_policy[DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1]; +extern const struct nla_policy drbd_detach_parms_nl_policy[DRBD_A_DETACH_PARMS_FORCE_DETACH + 1]; +extern const struct nla_policy drbd_device_info_nl_policy[DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1]; +extern const struct nla_policy drbd_device_statistics_nl_policy[DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1]; +extern const struct nla_policy drbd_disconnect_parms_nl_policy[DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1]; +extern const struct nla_policy drbd_disk_conf_nl_policy[DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1]; +extern const struct nla_policy drbd_drbd_cfg_context_nl_policy[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1]; +extern const struct nla_policy drbd_net_conf_nl_policy[DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1]; +extern const struct nla_policy drbd_new_c_uuid_parms_nl_policy[DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1]; +extern const struct nla_policy drbd_peer_device_info_nl_policy[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1]; +extern const struct nla_policy drbd_peer_device_statistics_nl_policy[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1]; +extern const struct nla_policy drbd_res_opts_nl_policy[DRBD_A_RES_OPTS_ON_NO_DATA + 1]; +extern const struct nla_policy drbd_resize_parms_nl_policy[DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1]; +extern const struct nla_policy drbd_resource_info_nl_policy[DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1]; +extern const struct nla_policy drbd_resource_statistics_nl_policy[DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1]; +extern const struct nla_policy drbd_set_role_parms_nl_policy[DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1]; +extern const struct nla_policy drbd_start_ov_parms_nl_policy[DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1]; + +/* Ops table for drbd */ +extern const struct genl_split_ops drbd_nl_ops[32]; + +int drbd_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb, + struct genl_info *info); +void +drbd_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb, + struct genl_info *info); +int drbd_adm_dump_devices_done(struct netlink_callback *cb); +int drbd_adm_dump_connections_done(struct netlink_callback *cb); +int drbd_adm_dump_peer_devices_done(struct netlink_callback *cb); + +int drbd_nl_get_status_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_get_status_dumpit(struct sk_buff *skb, struct netlink_callback *cb); +int drbd_nl_new_minor_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_del_minor_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_new_resource_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_del_resource_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_resource_opts_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_connect_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_disconnect_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_attach_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_resize_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_primary_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_secondary_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_new_c_uuid_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_start_ov_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_detach_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_invalidate_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_inval_peer_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_pause_sync_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_resume_sync_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_suspend_io_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_resume_io_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_outdate_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_get_timeout_type_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_down_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_chg_disk_opts_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_chg_net_opts_doit(struct sk_buff *skb, struct genl_info *info); +int drbd_nl_get_resources_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int drbd_nl_get_devices_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int drbd_nl_get_connections_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int drbd_nl_get_peer_devices_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int drbd_nl_get_initial_state_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); + +enum { + DRBD_NLGRP_EVENTS, +}; + +struct drbd_cfg_reply { + char info_text[0]; + __u32 info_text_len; +}; + +struct drbd_cfg_context { + __u32 ctx_volume; + char ctx_resource_name[128]; + __u32 ctx_resource_name_len; + char ctx_my_addr[128]; + __u32 ctx_my_addr_len; + char ctx_peer_addr[128]; + __u32 ctx_peer_addr_len; +}; + +struct disk_conf { + char backing_dev[128]; + __u32 backing_dev_len; + char meta_dev[128]; + __u32 meta_dev_len; + __s32 meta_dev_idx; + __u64 disk_size; + __u32 max_bio_bvecs; + __u32 on_io_error; + __u32 fencing; + __u32 resync_rate; + __s32 resync_after; + __u32 al_extents; + __u32 c_plan_ahead; + __u32 c_delay_target; + __u32 c_fill_target; + __u32 c_max_rate; + __u32 c_min_rate; + unsigned char disk_barrier; + unsigned char disk_flushes; + unsigned char disk_drain; + unsigned char md_flushes; + __u32 disk_timeout; + __u32 read_balancing; + unsigned char al_updates; + unsigned char discard_zeroes_if_aligned; + __u32 rs_discard_granularity; + unsigned char disable_write_same; +}; + +struct res_opts { + char cpu_mask[DRBD_CPU_MASK_SIZE]; + __u32 cpu_mask_len; + __u32 on_no_data; +}; + +struct net_conf { + char shared_secret[SHARED_SECRET_MAX]; + __u32 shared_secret_len; + char cram_hmac_alg[SHARED_SECRET_MAX]; + __u32 cram_hmac_alg_len; + char integrity_alg[SHARED_SECRET_MAX]; + __u32 integrity_alg_len; + char verify_alg[SHARED_SECRET_MAX]; + __u32 verify_alg_len; + char csums_alg[SHARED_SECRET_MAX]; + __u32 csums_alg_len; + __u32 wire_protocol; + __u32 connect_int; + __u32 timeout; + __u32 ping_int; + __u32 ping_timeo; + __u32 sndbuf_size; + __u32 rcvbuf_size; + __u32 ko_count; + __u32 max_buffers; + __u32 max_epoch_size; + __u32 unplug_watermark; + __u32 after_sb_0p; + __u32 after_sb_1p; + __u32 after_sb_2p; + __u32 rr_conflict; + __u32 on_congestion; + __u32 cong_fill; + __u32 cong_extents; + unsigned char two_primaries; + unsigned char discard_my_data; + unsigned char tcp_cork; + unsigned char always_asbp; + unsigned char tentative; + unsigned char use_rle; + unsigned char csums_after_crash_only; + __u32 sock_check_timeo; +}; + +struct set_role_parms { + unsigned char assume_uptodate; +}; + +struct resize_parms { + __u64 resize_size; + unsigned char resize_force; + unsigned char no_resync; + __u32 al_stripes; + __u32 al_stripe_size; +}; + +struct state_info { + __u32 sib_reason; + __u32 current_state; + __u64 capacity; + __u64 ed_uuid; + __u32 prev_state; + __u32 new_state; + char uuids[DRBD_NL_UUIDS_SIZE]; + __u32 uuids_len; + __u32 disk_flags; + __u64 bits_total; + __u64 bits_oos; + __u64 bits_rs_total; + __u64 bits_rs_failed; + char helper[32]; + __u32 helper_len; + __u32 helper_exit_code; + __u64 send_cnt; + __u64 recv_cnt; + __u64 read_cnt; + __u64 writ_cnt; + __u64 al_writ_cnt; + __u64 bm_writ_cnt; + __u32 ap_bio_cnt; + __u32 ap_pending_cnt; + __u32 rs_pending_cnt; +}; + +struct start_ov_parms { + __u64 ov_start_sector; + __u64 ov_stop_sector; +}; + +struct new_c_uuid_parms { + unsigned char clear_bm; +}; + +struct timeout_parms { + __u32 timeout_type; +}; + +struct disconnect_parms { + unsigned char force_disconnect; +}; + +struct detach_parms { + unsigned char force_detach; +}; + +struct resource_info { + __u32 res_role; + unsigned char res_susp; + unsigned char res_susp_nod; + unsigned char res_susp_fen; +}; + +struct device_info { + __u32 dev_disk_state; +}; + +struct connection_info { + __u32 conn_connection_state; + __u32 conn_role; +}; + +struct peer_device_info { + __u32 peer_repl_state; + __u32 peer_disk_state; + __u32 peer_resync_susp_user; + __u32 peer_resync_susp_peer; + __u32 peer_resync_susp_dependency; +}; + +struct resource_statistics { + __u32 res_stat_write_ordering; +}; + +struct device_statistics { + __u64 dev_size; + __u64 dev_read; + __u64 dev_write; + __u64 dev_al_writes; + __u64 dev_bm_writes; + __u32 dev_upper_pending; + __u32 dev_lower_pending; + unsigned char dev_upper_blocked; + unsigned char dev_lower_blocked; + unsigned char dev_al_suspended; + __u64 dev_exposed_data_uuid; + __u64 dev_current_uuid; + __u32 dev_disk_flags; + char history_uuids[DRBD_NL_HISTORY_UUIDS_SIZE]; + __u32 history_uuids_len; +}; + +struct connection_statistics { + unsigned char conn_congested; +}; + +struct peer_device_statistics { + __u64 peer_dev_received; + __u64 peer_dev_sent; + __u32 peer_dev_pending; + __u32 peer_dev_unacked; + __u64 peer_dev_out_of_sync; + __u64 peer_dev_resync_failed; + __u64 peer_dev_bitmap_uuid; + __u32 peer_dev_flags; +}; + +struct drbd_notification_header { + __u32 nh_type; +}; + +struct drbd_helper_info { + char helper_name[32]; + __u32 helper_name_len; + __u32 helper_status; +}; + +int drbd_cfg_reply_to_skb(struct sk_buff *skb, struct drbd_cfg_reply *s); + +int drbd_cfg_context_from_attrs(struct drbd_cfg_context *s, struct genl_info *info); +int drbd_cfg_context_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int drbd_cfg_context_to_skb(struct sk_buff *skb, struct drbd_cfg_context *s); + +int disk_conf_from_attrs(struct disk_conf *s, struct genl_info *info); +int disk_conf_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int disk_conf_to_skb(struct sk_buff *skb, struct disk_conf *s); +void set_disk_conf_defaults(struct disk_conf *x); + +int res_opts_from_attrs(struct res_opts *s, struct genl_info *info); +int res_opts_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int res_opts_to_skb(struct sk_buff *skb, struct res_opts *s); +void set_res_opts_defaults(struct res_opts *x); + +int net_conf_from_attrs(struct net_conf *s, struct genl_info *info); +int net_conf_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int net_conf_to_skb(struct sk_buff *skb, struct net_conf *s); +void set_net_conf_defaults(struct net_conf *x); + +int set_role_parms_from_attrs(struct set_role_parms *s, struct genl_info *info); +int set_role_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int set_role_parms_to_skb(struct sk_buff *skb, struct set_role_parms *s); + +int resize_parms_from_attrs(struct resize_parms *s, struct genl_info *info); +int resize_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int resize_parms_to_skb(struct sk_buff *skb, struct resize_parms *s); +void set_resize_parms_defaults(struct resize_parms *x); + +int state_info_to_skb(struct sk_buff *skb, struct state_info *s); + +int start_ov_parms_from_attrs(struct start_ov_parms *s, struct genl_info *info); +int start_ov_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int start_ov_parms_to_skb(struct sk_buff *skb, struct start_ov_parms *s); + +int new_c_uuid_parms_from_attrs(struct new_c_uuid_parms *s, struct genl_info *info); +int new_c_uuid_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int new_c_uuid_parms_to_skb(struct sk_buff *skb, struct new_c_uuid_parms *s); + +int timeout_parms_to_skb(struct sk_buff *skb, struct timeout_parms *s); + +int disconnect_parms_from_attrs(struct disconnect_parms *s, struct genl_info *info); +int disconnect_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int disconnect_parms_to_skb(struct sk_buff *skb, struct disconnect_parms *s); + +int detach_parms_from_attrs(struct detach_parms *s, struct genl_info *info); +int detach_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int detach_parms_to_skb(struct sk_buff *skb, struct detach_parms *s); + +int resource_info_from_attrs(struct resource_info *s, struct genl_info *info); +int resource_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int resource_info_to_skb(struct sk_buff *skb, struct resource_info *s); + +int device_info_from_attrs(struct device_info *s, struct genl_info *info); +int device_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int device_info_to_skb(struct sk_buff *skb, struct device_info *s); + +int connection_info_from_attrs(struct connection_info *s, struct genl_info *info); +int connection_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int connection_info_to_skb(struct sk_buff *skb, struct connection_info *s); + +int peer_device_info_from_attrs(struct peer_device_info *s, struct genl_info *info); +int peer_device_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int peer_device_info_to_skb(struct sk_buff *skb, struct peer_device_info *s); + +int resource_statistics_from_attrs(struct resource_statistics *s, struct genl_info *info); +int resource_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int resource_statistics_to_skb(struct sk_buff *skb, struct resource_statistics *s); + +int device_statistics_from_attrs(struct device_statistics *s, struct genl_info *info); +int device_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int device_statistics_to_skb(struct sk_buff *skb, struct device_statistics *s); + +int connection_statistics_from_attrs(struct connection_statistics *s, struct genl_info *info); +int connection_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int connection_statistics_to_skb(struct sk_buff *skb, struct connection_statistics *s); + +int peer_device_statistics_from_attrs(struct peer_device_statistics *s, struct genl_info *info); +int peer_device_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info); +int peer_device_statistics_to_skb(struct sk_buff *skb, struct peer_device_statistics *s); + +int drbd_notification_header_to_skb(struct sk_buff *skb, struct drbd_notification_header *s); + +int drbd_helper_info_to_skb(struct sk_buff *skb, struct drbd_helper_info *s); + +#endif /* _LINUX_DRBD_GEN_H */ diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c index 1d0feafceadc..6d0c12c10260 100644 --- a/drivers/block/drbd/drbd_proc.c +++ b/drivers/block/drbd/drbd_proc.c @@ -228,7 +228,7 @@ int drbd_seq_show(struct seq_file *seq, void *v) }; seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n", - GENL_MAGIC_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag()); + DRBD_FAMILY_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag()); /* cs .. connection state diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 0509746f8aed..dca495be0683 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -5012,8 +5012,8 @@ MODULE_LICENSE("GPL"); /* This doesn't actually get used other than for module information */ static const struct pnp_device_id floppy_pnpids[] = { - {"PNP0700", 0}, - {} + { .id = "PNP0700" }, + { } }; MODULE_DEVICE_TABLE(pnp, floppy_pnpids); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 0000913f7efc..310de0463beb 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -342,23 +342,19 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, { struct iov_iter iter; struct req_iterator rq_iter; - struct bio_vec *bvec; struct request *rq = blk_mq_rq_from_pdu(cmd); - struct bio *bio = rq->bio; struct file *file = lo->lo_backing_file; - struct bio_vec tmp; - unsigned int offset; unsigned int nr_bvec; int ret; nr_bvec = blk_rq_nr_bvec(rq); if (rq->bio != rq->biotail) { + struct bio_vec tmp, *bvec; - bvec = kmalloc_objs(struct bio_vec, nr_bvec, GFP_NOIO); - if (!bvec) + cmd->bvec = kmalloc_objs(*cmd->bvec, nr_bvec, GFP_NOIO); + if (!cmd->bvec) return -EIO; - cmd->bvec = bvec; /* * The bios of the request may be started from the middle of @@ -366,26 +362,26 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, * copy bio->bi_iov_vec to new bvec. The rq_for_each_bvec * API will take care of all details for us. */ + bvec = cmd->bvec; rq_for_each_bvec(tmp, rq, rq_iter) { *bvec = tmp; bvec++; } - bvec = cmd->bvec; - offset = 0; + iov_iter_bvec(&iter, rw, cmd->bvec, nr_bvec, blk_rq_bytes(rq)); + iter.iov_offset = 0; } else { /* * Same here, this bio may be started from the middle of the * 'bvec' because of bio splitting, so offset from the bvec * must be passed to iov iterator */ - offset = bio->bi_iter.bi_bvec_done; - bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter); + iov_iter_bvec(&iter, rw, + __bvec_iter_bvec(rq->bio->bi_io_vec, rq->bio->bi_iter), + nr_bvec, blk_rq_bytes(rq)); + iter.iov_offset = rq->bio->bi_iter.bi_bvec_done; } atomic_set(&cmd->ref, 2); - iov_iter_bvec(&iter, rw, bvec, nr_bvec, blk_rq_bytes(rq)); - iter.iov_offset = offset; - cmd->iocb.ki_pos = pos; cmd->iocb.ki_filp = file; cmd->iocb.ki_ioprio = req_get_ioprio(rq); diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 567192e371a8..f214a616386c 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -3340,7 +3340,7 @@ static void mtip_free_cmd(struct blk_mq_tag_set *set, struct request *rq, } static int mtip_init_cmd(struct blk_mq_tag_set *set, struct request *rq, - unsigned int hctx_idx, unsigned int numa_node) + unsigned int hctx_idx, int numa_node) { struct driver_data *dd = set->driver_data; struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); @@ -3405,6 +3405,7 @@ static int mtip_block_initialize(struct driver_data *dd) .max_segment_size = 0x400000, }; int rv = 0, wait_for_rebuild = 0; + bool disk_added = false; sector_t capacity; unsigned int index = 0; @@ -3438,6 +3439,7 @@ static int mtip_block_initialize(struct driver_data *dd) dev_err(&dd->pdev->dev, "Unable to allocate request queue\n"); rv = -ENOMEM; + dd->disk = NULL; goto block_queue_alloc_init_error; } dd->queue = dd->disk->queue; @@ -3496,6 +3498,7 @@ skip_create_disk: rv = device_add_disk(&dd->pdev->dev, dd->disk, mtip_disk_attr_groups); if (rv) goto read_capacity_error; + disk_added = true; if (dd->mtip_svc_handler) { set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); @@ -3511,7 +3514,9 @@ start_service_thread: dev_err(&dd->pdev->dev, "service thread failed to start\n"); dd->mtip_svc_handler = NULL; rv = -EFAULT; - goto kthread_run_error; + if (disk_added) + goto kthread_run_error; + goto read_capacity_error; } wake_up_process(dd->mtip_svc_handler); if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) @@ -3522,6 +3527,10 @@ start_service_thread: kthread_run_error: /* Delete our gendisk. This also removes the device from /dev */ del_gendisk(dd->disk); + mtip_hw_debugfs_exit(dd); + blk_mq_free_tag_set(&dd->tags); + mtip_hw_exit(dd); + return rv; read_capacity_error: init_hw_cmds_error: mtip_hw_debugfs_exit(dd); @@ -3529,6 +3538,7 @@ disk_index_error: ida_free(&rssd_index_ida, index); ida_get_error: put_disk(dd->disk); + dd->disk = NULL; block_queue_alloc_init_error: blk_mq_free_tag_set(&dd->tags); block_queue_alloc_tag_error: @@ -3839,7 +3849,10 @@ msi_initialize_err: } iomap_err: - kfree(dd); + if (dd->disk) + put_disk(dd->disk); + else + kfree(dd); pci_set_drvdata(pdev, NULL); return rv; done: diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c index b9fdeff31caf..328da73b6f2c 100644 --- a/drivers/block/n64cart.c +++ b/drivers/block/n64cart.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/string.h> enum { PI_DRAM_REG = 0, @@ -145,7 +146,7 @@ static int __init n64cart_probe(struct platform_device *pdev) disk->flags = GENHD_FL_NO_PART; disk->fops = &n64cart_fops; disk->private_data = &pdev->dev; - strcpy(disk->disk_name, "n64cart"); + strscpy(disk->disk_name, "n64cart"); set_capacity(disk, size >> SECTOR_SHIFT); set_disk_ro(disk, 1); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index fe63f3c55d0d..3a585a0c882a 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1238,6 +1238,42 @@ static struct socket *nbd_get_socket(struct nbd_device *nbd, unsigned long fd, return sock; } +#ifdef CONFIG_DEBUG_LOCK_ALLOC +static struct lock_class_key nbd_key[3]; +static struct lock_class_key nbd_slock_key[3]; + +static void nbd_reclassify_socket(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (WARN_ON_ONCE(!sock_allow_reclassification(sk))) + return; + + switch (sk->sk_family) { + case AF_INET: + sock_lock_init_class_and_name(sk, "slock-AF_INET-NBD", + &nbd_slock_key[0], + "sk_lock-AF_INET-NBD", + &nbd_key[0]); + break; + case AF_INET6: + sock_lock_init_class_and_name(sk, "slock-AF_INET6-NBD", + &nbd_slock_key[1], + "sk_lock-AF_INET6-NBD", + &nbd_key[1]); + break; + case AF_UNIX: + sock_lock_init_class_and_name(sk, "slock-AF_UNIX-NBD", + &nbd_slock_key[2], + "sk_lock-AF_UNIX-NBD", + &nbd_key[2]); + break; + } +} +#else +static inline void nbd_reclassify_socket(struct socket *sock) {} +#endif + static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg, bool netlink) { @@ -1254,6 +1290,7 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg, sock = nbd_get_socket(nbd, arg, &err); if (!sock) return err; + nbd_reclassify_socket(sock); /* * We need to make sure we don't get any errant requests while we're @@ -1888,7 +1925,7 @@ static void nbd_dbg_close(void) #endif static int nbd_init_request(struct blk_mq_tag_set *set, struct request *rq, - unsigned int hctx_idx, unsigned int numa_node) + unsigned int hctx_idx, int numa_node) { struct nbd_cmd *cmd = blk_mq_rq_to_pdu(rq); cmd->nbd = set->driver_data; diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 6c1e7347e6a7..90c13de47cff 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3672,7 +3672,7 @@ static void __rbd_lock(struct rbd_device *rbd_dev, const char *cookie) struct rbd_client_id cid = rbd_get_cid(rbd_dev); rbd_dev->lock_state = RBD_LOCK_STATE_LOCKED; - strcpy(rbd_dev->lock_cookie, cookie); + strscpy(rbd_dev->lock_cookie, cookie); rbd_set_owner_cid(rbd_dev, &cid); queue_work(rbd_dev->task_wq, &rbd_dev->acquired_lock_work); } @@ -6082,12 +6082,9 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, /* * Make sure the reported number of snapshot ids wouldn't go - * beyond the end of our buffer. But before checking that, - * make sure the computed size of the snapshot context we - * allocate is representable in a size_t. + * beyond the end of our buffer. */ - if (snap_count > (SIZE_MAX - sizeof (struct ceph_snap_context)) - / sizeof (u64)) { + if (snap_count > RBD_MAX_SNAP_COUNT) { ret = -EINVAL; goto out; } diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 6c041eaebdb9..4f6d9e652187 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -359,7 +359,6 @@ static void ublk_buf_cleanup(struct ublk_device *ub); static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq); static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub, u16 q_id, u16 tag, struct ublk_io *io); -static inline unsigned int ublk_req_build_flags(struct request *req); static void ublk_batch_dispatch(struct ublk_queue *ubq, const struct ublk_batch_io_data *data, struct ublk_batch_fetch_cmd *fcmd); @@ -471,6 +470,62 @@ static inline bool ublk_dev_support_integrity(const struct ublk_device *ub) return ub->dev_info.flags & UBLK_F_INTEGRITY; } +static inline unsigned int ublk_req_build_flags(struct request *req) +{ + unsigned flags = 0; + + if (req->cmd_flags & REQ_FAILFAST_DEV) + flags |= UBLK_IO_F_FAILFAST_DEV; + + if (req->cmd_flags & REQ_FAILFAST_TRANSPORT) + flags |= UBLK_IO_F_FAILFAST_TRANSPORT; + + if (req->cmd_flags & REQ_FAILFAST_DRIVER) + flags |= UBLK_IO_F_FAILFAST_DRIVER; + + if (req->cmd_flags & REQ_META) + flags |= UBLK_IO_F_META; + + if (req->cmd_flags & REQ_FUA) + flags |= UBLK_IO_F_FUA; + + if (req->cmd_flags & REQ_NOUNMAP) + flags |= UBLK_IO_F_NOUNMAP; + + if (req->cmd_flags & REQ_SWAP) + flags |= UBLK_IO_F_SWAP; + + if (blk_integrity_rq(req)) + flags |= UBLK_IO_F_INTEGRITY; + + return flags; +} + +static void ublk_init_iod(struct ublk_queue *ubq, struct request *req, + uint8_t ublk_op, uint32_t nr_sectors, + uint64_t start_sector) +{ + struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag); + struct ublk_io *io = &ubq->ios[req->tag]; + + iod->op_flags = ublk_op | ublk_req_build_flags(req); + iod->nr_sectors = nr_sectors; + iod->start_sector = start_sector; + + /* Try shmem zero-copy match before setting addr */ + if (ublk_support_shmem_zc(ubq) && blk_rq_has_data(req)) { + u32 buf_idx, buf_off; + + if (ublk_try_buf_match(ubq->dev, req, &buf_idx, &buf_off)) { + iod->op_flags |= UBLK_IO_F_SHMEM_ZC; + iod->addr = ublk_shmem_zc_addr(buf_idx, buf_off); + return; + } + } + + iod->addr = io->buf.addr; +} + #ifdef CONFIG_BLK_DEV_ZONED struct ublk_zoned_report_desc { @@ -652,8 +707,6 @@ out: static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq, struct request *req) { - struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag); - struct ublk_io *io = &ubq->ios[req->tag]; struct ublk_zoned_report_desc *desc; u32 ublk_op; @@ -683,9 +736,8 @@ static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq, ublk_op = desc->operation; switch (ublk_op) { case UBLK_IO_OP_REPORT_ZONES: - iod->op_flags = ublk_op | ublk_req_build_flags(req); - iod->nr_zones = desc->nr_zones; - iod->start_sector = desc->sector; + ublk_init_iod(ubq, req, ublk_op, desc->nr_zones, + desc->sector); return BLK_STS_OK; default: return BLK_STS_IOERR; @@ -697,11 +749,7 @@ static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq, return BLK_STS_IOERR; } - iod->op_flags = ublk_op | ublk_req_build_flags(req); - iod->nr_sectors = blk_rq_sectors(req); - iod->start_sector = blk_rq_pos(req); - iod->addr = io->buf.addr; - + ublk_init_iod(ubq, req, ublk_op, blk_rq_sectors(req), blk_rq_pos(req)); return BLK_STS_OK; } @@ -1189,11 +1237,6 @@ static inline struct ublk_queue *ublk_get_queue(struct ublk_device *dev, return dev->queues[qid]; } -static inline bool ublk_rq_has_data(const struct request *rq) -{ - return bio_has_data(rq->bio); -} - static inline struct ublksrv_io_desc * ublk_queue_cmd_buf(struct ublk_device *ub, int q_id) { @@ -1406,12 +1449,12 @@ static size_t ublk_copy_user_integrity(const struct request *req, static inline bool ublk_need_map_req(const struct request *req) { - return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE; + return blk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE; } static inline bool ublk_need_unmap_req(const struct request *req) { - return ublk_rq_has_data(req) && + return blk_rq_has_data(req) && (req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN); } @@ -1460,41 +1503,8 @@ static unsigned int ublk_unmap_io(bool need_map, return rq_bytes; } -static inline unsigned int ublk_req_build_flags(struct request *req) -{ - unsigned flags = 0; - - if (req->cmd_flags & REQ_FAILFAST_DEV) - flags |= UBLK_IO_F_FAILFAST_DEV; - - if (req->cmd_flags & REQ_FAILFAST_TRANSPORT) - flags |= UBLK_IO_F_FAILFAST_TRANSPORT; - - if (req->cmd_flags & REQ_FAILFAST_DRIVER) - flags |= UBLK_IO_F_FAILFAST_DRIVER; - - if (req->cmd_flags & REQ_META) - flags |= UBLK_IO_F_META; - - if (req->cmd_flags & REQ_FUA) - flags |= UBLK_IO_F_FUA; - - if (req->cmd_flags & REQ_NOUNMAP) - flags |= UBLK_IO_F_NOUNMAP; - - if (req->cmd_flags & REQ_SWAP) - flags |= UBLK_IO_F_SWAP; - - if (blk_integrity_rq(req)) - flags |= UBLK_IO_F_INTEGRITY; - - return flags; -} - static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req) { - struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag); - struct ublk_io *io = &ubq->ios[req->tag]; u32 ublk_op; switch (req_op(req)) { @@ -1519,25 +1529,7 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req) return BLK_STS_IOERR; } - /* need to translate since kernel may change */ - iod->op_flags = ublk_op | ublk_req_build_flags(req); - iod->nr_sectors = blk_rq_sectors(req); - iod->start_sector = blk_rq_pos(req); - - /* Try shmem zero-copy match before setting addr */ - if (ublk_support_shmem_zc(ubq) && ublk_rq_has_data(req)) { - u32 buf_idx, buf_off; - - if (ublk_try_buf_match(ubq->dev, req, - &buf_idx, &buf_off)) { - iod->op_flags |= UBLK_IO_F_SHMEM_ZC; - iod->addr = ublk_shmem_zc_addr(buf_idx, buf_off); - return BLK_STS_OK; - } - } - - iod->addr = io->buf.addr; - + ublk_init_iod(ubq, req, ublk_op, blk_rq_sectors(req), blk_rq_pos(req)); return BLK_STS_OK; } @@ -1815,7 +1807,7 @@ static void ublk_dispatch_req(struct ublk_queue *ubq, struct request *req) if (!ublk_start_io(ubq, req, io)) return; - if (ublk_support_auto_buf_reg(ubq) && ublk_rq_has_data(req)) { + if (ublk_support_auto_buf_reg(ubq) && blk_rq_has_data(req)) { ublk_auto_buf_dispatch(ubq, req, io, io->cmd, issue_flags); } else { ublk_init_req_ref(ubq, io); @@ -1836,7 +1828,7 @@ static bool __ublk_batch_prep_dispatch(struct ublk_queue *ubq, if (!ublk_start_io(ubq, req, io)) return false; - if (ublk_support_auto_buf_reg(ubq) && ublk_rq_has_data(req)) { + if (ublk_support_auto_buf_reg(ubq) && blk_rq_has_data(req)) { res = ublk_auto_buf_register(ubq, req, io, cmd, data->issue_flags); @@ -2735,23 +2727,27 @@ static void ublk_start_cancel(struct ublk_device *ub) { struct gendisk *disk = ublk_get_disk(ub); - /* Our disk has been dead */ - if (!disk) - return; - mutex_lock(&ub->cancel_mutex); if (ub->canceling) goto out; - /* - * Now we are serialized with ublk_queue_rq() - * - * Make sure that ubq->canceling is set when queue is frozen, - * because ublk_queue_rq() has to rely on this flag for avoiding to - * touch completed uring_cmd - */ - blk_mq_quiesce_queue(disk->queue); - ublk_set_canceling(ub, true); - blk_mq_unquiesce_queue(disk->queue); + + if (disk) { + /* + * Quiesce to serialize with ublk_queue_rq(), ensuring + * ubq->canceling is visible when the queue resumes. + */ + blk_mq_quiesce_queue(disk->queue); + ublk_set_canceling(ub, true); + blk_mq_unquiesce_queue(disk->queue); + } else { + /* + * Disk not yet allocated by ublk_ctrl_start_dev(), so + * there is no request queue and ublk_queue_rq() cannot + * be running. Just set the flag; if start_dev proceeds + * later, new I/O will see canceling and be aborted. + */ + ublk_set_canceling(ub, true); + } out: mutex_unlock(&ub->cancel_mutex); ublk_put_disk(disk); @@ -3227,7 +3223,7 @@ ublk_daemon_register_io_buf(struct io_uring_cmd *cmd, return ublk_register_io_buf(cmd, ub, q_id, tag, io, index, issue_flags); - if (!ublk_dev_support_zero_copy(ub) || !ublk_rq_has_data(req)) + if (!ublk_dev_support_zero_copy(ub) || !blk_rq_has_data(req)) return -EINVAL; ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index, @@ -3510,7 +3506,7 @@ static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub, if (unlikely(!blk_mq_request_started(req) || req->tag != tag)) goto fail_put; - if (!ublk_rq_has_data(req)) + if (!blk_rq_has_data(req)) goto fail_put; return req; @@ -4085,7 +4081,7 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir) return -EINVAL; req = io->req; - if (!ublk_rq_has_data(req)) + if (!blk_rq_has_data(req)) return -EINVAL; } else { req = __ublk_check_and_get_req(ub, q_id, tag, io); diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index b1c9a27fe00f..32bf3ba07a9d 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -689,6 +689,8 @@ static int virtblk_report_zones(struct gendisk *disk, sector_t sector, nz = min_t(u64, virtio64_to_cpu(vblk->vdev, report->nr_zones), nr_zones); + nz = min_t(u64, nz, + (buflen - sizeof(*report)) / sizeof(report->zones[0])); if (!nz) break; diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index aebc710f0d6a..e11ee1ed3832 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -33,6 +33,7 @@ #include <linux/cpuhotplug.h> #include <linux/part_stat.h> #include <linux/kernel_read_file.h> +#include <linux/rcupdate.h> #include "zram_drv.h" @@ -504,6 +505,7 @@ struct zram_wb_ctl { wait_queue_head_t done_wait; spinlock_t done_lock; atomic_t num_inflight; + struct rcu_head rcu; }; struct zram_wb_req { @@ -847,7 +849,7 @@ static void release_wb_ctl(struct zram_wb_ctl *wb_ctl) release_wb_req(req); } - kfree(wb_ctl); + kfree_rcu(wb_ctl, rcu); } static struct zram_wb_ctl *init_wb_ctl(struct zram *zram) @@ -964,11 +966,13 @@ static void zram_writeback_endio(struct bio *bio) struct zram_wb_ctl *wb_ctl = bio->bi_private; unsigned long flags; + rcu_read_lock(); spin_lock_irqsave(&wb_ctl->done_lock, flags); list_add(&req->entry, &wb_ctl->done_reqs); spin_unlock_irqrestore(&wb_ctl->done_lock, flags); wake_up(&wb_ctl->done_wait); + rcu_read_unlock(); } static void zram_submit_wb_request(struct zram *zram, @@ -2333,7 +2337,7 @@ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec, if (!page) return -ENOMEM; - ret = zram_read_page(zram, page, index, bio); + ret = zram_read_page(zram, page, index, NULL); if (!ret) { memcpy_from_bvec(page_address(page) + offset, bvec); ret = zram_write_page(zram, page, index); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7f5fce93d984..830fefb342c6 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3540,7 +3540,13 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev, "firmware rome 0x%x build 0x%x", rver_rom, rver_patch, ver_rom, ver_patch); - if (rver_rom != ver_rom || rver_patch <= ver_patch) { + /* Allow rampatch when the patch version equals the firmware version. + * A firmware download may be aborted by a transient USB error (e.g. + * disconnect) after the controller updates version info but before + * completion. + * Allowing equal versions enables re-flashing during recovery. + */ + if (rver_rom != ver_rom || rver_patch < ver_patch) { bt_dev_err(hdev, "rampatch file version did not match with firmware"); err = -EINVAL; goto done; diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index ed280399bf47..34500137df2c 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1680,8 +1680,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code) mod_timer(&qca->tx_idle_timer, jiffies + msecs_to_jiffies(qca->tx_idle_delay)); - /* Controller reset completion time is 50ms */ - msleep(50); + /* Wait for the controller to load the rampatch and NVM. */ + msleep(100); clear_bit(QCA_SSR_TRIGGERED, &qca->flags); clear_bit(QCA_IBS_DISABLED, &qca->flags); diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 3181d8aa32a3..e4b1db809187 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -216,7 +216,7 @@ config TI_SYSC config TS_NBUS tristate "Technologic Systems NBUS Driver" depends on SOC_IMX28 - depends on OF_GPIO && PWM + depends on PWM help Driver for the Technologic Systems NBUS which is used to interface with the peripherals in the FPGA of the TS-4600 SoM. diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index f735e0462c55..87070155b057 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -327,12 +327,6 @@ static int of_weim_notify(struct notifier_block *nb, unsigned long action, "Failed to setup timing for '%pOF'\n", rd->dn); if (!of_node_check_flag(rd->dn, OF_POPULATED)) { - /* - * Clear the flag before adding the device so that - * fw_devlink doesn't skip adding consumers to this - * device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { dev_err(&pdev->dev, "Failed to create child device '%pOF'\n", diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 9196dc50a48d..d3d230247262 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -156,8 +156,6 @@ static int cdx_unregister_device(struct device *dev, } else { cdx_destroy_res_attr(cdx_dev, MAX_CDX_DEV_RESOURCES); debugfs_remove_recursive(cdx_dev->debugfs_dir); - kfree(cdx_dev->driver_override); - cdx_dev->driver_override = NULL; } /* @@ -268,6 +266,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) const struct cdx_driver *cdx_drv = to_cdx_driver(drv); const struct cdx_device_id *found_id = NULL; const struct cdx_device_id *ids; + int ret; if (cdx_dev->is_bus) return false; @@ -275,7 +274,8 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) ids = cdx_drv->match_id_table; /* When driver_override is set, only bind to the matching driver */ - if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name)) + ret = device_match_driver_override(dev, drv); + if (ret == 0) return false; found_id = cdx_match_id(ids, cdx_dev); @@ -289,7 +289,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) */ if (!found_id->override_only) return true; - if (cdx_dev->driver_override) + if (ret > 0) return true; ids = found_id + 1; @@ -453,36 +453,6 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(modalias); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - int ret; - - if (WARN_ON(dev->bus != &cdx_bus_type)) - return -EINVAL; - - ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", cdx_dev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -552,7 +522,6 @@ static struct attribute *cdx_dev_attrs[] = { &dev_attr_class.attr, &dev_attr_revision.attr, &dev_attr_modalias.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -646,6 +615,7 @@ ATTRIBUTE_GROUPS(cdx_bus); const struct bus_type cdx_bus_type = { .name = "cdx", + .driver_override = true, .match = cdx_bus_match, .probe = cdx_probe, .remove = cdx_remove, diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 0d25bbdc7e6a..4d0b7d7c0aad 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -27,6 +27,7 @@ #include <linux/gfp.h> #include <linux/page-flags.h> #include <linux/mm.h> +#include <asm/cpuid/api.h> #include "agp.h" #include "intel-agp.h" diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 492a2a61a65b..a5bcef4a54ee 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -373,6 +373,16 @@ config HW_RANDOM_HISTB To compile this driver as a module, choose M here: the module will be called histb-rng. +config HW_RANDOM_HISI_TRNG + tristate "HiSilicon True Random Number Generator support" + depends on ARM64 && ACPI + help + This driver provides kernel-side support for the True Random Number + Generator hardware found on some HiSilicon SoCs. + + To compile this driver as a module, choose M here: the module will be + called hisi-trng-v2. + config HW_RANDOM_ST tristate "ST Microelectronics HW Random Number Generator support" depends on ARCH_STI || COMPILE_TEST @@ -615,6 +625,17 @@ config HW_RANDOM_ROCKCHIP If unsure, say Y. +config HW_RANDOM_XILINX + tristate "Support for Xilinx True Random Generator" + depends on ZYNQMP_FIRMWARE || COMPILE_TEST + select CRYPTO_LIB_SHA512 + help + Xilinx Versal SoC driver provides kernel-side support for True Random Number + Generator and Pseudo random Number in CTR_DRBG mode as defined in NIST SP800-90A. + + To compile this driver as a module, choose M here: the module + will be called xilinx-trng. + endif # HW_RANDOM config UML_RANDOM diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index b9132b3f5d21..95b5adb49560 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o obj-$(CONFIG_HW_RANDOM_HISTB) += histb-rng.o +obj-$(CONFIG_HW_RANDOM_HISI_TRNG) += hisi-trng-v2.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o obj-$(CONFIG_HW_RANDOM_BCM74110) += bcm74110-rng.o obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o @@ -52,3 +53,4 @@ obj-$(CONFIG_HW_RANDOM_CN10K) += cn10k-rng.o obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o obj-$(CONFIG_HW_RANDOM_JH7110) += jh7110-trng.o +obj-$(CONFIG_HW_RANDOM_XILINX) += xilinx-trng.o diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c index 332d594bdf70..dff80daae587 100644 --- a/drivers/char/hw_random/amd-rng.c +++ b/drivers/char/hw_random/amd-rng.c @@ -47,9 +47,9 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_VDEVICE(AMD, 0x7443), 0, }, - { PCI_VDEVICE(AMD, 0x746b), 0, }, - { 0, }, /* terminate list */ + { PCI_VDEVICE(AMD, 0x7443) }, + { PCI_VDEVICE(AMD, 0x746b) }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/char/hw_random/cavium-rng.c b/drivers/char/hw_random/cavium-rng.c index d9d7b6038c06..3e2c042009f6 100644 --- a/drivers/char/hw_random/cavium-rng.c +++ b/drivers/char/hw_random/cavium-rng.c @@ -73,8 +73,8 @@ static void cavium_rng_remove(struct pci_dev *pdev) } static const struct pci_device_id cavium_rng_pf_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa018), 0, 0, 0}, /* Thunder RNM */ - {0,}, + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa018) }, /* Thunder RNM */ + { }, }; MODULE_DEVICE_TABLE(pci, cavium_rng_pf_id_table); diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index aba92d777f72..6931657ad2ca 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -25,12 +25,13 @@ #include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/sysfs.h> #include <linux/uaccess.h> #include <linux/workqueue.h> #define RNG_MODULE_NAME "hw_random" -#define RNG_BUFFER_SIZE (SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES) +#define RNG_BUFFER_SIZE MAX(32, SMP_CACHE_BYTES) static struct hwrng __rcu *current_rng; /* the current rng has been explicitly chosen by user via sysfs */ @@ -54,13 +55,9 @@ module_param(default_quality, ushort, 0644); MODULE_PARM_DESC(default_quality, "default maximum entropy content of hwrng per 1024 bits of input"); -static void drop_current_rng(void); static int hwrng_init(struct hwrng *rng); static int hwrng_fillfn(void *unused); -static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, - int wait); - static size_t rng_buffer_size(void) { return RNG_BUFFER_SIZE; @@ -214,8 +211,8 @@ static int rng_dev_open(struct inode *inode, struct file *filp) return 0; } -static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, - int wait) { +static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, bool wait) +{ int present; BUG_ON(!mutex_is_locked(&reading_mutex)); @@ -418,21 +415,17 @@ static ssize_t rng_available_show(struct device *dev, struct device_attribute *attr, char *buf) { - int err; struct hwrng *rng; + int len = 0; - err = mutex_lock_interruptible(&rng_mutex); - if (err) + if (mutex_lock_interruptible(&rng_mutex)) return -ERESTARTSYS; - buf[0] = '\0'; - list_for_each_entry(rng, &rng_list, list) { - strlcat(buf, rng->name, PAGE_SIZE); - strlcat(buf, " ", PAGE_SIZE); - } - strlcat(buf, "none\n", PAGE_SIZE); + list_for_each_entry(rng, &rng_list, list) + len += sysfs_emit_at(buf, len, "%s ", rng->name); + len += sysfs_emit_at(buf, len, "none\n"); mutex_unlock(&rng_mutex); - return strlen(buf); + return len; } static ssize_t rng_selected_show(struct device *dev, @@ -538,8 +531,7 @@ static int hwrng_fillfn(void *unused) } mutex_lock(&reading_mutex); - rc = rng_get_data(rng, rng_fillbuf, - rng_buffer_size(), 1); + rc = rng_get_data(rng, rng_fillbuf, rng_buffer_size(), true); if (current_quality != rng->quality) rng->quality = current_quality; /* obsolete */ quality = rng->quality; diff --git a/drivers/char/hw_random/geode-rng.c b/drivers/char/hw_random/geode-rng.c index 1b21d58e1768..ae63eff64344 100644 --- a/drivers/char/hw_random/geode-rng.c +++ b/drivers/char/hw_random/geode-rng.c @@ -46,8 +46,8 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES), 0, }, - { 0, }, /* terminate list */ + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES) }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/char/hw_random/hisi-trng-v2.c b/drivers/char/hw_random/hisi-trng-v2.c new file mode 100644 index 000000000000..6584ed051e09 --- /dev/null +++ b/drivers/char/hw_random/hisi-trng-v2.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 HiSilicon Limited. */ + +#include <linux/acpi.h> +#include <linux/err.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/random.h> + +#define HISI_TRNG_REG 0x00F0 +#define HISI_TRNG_BYTES 4 +#define HISI_TRNG_QUALITY 512 +#define SLEEP_US 10 +#define TIMEOUT_US 10000 + +struct hisi_trng { + void __iomem *base; + struct hwrng rng; +}; + +static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct hisi_trng *trng; + int currsize = 0; + u32 val = 0; + int ret; + + trng = container_of(rng, struct hisi_trng, rng); + + do { + ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, + val, SLEEP_US, TIMEOUT_US); + if (ret) + return currsize; + + if (max - currsize >= HISI_TRNG_BYTES) { + memcpy(buf + currsize, &val, HISI_TRNG_BYTES); + currsize += HISI_TRNG_BYTES; + if (currsize == max) + return currsize; + continue; + } + + /* copy remaining bytes */ + memcpy(buf + currsize, &val, max - currsize); + currsize = max; + } while (currsize < max); + + return currsize; +} + +static int hisi_trng_probe(struct platform_device *pdev) +{ + struct hisi_trng *trng; + int ret; + + trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); + if (!trng) + return -ENOMEM; + + trng->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(trng->base)) + return PTR_ERR(trng->base); + + trng->rng.name = pdev->name; + trng->rng.read = hisi_trng_read; + trng->rng.quality = HISI_TRNG_QUALITY; + + ret = devm_hwrng_register(&pdev->dev, &trng->rng); + if (ret) + dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret); + return ret; +} + +static const struct acpi_device_id hisi_trng_acpi_match[] = { + { "HISI02B3", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); + +static struct platform_driver hisi_trng_driver = { + .probe = hisi_trng_probe, + .driver = { + .name = "hisi-trng-v2", + .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), + }, +}; + +module_platform_driver(hisi_trng_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); +MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); +MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c index 9776f4daa044..4712c3c530e4 100644 --- a/drivers/char/hw_random/jh7110-trng.c +++ b/drivers/char/hw_random/jh7110-trng.c @@ -256,19 +256,22 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai if (wait) { ret = starfive_trng_wait_idle(trng); - if (ret) - return -ETIMEDOUT; + if (ret) { + ret = -ETIMEDOUT; + goto out_put; + } } ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait); if (ret) - return ret; + goto out_put; memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max); + ret = max; +out_put: pm_runtime_put_sync_autosuspend(trng->dev); - - return max; + return ret; } static int starfive_trng_probe(struct platform_device *pdev) diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c index 5808d09d12c4..1f9793010e6c 100644 --- a/drivers/char/hw_random/mtk-rng.c +++ b/drivers/char/hw_random/mtk-rng.c @@ -3,9 +3,11 @@ * Driver for Mediatek Hardware Random Number Generator * * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org> */ #define MTK_RNG_DEV KBUILD_MODNAME +#include <linux/arm-smccc.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> @@ -17,6 +19,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/soc/mediatek/mtk_sip_svc.h> /* Runtime PM autosuspend timeout: */ #define RNG_AUTOSUSPEND_TIMEOUT 100 @@ -30,6 +33,11 @@ #define RNG_DATA 0x08 +/* Driver feature flags */ +#define MTK_RNG_SMC BIT(0) + +#define MTK_SIP_KERNEL_GET_RND MTK_SIP_SMC_CMD(0x550) + #define to_mtk_rng(p) container_of(p, struct mtk_rng, rng) struct mtk_rng { @@ -37,6 +45,7 @@ struct mtk_rng { struct clk *clk; struct hwrng rng; struct device *dev; + unsigned long flags; }; static int mtk_rng_init(struct hwrng *rng) @@ -103,6 +112,56 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) return retval || !wait ? retval : -EIO; } +static int mtk_rng_read_smc(struct hwrng *rng, void *buf, size_t max, + bool wait) +{ + struct arm_smccc_res res; + int retval = 0; + + while (max >= sizeof(u32)) { + arm_smccc_smc(MTK_SIP_KERNEL_GET_RND, 0, 0, 0, 0, 0, 0, 0, + &res); + if (res.a0) + break; + + *(u32 *)buf = res.a1; + retval += sizeof(u32); + buf += sizeof(u32); + max -= sizeof(u32); + } + + return retval || !wait ? retval : -EIO; +} + +static bool mtk_rng_hw_accessible(struct mtk_rng *priv) +{ + u32 val; + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return false; + + val = readl(priv->base + RNG_CTRL); + val |= RNG_EN; + writel(val, priv->base + RNG_CTRL); + + val = readl(priv->base + RNG_CTRL); + + if (val & RNG_EN) { + /* HW is accessible, clean up: disable RNG and clock */ + writel(val & ~RNG_EN, priv->base + RNG_CTRL); + clk_disable_unprepare(priv->clk); + return true; + } + + /* + * If TF-A blocks direct access, the register reads back as 0. + * Leave the clock enabled as TF-A needs it. + */ + return false; +} + static int mtk_rng_probe(struct platform_device *pdev) { int ret; @@ -114,23 +173,42 @@ static int mtk_rng_probe(struct platform_device *pdev) priv->dev = &pdev->dev; priv->rng.name = pdev->name; -#ifndef CONFIG_PM - priv->rng.init = mtk_rng_init; - priv->rng.cleanup = mtk_rng_cleanup; -#endif - priv->rng.read = mtk_rng_read; priv->rng.quality = 900; - - priv->clk = devm_clk_get(&pdev->dev, "rng"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - dev_err(&pdev->dev, "no clock for device: %d\n", ret); - return ret; + priv->flags = (unsigned long)device_get_match_data(&pdev->dev); + + if (!(priv->flags & MTK_RNG_SMC)) { + priv->clk = devm_clk_get(&pdev->dev, "rng"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "no clock for device: %d\n", ret); + return ret; + } + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + if (IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) && + of_device_is_compatible(pdev->dev.of_node, + "mediatek,mt7986-rng") && + !mtk_rng_hw_accessible(priv)) { + priv->flags |= MTK_RNG_SMC; + dev_info(&pdev->dev, + "HW RNG not MMIO accessible, using SMC\n"); + } } - priv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); + if (priv->flags & MTK_RNG_SMC) { + if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC)) + return -ENODEV; + priv->rng.read = mtk_rng_read_smc; + } else { +#ifndef CONFIG_PM + priv->rng.init = mtk_rng_init; + priv->rng.cleanup = mtk_rng_cleanup; +#endif + priv->rng.read = mtk_rng_read; + } ret = devm_hwrng_register(&pdev->dev, &priv->rng); if (ret) { @@ -139,12 +217,15 @@ static int mtk_rng_probe(struct platform_device *pdev) return ret; } - dev_set_drvdata(&pdev->dev, priv); - pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT); - pm_runtime_use_autosuspend(&pdev->dev); - ret = devm_pm_runtime_enable(&pdev->dev); - if (ret) - return ret; + if (!(priv->flags & MTK_RNG_SMC)) { + dev_set_drvdata(&pdev->dev, priv); + pm_runtime_set_autosuspend_delay(&pdev->dev, + RNG_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; + } dev_info(&pdev->dev, "registered RNG driver\n"); @@ -181,8 +262,11 @@ static const struct dev_pm_ops mtk_rng_pm_ops = { #endif /* CONFIG_PM */ static const struct of_device_id mtk_rng_match[] = { - { .compatible = "mediatek,mt7986-rng" }, { .compatible = "mediatek,mt7623-rng" }, + { .compatible = "mediatek,mt7981-rng", .data = (void *)MTK_RNG_SMC }, + { .compatible = "mediatek,mt7986-rng" }, + { .compatible = "mediatek,mt7987-rng", .data = (void *)MTK_RNG_SMC }, + { .compatible = "mediatek,mt7988-rng", .data = (void *)MTK_RNG_SMC }, {}, }; MODULE_DEVICE_TABLE(of, mtk_rng_match); @@ -200,4 +284,5 @@ module_platform_driver(mtk_rng_driver); MODULE_DESCRIPTION("Mediatek Random Number Generator Driver"); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/s390-trng.c b/drivers/char/hw_random/s390-trng.c index 3024d5e9fd61..5520f66274b3 100644 --- a/drivers/char/hw_random/s390-trng.c +++ b/drivers/char/hw_random/s390-trng.c @@ -20,6 +20,7 @@ #include <linux/atomic.h> #include <linux/random.h> #include <linux/sched/signal.h> +#include <linux/slab.h> #include <asm/debug.h> #include <asm/cpacf.h> #include <asm/archrandom.h> @@ -67,7 +68,7 @@ static ssize_t trng_read(struct file *file, char __user *ubuf, */ if (nbytes > sizeof(buf)) { - p = (u8 *) __get_free_page(GFP_KERNEL); + p = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!p) return -ENOMEM; } @@ -94,7 +95,7 @@ static ssize_t trng_read(struct file *file, char __user *ubuf, } if (p != buf) - free_page((unsigned long) p); + kfree(p); DEBUG_DBG("trng_read()=%zd\n", ret); return ret; diff --git a/drivers/crypto/xilinx/xilinx-trng.c b/drivers/char/hw_random/xilinx-trng.c index 5276ac2d82bb..f615d5adddde 100644 --- a/drivers/crypto/xilinx/xilinx-trng.c +++ b/drivers/char/hw_random/xilinx-trng.c @@ -4,9 +4,9 @@ * Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. */ +#include <crypto/sha2.h> #include <linux/bitfield.h> #include <linux/clk.h> -#include <linux/crypto.h> #include <linux/delay.h> #include <linux/firmware/xlnx-zynqmp.h> #include <linux/hw_random.h> @@ -14,14 +14,8 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/mod_devicetable.h> #include <linux/platform_device.h> -#include <crypto/aes.h> -#include <crypto/df_sp80090a.h> -#include <crypto/internal/drbg.h> -#include <crypto/internal/cipher.h> -#include <crypto/internal/rng.h> /* TRNG Registers Offsets */ #define TRNG_STATUS_OFFSET 0x4U @@ -47,7 +41,6 @@ /* Sizes in bytes */ #define TRNG_SEED_LEN_BYTES 48U -#define TRNG_ENTROPY_SEED_LEN_BYTES 64U #define TRNG_SEC_STRENGTH_SHIFT 5U #define TRNG_SEC_STRENGTH_BYTES BIT(TRNG_SEC_STRENGTH_SHIFT) #define TRNG_BYTES_PER_REG 4U @@ -59,18 +52,9 @@ struct xilinx_rng { void __iomem *rng_base; struct device *dev; - unsigned char *scratchpadbuf; - struct aes_enckey *aeskey; - struct mutex lock; /* Protect access to TRNG device */ struct hwrng trng; }; -struct xilinx_rng_ctx { - struct xilinx_rng *rng; -}; - -static struct xilinx_rng *xilinx_rng_dev; - static void xtrng_readwrite32(void __iomem *addr, u32 mask, u8 value) { u32 val; @@ -183,29 +167,30 @@ static void xtrng_enable_entropy(struct xilinx_rng *rng) static int xtrng_reseed_internal(struct xilinx_rng *rng) { - u8 entropy[TRNG_ENTROPY_SEED_LEN_BYTES]; - struct drbg_string data; - LIST_HEAD(seedlist); + static const u8 default_salt[SHA512_DIGEST_SIZE]; + u8 entropy[SHA512_DIGEST_SIZE] __aligned(4); u32 val; int ret; - drbg_string_fill(&data, entropy, TRNG_SEED_LEN_BYTES); - list_add_tail(&data.list, &seedlist); - memset(entropy, 0, sizeof(entropy)); xtrng_enable_entropy(rng); - /* collect random data to use it as entropy (input for DF) */ + /* Collect some output from the TRNG. */ + static_assert(sizeof(entropy) >= TRNG_SEED_LEN_BYTES); ret = xtrng_collect_random_data(rng, entropy, TRNG_SEED_LEN_BYTES, true); if (ret != TRNG_SEED_LEN_BYTES) return -EINVAL; - ret = crypto_drbg_ctr_df(rng->aeskey, rng->scratchpadbuf, - TRNG_SEED_LEN_BYTES, &seedlist, AES_BLOCK_SIZE, - TRNG_SEED_LEN_BYTES); - if (ret) - return ret; + /* Extract entropy from the TRNG output using HKDF-SHA512-Extract. */ + hmac_sha512_usingrawkey(default_salt, sizeof(default_salt), entropy, + TRNG_SEED_LEN_BYTES, entropy); + + /* Write the extracted entropy to the hardware. */ xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET, - (u32 *)rng->scratchpadbuf, TRNG_NUM_INIT_REGS); + (u32 *)entropy, TRNG_NUM_INIT_REGS); + + /* Clear the entropy from the stack. */ + memzero_explicit(entropy, sizeof(entropy)); + /* select reseed operation */ iowrite32(TRNG_CTRL_PRNGXS_MASK, rng->rng_base + TRNG_CTRL_OFFSET); @@ -246,71 +231,25 @@ static int xtrng_random_bytes_generate(struct xilinx_rng *rng, u8 *rand_buf_ptr, return nbytes; } -static int xtrng_trng_generate(struct crypto_rng *tfm, const u8 *src, u32 slen, - u8 *dst, u32 dlen) -{ - struct xilinx_rng_ctx *ctx = crypto_rng_ctx(tfm); - int ret; - - mutex_lock(&ctx->rng->lock); - ret = xtrng_random_bytes_generate(ctx->rng, dst, dlen, true); - mutex_unlock(&ctx->rng->lock); - - return ret < 0 ? ret : 0; -} - -static int xtrng_trng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen) -{ - return 0; -} - -static int xtrng_trng_init(struct crypto_tfm *rtfm) -{ - struct xilinx_rng_ctx *ctx = crypto_tfm_ctx(rtfm); - - ctx->rng = xilinx_rng_dev; - - return 0; -} - -static struct rng_alg xtrng_trng_alg = { - .generate = xtrng_trng_generate, - .seed = xtrng_trng_seed, - .seedsize = 0, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "xilinx-trng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct xilinx_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = xtrng_trng_init, - }, -}; - static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) { u8 buf[TRNG_SEC_STRENGTH_BYTES]; struct xilinx_rng *rng; - int ret = -EINVAL, i = 0; + int ret = 0, i = 0; rng = container_of(hwrng, struct xilinx_rng, trng); - /* Return in case wait not set and lock not available. */ - if (!mutex_trylock(&rng->lock) && !wait) - return 0; - else if (!mutex_is_locked(&rng->lock) && wait) - mutex_lock(&rng->lock); - while (i < max) { ret = xtrng_random_bytes_generate(rng, buf, TRNG_SEC_STRENGTH_BYTES, wait); - if (ret < 0) + if (ret < 0) { + if (i == 0) + return ret; break; + } memcpy(data + i, buf, min_t(int, ret, (max - i))); i += min_t(int, ret, (max - i)); } - mutex_unlock(&rng->lock); - - return ret; + return i; } static int xtrng_hwrng_register(struct hwrng *trng) @@ -335,7 +274,6 @@ static void xtrng_hwrng_unregister(struct hwrng *trng) static int xtrng_probe(struct platform_device *pdev) { struct xilinx_rng *rng; - size_t sb_size; int ret; rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); @@ -349,46 +287,21 @@ static int xtrng_probe(struct platform_device *pdev) return PTR_ERR(rng->rng_base); } - rng->aeskey = devm_kzalloc(&pdev->dev, sizeof(*rng->aeskey), GFP_KERNEL); - if (!rng->aeskey) - return -ENOMEM; - - sb_size = crypto_drbg_ctr_df_datalen(TRNG_SEED_LEN_BYTES, AES_BLOCK_SIZE); - rng->scratchpadbuf = devm_kzalloc(&pdev->dev, sb_size, GFP_KERNEL); - if (!rng->scratchpadbuf) { - ret = -ENOMEM; - goto end; - } - xtrng_trng_reset(rng->rng_base); ret = xtrng_reseed_internal(rng); if (ret) { dev_err(&pdev->dev, "TRNG Seed fail\n"); - goto end; - } - - xilinx_rng_dev = rng; - mutex_init(&rng->lock); - ret = crypto_register_rng(&xtrng_trng_alg); - if (ret) { - dev_err(&pdev->dev, "Crypto Random device registration failed: %d\n", ret); - goto end; + return ret; } ret = xtrng_hwrng_register(&rng->trng); if (ret) { dev_err(&pdev->dev, "HWRNG device registration failed: %d\n", ret); - goto crypto_rng_free; + return ret; } platform_set_drvdata(pdev, rng); return 0; - -crypto_rng_free: - crypto_unregister_rng(&xtrng_trng_alg); - -end: - return ret; } static void xtrng_remove(struct platform_device *pdev) @@ -398,13 +311,11 @@ static void xtrng_remove(struct platform_device *pdev) rng = platform_get_drvdata(pdev); xtrng_hwrng_unregister(&rng->trng); - crypto_unregister_rng(&xtrng_trng_alg); xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET, zero, TRNG_NUM_INIT_REGS); xtrng_write_multiple_registers(rng->rng_base + TRNG_PER_STRNG_OFFSET, zero, TRNG_NUM_INIT_REGS); xtrng_hold_reset(rng->rng_base); - xilinx_rng_dev = NULL; } static const struct of_device_id xtrng_of_match[] = { diff --git a/drivers/clk/qcom/dispcc-sc8280xp.c b/drivers/clk/qcom/dispcc-sc8280xp.c index e91dfed0f37e..acc927c2142a 100644 --- a/drivers/clk/qcom/dispcc-sc8280xp.c +++ b/drivers/clk/qcom/dispcc-sc8280xp.c @@ -977,7 +977,7 @@ static struct clk_rcg2 disp0_cc_mdss_mdp_clk_src = { .name = "disp0_cc_mdss_mdp_clk_src", .parent_data = disp0_cc_parent_data_5, .num_parents = ARRAY_SIZE(disp0_cc_parent_data_5), - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_shared_no_init_park_ops, }, }; @@ -991,7 +991,7 @@ static struct clk_rcg2 disp1_cc_mdss_mdp_clk_src = { .name = "disp1_cc_mdss_mdp_clk_src", .parent_data = disp1_cc_parent_data_5, .num_parents = ARRAY_SIZE(disp1_cc_parent_data_5), - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_shared_no_init_park_ops, }, }; diff --git a/drivers/clk/qcom/dispcc-x1e80100.c b/drivers/clk/qcom/dispcc-x1e80100.c index aa7fd43969f9..cd45bedf2649 100644 --- a/drivers/clk/qcom/dispcc-x1e80100.c +++ b/drivers/clk/qcom/dispcc-x1e80100.c @@ -580,7 +580,7 @@ static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { .parent_data = disp_cc_parent_data_6, .num_parents = ARRAY_SIZE(disp_cc_parent_data_6), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_shared_no_init_park_ops, }, }; diff --git a/drivers/clk/samsung/clk-gs101.c b/drivers/clk/samsung/clk-gs101.c index d2bcd3a9daf8..b44bb31f38b3 100644 --- a/drivers/clk/samsung/clk-gs101.c +++ b/drivers/clk/samsung/clk-gs101.c @@ -3921,7 +3921,7 @@ static const unsigned long peric0_clk_regs[] __initconst = { CLK_CON_DIV_DIV_CLK_PERIC0_USI4_USI, CLK_CON_DIV_DIV_CLK_PERIC0_USI5_USI, CLK_CON_DIV_DIV_CLK_PERIC0_USI6_USI, - CLK_CON_DIV_DIV_CLK_PERIC0_USI6_USI, + CLK_CON_DIV_DIV_CLK_PERIC0_USI7_USI, CLK_CON_DIV_DIV_CLK_PERIC0_USI8_USI, CLK_CON_BUF_CLKBUF_PERIC0_IP, CLK_CON_GAT_CLK_BLK_PERIC0_UID_PERIC0_CMU_PERIC0_IPCLKPORT_PCLK, diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index d1a33a231a44..d9c76dd443f8 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -793,4 +793,35 @@ config RTK_SYSTIMER this option only when building for a Realtek platform or for compilation testing. +choice + prompt "NXP clocksource for scheduler clock" + depends on SOC_VF610 || ARCH_S32 + # Default to Global Timer for Vybrid (32-bit) + default VF_USE_ARM_GLOBAL_TIMER if SOC_VF610 + # Default to None for S32 (64-bit) + default VF_TIMER_NONE if ARCH_S32 + + config VF_USE_ARM_GLOBAL_TIMER + bool "Use NXP Vybrid Global Timer" + depends on ARCH_MULTI_V7 && SOC_VF610 + select ARM_GLOBAL_TIMER + select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK + help + Use the NXP Vybrid Global Timer as clocksource. + + config VF_USE_PIT_TIMER + bool "Use NXP PIT timer" + select NXP_PIT_TIMER + help + Use NXP Periodic Interrupt Timer (PIT) as clocksource. + + config VF_TIMER_NONE + bool "None (Use standard Arch Timer)" + depends on ARCH_S32 + help + Do not use any specific NXP timer driver. Use the standard + ARM Architected Timer instead. + +endchoice + endmenu diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 90aeff44a276..4adf756423de 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -688,6 +688,7 @@ static void __arch_timer_setup(struct clock_event_device *clk) clk->irq = arch_timer_ppi[arch_timer_uses_ppi]; switch (arch_timer_uses_ppi) { case ARCH_TIMER_VIRT_PPI: + case ARCH_TIMER_HYP_VIRT_PPI: clk->set_state_shutdown = arch_timer_shutdown_virt; clk->set_state_oneshot_stopped = arch_timer_shutdown_virt; sne = erratum_handler(set_next_event_virt); @@ -879,7 +880,7 @@ static void __init arch_timer_banner(void) pr_info("cp15 timer running at %lu.%02luMHz (%s).\n", (unsigned long)arch_timer_rate / 1000000, (unsigned long)(arch_timer_rate / 10000) % 100, - (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) ? "virt" : "phys"); + arch_timer_ppi_names[arch_timer_uses_ppi]); } u32 arch_timer_get_rate(void) @@ -912,7 +913,8 @@ static void __init arch_counter_register(void) int width; if ((IS_ENABLED(CONFIG_ARM64) && !is_hyp_mode_available()) || - arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) { + arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI || + arch_timer_uses_ppi == ARCH_TIMER_HYP_VIRT_PPI) { if (arch_timer_counter_has_wa()) { rd = arch_counter_get_cntvct_stable; scr = raw_counter_get_cntvct_stable; @@ -1023,6 +1025,7 @@ static int __init arch_timer_register(void) ppi = arch_timer_ppi[arch_timer_uses_ppi]; switch (arch_timer_uses_ppi) { case ARCH_TIMER_VIRT_PPI: + case ARCH_TIMER_HYP_VIRT_PPI: err = request_percpu_irq(ppi, arch_timer_handler_virt, "arch_timer", arch_timer_evt); break; @@ -1090,25 +1093,34 @@ static int __init arch_timer_common_init(void) /** * arch_timer_select_ppi() - Select suitable PPI for the current system. * - * If HYP mode is available, we know that the physical timer - * has been configured to be accessible from PL1. Use it, so - * that a guest can use the virtual timer instead. + * On AArch32, if HYP mode is available, we know that the physical + * timer has been configured to be accessible from PL1. Use it, so + * that a guest can use the virtual timer instead (though KVM host + * support has long been removed). * - * On ARMv8.1 with VH extensions, the kernel runs in HYP. VHE - * accesses to CNTP_*_EL1 registers are silently redirected to - * their CNTHP_*_EL2 counterparts, and use a different PPI - * number. + * On ARMv8.1 with FEAT_VHE, the kernel runs in EL2. Accesses to + * CNTV_*_EL1 registers are silently redirected to their CNTHV_*_EL2 + * counterparts, and the timer uses a different PPI number. Similar + * thing happen when using the EL2 physical timer. Note that a bunch + * of DTs out there omit the virtual EL2 timer, so fallback gracefully + * on the physical timer. + * + * Without VHE, if no interrupt provided for virtual timer, we'll have + * to stick to the physical timer. It'd better be accessible... * - * If no interrupt provided for virtual timer, we'll have to - * stick to the physical timer. It'd better be accessible... * For arm64 we never use the secure interrupt. * * Return: a suitable PPI type for the current system. */ static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void) { - if (is_kernel_in_hyp_mode()) + if (is_kernel_in_hyp_mode()) { + if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI]) + return ARCH_TIMER_HYP_VIRT_PPI; + + pr_warn_once(FW_BUG "VHE-capable CPU without EL2 virtual timer interrupt\n"); return ARCH_TIMER_HYP_PPI; + } if (!is_hyp_mode_available() && arch_timer_ppi[ARCH_TIMER_VIRT_PPI]) return ARCH_TIMER_VIRT_PPI; @@ -1200,14 +1212,9 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table) if (ret) return ret; - arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI] = - acpi_gtdt_map_ppi(ARCH_TIMER_PHYS_NONSECURE_PPI); - - arch_timer_ppi[ARCH_TIMER_VIRT_PPI] = - acpi_gtdt_map_ppi(ARCH_TIMER_VIRT_PPI); - - arch_timer_ppi[ARCH_TIMER_HYP_PPI] = - acpi_gtdt_map_ppi(ARCH_TIMER_HYP_PPI); + /* The GTDT parser can't be bothered with the secure timer */ + for (int i = ARCH_TIMER_PHYS_NONSECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++) + arch_timer_ppi[i] = acpi_gtdt_map_ppi(i); arch_timer_populate_kvm_info(); @@ -1253,10 +1260,14 @@ int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *ts, if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC_DISCOVERY)) return -EOPNOTSUPP; - if (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) + switch (arch_timer_uses_ppi) { + case ARCH_TIMER_VIRT_PPI: + case ARCH_TIMER_HYP_VIRT_PPI: ptp_counter = KVM_PTP_VIRT_COUNTER; - else + break; + default: ptp_counter = KVM_PTP_PHYS_COUNTER; + } arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, ptp_counter, &hvc_res); diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index e9f5034a1bc8..df567795d175 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -444,6 +444,22 @@ static u64 notrace read_hv_clock_tsc_cs(struct clocksource *arg) return read_hv_clock_tsc(); } +static u64 notrace read_hv_clock_tsc_cs_snapshot(struct clocksource *arg, + struct clocksource_hw_snapshot *chs) +{ + u64 time; + + if (hv_read_tsc_page_tsc(tsc_page, &chs->hw_cycles, &time)) { + chs->hw_csid = CSID_X86_TSC; + } else { + chs->hw_cycles = 0; + chs->hw_csid = CSID_GENERIC; + time = read_hv_clock_msr(); + } + + return time; +} + static u64 noinstr read_hv_sched_clock_tsc(void) { return (read_hv_clock_tsc() - hv_sched_clock_offset) * @@ -492,18 +508,19 @@ static int hv_cs_enable(struct clocksource *cs) #endif static struct clocksource hyperv_cs_tsc = { - .name = "hyperv_clocksource_tsc_page", - .rating = 500, - .read = read_hv_clock_tsc_cs, - .mask = CLOCKSOURCE_MASK(64), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .suspend= suspend_hv_clock_tsc, - .resume = resume_hv_clock_tsc, + .name = "hyperv_clocksource_tsc_page", + .rating = 500, + .read = read_hv_clock_tsc_cs, + .read_snapshot = read_hv_clock_tsc_cs_snapshot, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .suspend = suspend_hv_clock_tsc, + .resume = resume_hv_clock_tsc, #ifdef HAVE_VDSO_CLOCKMODE_HVCLOCK - .enable = hv_cs_enable, - .vdso_clock_mode = VDSO_CLOCKMODE_HVCLOCK, + .enable = hv_cs_enable, + .vdso_clock_mode = VDSO_CLOCKMODE_HVCLOCK, #else - .vdso_clock_mode = VDSO_CLOCKMODE_NONE, + .vdso_clock_mode = VDSO_CLOCKMODE_NONE, #endif }; diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c index 1501c7db9a8e..a1669266c94d 100644 --- a/drivers/clocksource/mips-gic-timer.c +++ b/drivers/clocksource/mips-gic-timer.c @@ -198,7 +198,9 @@ static struct clocksource gic_clocksource = { .name = "GIC", .read = gic_hpt_read, .flags = CLOCK_SOURCE_IS_CONTINUOUS, +#ifdef CONFIG_GENERIC_GETTIMEOFDAY .vdso_clock_mode = VDSO_CLOCKMODE_GIC, +#endif }; static void gic_clocksource_unstable(char *reason) diff --git a/drivers/clocksource/mmio.c b/drivers/clocksource/mmio.c index cd5fbf49ac29..0fee8edb837a 100644 --- a/drivers/clocksource/mmio.c +++ b/drivers/clocksource/mmio.c @@ -21,21 +21,25 @@ u64 clocksource_mmio_readl_up(struct clocksource *c) { return (u64)readl_relaxed(to_mmio_clksrc(c)->reg); } +EXPORT_SYMBOL_GPL(clocksource_mmio_readl_up); u64 clocksource_mmio_readl_down(struct clocksource *c) { return ~(u64)readl_relaxed(to_mmio_clksrc(c)->reg) & c->mask; } +EXPORT_SYMBOL_GPL(clocksource_mmio_readl_down); u64 clocksource_mmio_readw_up(struct clocksource *c) { return (u64)readw_relaxed(to_mmio_clksrc(c)->reg); } +EXPORT_SYMBOL_GPL(clocksource_mmio_readw_up); u64 clocksource_mmio_readw_down(struct clocksource *c) { return ~(u64)readw_relaxed(to_mmio_clksrc(c)->reg) & c->mask; } +EXPORT_SYMBOL_GPL(clocksource_mmio_readw_down); /** * clocksource_mmio_init - Initialize a simple mmio based clocksource @@ -46,9 +50,9 @@ u64 clocksource_mmio_readw_down(struct clocksource *c) * @bits: Number of valid bits * @read: One of clocksource_mmio_read*() above */ -int __init clocksource_mmio_init(void __iomem *base, const char *name, - unsigned long hz, int rating, unsigned bits, - u64 (*read)(struct clocksource *)) +int clocksource_mmio_init(void __iomem *base, const char *name, + unsigned long hz, int rating, unsigned bits, + u64 (*read)(struct clocksource *)) { struct clocksource_mmio *cs; @@ -68,3 +72,4 @@ int __init clocksource_mmio_init(void __iomem *base, const char *name, return clocksource_register_hz(&cs->clksrc, hz); } +EXPORT_SYMBOL_GPL(clocksource_mmio_init); diff --git a/drivers/clocksource/timer-of.c b/drivers/clocksource/timer-of.c index 420202bf76e4..ba63433211b0 100644 --- a/drivers/clocksource/timer-of.c +++ b/drivers/clocksource/timer-of.c @@ -19,7 +19,7 @@ * * Free the irq resource */ -static __init void timer_of_irq_exit(struct of_timer_irq *of_irq) +static void timer_of_irq_exit(struct of_timer_irq *of_irq) { struct timer_of *to = container_of(of_irq, struct timer_of, of_irq); @@ -41,8 +41,8 @@ static __init void timer_of_irq_exit(struct of_timer_irq *of_irq) * * Returns 0 on success, < 0 otherwise */ -static __init int timer_of_irq_init(struct device_node *np, - struct of_timer_irq *of_irq) +static int timer_of_irq_init(struct device_node *np, + struct of_timer_irq *of_irq) { int ret; struct timer_of *to = container_of(of_irq, struct timer_of, of_irq); @@ -82,7 +82,7 @@ static __init int timer_of_irq_init(struct device_node *np, * * Disables and releases the refcount on the clk */ -static __init void timer_of_clk_exit(struct of_timer_clk *of_clk) +static void timer_of_clk_exit(struct of_timer_clk *of_clk) { of_clk->rate = 0; clk_disable_unprepare(of_clk->clk); @@ -98,8 +98,8 @@ static __init void timer_of_clk_exit(struct of_timer_clk *of_clk) * * Returns 0 on success, < 0 otherwise */ -static __init int timer_of_clk_init(struct device_node *np, - struct of_timer_clk *of_clk) +static int timer_of_clk_init(struct device_node *np, + struct of_timer_clk *of_clk) { int ret; @@ -137,13 +137,13 @@ out_clk_put: goto out; } -static __init void timer_of_base_exit(struct of_timer_base *of_base) +static void timer_of_base_exit(struct of_timer_base *of_base) { iounmap(of_base->base); } -static __init int timer_of_base_init(struct device_node *np, - struct of_timer_base *of_base) +static int timer_of_base_init(struct device_node *np, + struct of_timer_base *of_base) { of_base->base = of_base->name ? of_io_request_and_map(np, of_base->index, of_base->name) : @@ -156,7 +156,7 @@ static __init int timer_of_base_init(struct device_node *np, return 0; } -int __init timer_of_init(struct device_node *np, struct timer_of *to) +int timer_of_init(struct device_node *np, struct timer_of *to) { int ret = -EINVAL; int flags = 0; @@ -200,6 +200,7 @@ out_fail: timer_of_base_exit(&to->of_base); return ret; } +EXPORT_SYMBOL_GPL(timer_of_init); /** * timer_of_cleanup - release timer_of resources @@ -208,7 +209,7 @@ out_fail: * Release the resources that has been used in timer_of_init(). * This function should be called in init error cases */ -void __init timer_of_cleanup(struct timer_of *to) +void timer_of_cleanup(struct timer_of *to) { if (to->flags & TIMER_OF_IRQ) timer_of_irq_exit(&to->of_irq); @@ -219,3 +220,4 @@ void __init timer_of_cleanup(struct timer_of *to) if (to->flags & TIMER_OF_BASE) timer_of_base_exit(&to->of_base); } +EXPORT_SYMBOL_GPL(timer_of_cleanup); diff --git a/drivers/clocksource/timer-of.h b/drivers/clocksource/timer-of.h index 01a2c6b7db06..74a632b85b47 100644 --- a/drivers/clocksource/timer-of.h +++ b/drivers/clocksource/timer-of.h @@ -65,9 +65,8 @@ static inline unsigned long timer_of_period(struct timer_of *to) return to->of_clk.period; } -extern int __init timer_of_init(struct device_node *np, - struct timer_of *to); +int timer_of_init(struct device_node *np, struct timer_of *to); -extern void __init timer_of_cleanup(struct timer_of *to); +void timer_of_cleanup(struct timer_of *to); #endif diff --git a/drivers/clocksource/timer-rtl-otto.c b/drivers/clocksource/timer-rtl-otto.c index 6113d2fdd4de..dd236a7babee 100644 --- a/drivers/clocksource/timer-rtl-otto.c +++ b/drivers/clocksource/timer-rtl-otto.c @@ -225,7 +225,7 @@ static int rttm_enable_clocksource(struct clocksource *cs) return 0; } -struct rttm_cs rttm_cs = { +static struct rttm_cs rttm_cs = { .to = { .flags = TIMER_OF_BASE | TIMER_OF_CLOCK, }, diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index f827d3f98f60..6ab300d22621 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -18,21 +18,30 @@ #include <linux/slab.h> #include <linux/platform_device.h> -#define TIMER_IRQ_EN_REG 0x00 +#define TIMER_IRQ_EN_REG 0x00 #define TIMER_IRQ_EN(val) BIT(val) -#define TIMER_IRQ_ST_REG 0x04 -#define TIMER_CTL_REG(val) (0x20 * (val) + 0x10) +#define TIMER_IRQ_ST_REG 0x04 +#define TIMER_CTL_REG(val, offset) (0x20 * (val) + 0x10 + (offset)) #define TIMER_CTL_ENABLE BIT(0) #define TIMER_CTL_RELOAD BIT(1) #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) #define TIMER_CTL_ONESHOT BIT(7) -#define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14) -#define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18) -#define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c) -#define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20) +#define TIMER_INTVAL_LO_REG(val, offset) (0x20 * (val) + 0x14 + (offset)) +#define TIMER_INTVAL_HI_REG(val, offset) (0x20 * (val) + 0x18 + (offset)) +#define TIMER_CNTVAL_LO_REG(val, offset) (0x20 * (val) + 0x1c + (offset)) +#define TIMER_CNTVAL_HI_REG(val, offset) (0x20 * (val) + 0x20 + (offset)) #define TIMER_SYNC_TICKS 3 +/** + * struct sunxi_timer_quirks - Differences between SoC variants. + * + * @from_ctl_base_offset: offset applied from ctl register onwards + */ +struct sunxi_timer_quirks { + u32 from_ctl_base_offset; +}; + struct sun5i_timer { void __iomem *base; struct clk *clk; @@ -40,6 +49,7 @@ struct sun5i_timer { u32 ticks_per_jiffy; struct clocksource clksrc; struct clock_event_device clkevt; + const struct sunxi_timer_quirks *quirks; }; #define nb_to_sun5i_timer(x) \ @@ -57,28 +67,36 @@ struct sun5i_timer { */ static void sun5i_clkevt_sync(struct sun5i_timer *ce) { - u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1)); + u32 offset = ce->quirks->from_ctl_base_offset; + u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1, offset)); - while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) + while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1, offset))) < + TIMER_SYNC_TICKS) cpu_relax(); } static void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer) { - u32 val = readl(ce->base + TIMER_CTL_REG(timer)); - writel(val & ~TIMER_CTL_ENABLE, ce->base + TIMER_CTL_REG(timer)); + u32 offset = ce->quirks->from_ctl_base_offset; + u32 val = readl(ce->base + TIMER_CTL_REG(timer, offset)); + + writel(val & ~TIMER_CTL_ENABLE, + ce->base + TIMER_CTL_REG(timer, offset)); sun5i_clkevt_sync(ce); } static void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay) { - writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer)); + u32 offset = ce->quirks->from_ctl_base_offset; + + writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer, offset)); } static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic) { - u32 val = readl(ce->base + TIMER_CTL_REG(timer)); + u32 offset = ce->quirks->from_ctl_base_offset; + u32 val = readl(ce->base + TIMER_CTL_REG(timer, offset)); if (periodic) val &= ~TIMER_CTL_ONESHOT; @@ -86,7 +104,7 @@ static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool perio val |= TIMER_CTL_ONESHOT; writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, - ce->base + TIMER_CTL_REG(timer)); + ce->base + TIMER_CTL_REG(timer, offset)); } static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) @@ -141,8 +159,9 @@ static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) static u64 sun5i_clksrc_read(struct clocksource *clksrc) { struct sun5i_timer *cs = clksrc_to_sun5i_timer(clksrc); + u32 offset = cs->quirks->from_ctl_base_offset; - return ~readl(cs->base + TIMER_CNTVAL_LO_REG(1)); + return ~readl(cs->base + TIMER_CNTVAL_LO_REG(1, offset)); } static int sun5i_rate_cb(struct notifier_block *nb, @@ -173,12 +192,13 @@ static int sun5i_setup_clocksource(struct platform_device *pdev, unsigned long rate) { struct sun5i_timer *cs = platform_get_drvdata(pdev); + u32 offset = cs->quirks->from_ctl_base_offset; void __iomem *base = cs->base; int ret; - writel(~0, base + TIMER_INTVAL_LO_REG(1)); + writel(~0, base + TIMER_INTVAL_LO_REG(1, offset)); writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, - base + TIMER_CTL_REG(1)); + base + TIMER_CTL_REG(1, offset)); cs->clksrc.name = pdev->dev.of_node->name; cs->clksrc.rating = 340; @@ -237,6 +257,7 @@ static int sun5i_setup_clockevent(struct platform_device *pdev, static int sun5i_timer_probe(struct platform_device *pdev) { + const struct sunxi_timer_quirks *quirks; struct device *dev = &pdev->dev; struct sun5i_timer *st; struct reset_control *rstc; @@ -273,11 +294,18 @@ static int sun5i_timer_probe(struct platform_device *pdev) return -EINVAL; } + quirks = of_device_get_match_data(&pdev->dev); + if (!quirks) { + dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); + return -ENODEV; + } + st->base = timer_base; st->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); st->clk = clk; st->clk_rate_cb.notifier_call = sun5i_rate_cb; st->clk_rate_cb.next = NULL; + st->quirks = quirks; ret = devm_clk_notifier_register(dev, clk, &st->clk_rate_cb); if (ret) { @@ -286,6 +314,9 @@ static int sun5i_timer_probe(struct platform_device *pdev) } rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "failed to get reset\n"); if (rstc) reset_control_deassert(rstc); @@ -311,9 +342,27 @@ static void sun5i_timer_remove(struct platform_device *pdev) clocksource_unregister(&st->clksrc); } +static const struct sunxi_timer_quirks sun5i_sun7i_hstimer_quirks = { + .from_ctl_base_offset = 0x0, +}; + +static const struct sunxi_timer_quirks sun20i_d1_hstimer_quirks = { + .from_ctl_base_offset = 0x10, +}; + static const struct of_device_id sun5i_timer_of_match[] = { - { .compatible = "allwinner,sun5i-a13-hstimer" }, - { .compatible = "allwinner,sun7i-a20-hstimer" }, + { + .compatible = "allwinner,sun5i-a13-hstimer", + .data = &sun5i_sun7i_hstimer_quirks, + }, + { + .compatible = "allwinner,sun7i-a20-hstimer", + .data = &sun5i_sun7i_hstimer_quirks, + }, + { + .compatible = "allwinner,sun20i-d1-hstimer", + .data = &sun20i_d1_hstimer_quirks, + }, {}, }; MODULE_DEVICE_TABLE(of, sun5i_timer_of_match); diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index 355558893e5f..78600ddeb1c6 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -57,6 +57,15 @@ #define WDTUR 0x00c #define WDTUR_UNLOCK_PATTERN 0x0000c45a +#define TEGRA186_KERNEL_WDT_TIMEOUT 120 + +/* WDT security configuration registers */ +#define WDTSCR(x) (0xf02c + (x) * 4) +#define WDTSCR_SEC_WEN BIT(28) +#define WDTSCR_SEC_REN BIT(27) +#define WDTSCR_SEC_G1W BIT(9) +#define WDTSCR_SEC_G1R BIT(1) + struct tegra186_timer_soc { unsigned int num_timers; unsigned int num_wdts; @@ -75,6 +84,7 @@ struct tegra186_wdt { void __iomem *regs; unsigned int index; bool locked; + bool is_kernel_wdt; struct tegra186_tmr *tmr; }; @@ -89,7 +99,7 @@ struct tegra186_timer { struct device *dev; void __iomem *regs; - struct tegra186_wdt *wdt; + struct tegra186_wdt **wdts; struct clocksource usec; struct clocksource tsc; struct clocksource osc; @@ -149,7 +159,8 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt) u32 value; /* unmask hardware IRQ, this may have been lost across powergate */ - value = TKEIE_WDT_MASK(wdt->index, 1); + value = readl(tegra->regs + TKEIE(wdt->tmr->hwirq)); + value |= TKEIE_WDT_MASK(wdt->index, 1); writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq)); /* clear interrupt */ @@ -174,6 +185,10 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt) value &= ~WDTCR_PERIOD_MASK; value |= WDTCR_PERIOD(1); + /* enable local interrupt for kernel watchdog */ + if (wdt->is_kernel_wdt) + value |= WDTCR_LOCAL_INT_ENABLE; + /* enable system POR reset */ value |= WDTCR_SYSTEM_POR_RESET_ENABLE; @@ -211,6 +226,16 @@ static int tegra186_wdt_ping(struct watchdog_device *wdd) return 0; } +static irqreturn_t tegra186_wdt_irq(int irq, void *data) +{ + struct tegra186_wdt *wdt = data; + + tegra186_wdt_disable(wdt); + tegra186_wdt_enable(wdt); + + return IRQ_HANDLED; +} + static int tegra186_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { @@ -297,6 +322,23 @@ static const struct watchdog_ops tegra186_wdt_ops = { .get_timeleft = tegra186_wdt_get_timeleft, }; +static bool tegra186_wdt_is_accessible(struct tegra186_timer *tegra, unsigned int index) +{ + u32 value; + + value = readl_relaxed(tegra->regs + WDTSCR(index)); + + /* Check OS write access if write blocking is enabled. */ + if ((value & WDTSCR_SEC_WEN) && !(value & WDTSCR_SEC_G1W)) + return false; + + /* Check OS read access if read blocking is enabled. */ + if ((value & WDTSCR_SEC_REN) && !(value & WDTSCR_SEC_G1R)) + return false; + + return true; +} + static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra, unsigned int index) { @@ -336,10 +378,6 @@ static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra, if (err < 0) return ERR_PTR(err); - err = devm_watchdog_register_device(tegra->dev, &wdt->base); - if (err < 0) - return ERR_PTR(err); - return wdt; } @@ -421,8 +459,11 @@ static int tegra186_timer_usec_init(struct tegra186_timer *tegra) static int tegra186_timer_probe(struct platform_device *pdev) { + struct tegra186_wdt *kernel_wdt = NULL; struct device *dev = &pdev->dev; struct tegra186_timer *tegra; + unsigned int i; + int irq; int err; tegra = devm_kzalloc(dev, sizeof(*tegra), GFP_KERNEL); @@ -441,12 +482,33 @@ static int tegra186_timer_probe(struct platform_device *pdev) if (err < 0) return err; - /* create a watchdog using a preconfigured timer */ - tegra->wdt = tegra186_wdt_create(tegra, 0); - if (IS_ERR(tegra->wdt)) { - err = PTR_ERR(tegra->wdt); - dev_err(dev, "failed to create WDT: %d\n", err); - return err; + irq = err; + + tegra->wdts = devm_kcalloc(dev, tegra->soc->num_wdts, sizeof(*tegra->wdts), GFP_KERNEL); + if (!tegra->wdts) + return -ENOMEM; + + for (i = 0; i < tegra->soc->num_wdts; i++) { + if (!tegra186_wdt_is_accessible(tegra, i)) { + dev_warn(dev, "WDT%u is not accessible\n", i); + continue; + } + + tegra->wdts[i] = tegra186_wdt_create(tegra, i); + if (IS_ERR(tegra->wdts[i])) + return dev_err_probe(dev, PTR_ERR(tegra->wdts[i]), + "failed to create WDT%u\n", i); + + /* Reserve the first accessible WDT for the Kernel. */ + if (!kernel_wdt) { + kernel_wdt = tegra->wdts[i]; + kernel_wdt->is_kernel_wdt = true; + } else { + err = devm_watchdog_register_device(dev, &tegra->wdts[i]->base); + if (err < 0) + return dev_err_probe(dev, err, + "failed to register WDT%u\n", i); + } } err = tegra186_timer_tsc_init(tegra); @@ -467,8 +529,22 @@ static int tegra186_timer_probe(struct platform_device *pdev) goto unregister_osc; } + if (kernel_wdt) { + err = devm_request_irq(dev, irq, tegra186_wdt_irq, 0, + dev_name(dev), kernel_wdt); + if (err < 0) { + dev_err(dev, "failed to request kernel WDT IRQ: %d\n", err); + goto unregister_usec; + } + + tegra186_wdt_set_timeout(&kernel_wdt->base, TEGRA186_KERNEL_WDT_TIMEOUT); + tegra186_wdt_enable(kernel_wdt); + } + return 0; +unregister_usec: + clocksource_unregister(&tegra->usec); unregister_osc: clocksource_unregister(&tegra->osc); unregister_tsc: @@ -488,9 +564,14 @@ static void tegra186_timer_remove(struct platform_device *pdev) static int __maybe_unused tegra186_timer_suspend(struct device *dev) { struct tegra186_timer *tegra = dev_get_drvdata(dev); + unsigned int i; - if (watchdog_active(&tegra->wdt->base)) - tegra186_wdt_disable(tegra->wdt); + for (i = 0; i < tegra->soc->num_wdts; i++) { + struct tegra186_wdt *wdt = tegra->wdts[i]; + + if (wdt && (wdt->is_kernel_wdt || watchdog_active(&wdt->base))) + tegra186_wdt_disable(wdt); + } return 0; } @@ -498,9 +579,14 @@ static int __maybe_unused tegra186_timer_suspend(struct device *dev) static int __maybe_unused tegra186_timer_resume(struct device *dev) { struct tegra186_timer *tegra = dev_get_drvdata(dev); + unsigned int i; - if (watchdog_active(&tegra->wdt->base)) - tegra186_wdt_enable(tegra->wdt); + for (i = 0; i < tegra->soc->num_wdts; i++) { + struct tegra186_wdt *wdt = tegra->wdts[i]; + + if (wdt && (wdt->is_kernel_wdt || watchdog_active(&wdt->base))) + tegra186_wdt_enable(wdt); + } return 0; } @@ -510,12 +596,12 @@ static SIMPLE_DEV_PM_OPS(tegra186_timer_pm_ops, tegra186_timer_suspend, static const struct tegra186_timer_soc tegra186_timer = { .num_timers = 10, - .num_wdts = 3, + .num_wdts = 2, }; static const struct tegra186_timer_soc tegra234_timer = { .num_timers = 16, - .num_wdts = 3, + .num_wdts = 2, }; static const struct of_device_id tegra186_timer_of_match[] = { diff --git a/drivers/clocksource/timer-ti-dm-systimer.c b/drivers/clocksource/timer-ti-dm-systimer.c index eb0dfe4b9b7c..3804c1234522 100644 --- a/drivers/clocksource/timer-ti-dm-systimer.c +++ b/drivers/clocksource/timer-ti-dm-systimer.c @@ -226,7 +226,7 @@ static bool __init dmtimer_is_preferred(struct device_node *np) * Some omap3 boards with unreliable oscillator must not use the counter_32k * or dmtimer1 with 32 KiHz source. Additionally, the boards with unreliable * oscillator should really set counter_32k as disabled, and delete dmtimer1 - * ti,always-on property, but let's not count on it. For these quirky cases, + * ti,timer-alwon property, but let's not count on it. For these quirky cases, * we prefer using the always-on secure dmtimer12 with the internal 32 KiHz * clock as the clocksource, and any available dmtimer as clockevent. * diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 793e7cdcb1b1..bd06afb7d522 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -20,8 +20,11 @@ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> #include <linux/cpu_pm.h> #include <linux/module.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/device.h> #include <linux/err.h> @@ -29,6 +32,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/platform_data/dmtimer-omap.h> +#include <linux/sched_clock.h> #include <clocksource/timer-ti-dm.h> #include <linux/delay.h> @@ -148,6 +152,21 @@ static u32 omap_reserved_systimers; static LIST_HEAD(omap_timer_list); static DEFINE_SPINLOCK(dm_timer_lock); +struct dmtimer_clocksource { + struct clocksource dev; + struct dmtimer *timer; + unsigned int loadval; +}; + +struct omap_dm_timer_clockevent { + struct clock_event_device dev; + struct dmtimer *timer; + u32 period; +}; + +static bool omap_dm_timer_clockevent_setup; +static void __iomem *omap_dm_timer_sched_clock_counter; + enum { REQUEST_ANY = 0, REQUEST_BY_ID, @@ -1185,6 +1204,192 @@ static const struct dev_pm_ops omap_dm_timer_pm_ops = { static const struct of_device_id omap_timer_match[]; +static struct dmtimer_clocksource *omap_dm_timer_to_clocksource(struct clocksource *cs) +{ + return container_of(cs, struct dmtimer_clocksource, dev); +} + +static u64 omap_dm_timer_read_cycles(struct clocksource *cs) +{ + struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs); + struct dmtimer *timer = clksrc->timer; + + return (u64)__omap_dm_timer_read_counter(timer); +} + +static u64 notrace omap_dm_timer_read_sched_clock(void) +{ + /* Posted mode is not active here, so we can read directly */ + return readl_relaxed(omap_dm_timer_sched_clock_counter); +} + +static void omap_dm_timer_clocksource_suspend(struct clocksource *cs) +{ + struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs); + struct dmtimer *timer = clksrc->timer; + + clksrc->loadval = __omap_dm_timer_read_counter(timer); + __omap_dm_timer_stop(timer); +} + +static void omap_dm_timer_clocksource_resume(struct clocksource *cs) +{ + struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs); + struct dmtimer *timer = clksrc->timer; + + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clksrc->loadval); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR); +} + +static void omap_dm_timer_clocksource_unregister(void *data) +{ + struct clocksource *cs = data; + + clocksource_unregister(cs); +} + +static int omap_dm_timer_setup_clocksource(struct dmtimer *timer) +{ + struct device *dev = &timer->pdev->dev; + struct dmtimer_clocksource *clksrc; + int err; + + __omap_dm_timer_init_regs(timer); + + timer->reserved = 1; + + clksrc = devm_kzalloc(dev, sizeof(*clksrc), GFP_KERNEL); + if (!clksrc) + return -ENOMEM; + + clksrc->timer = timer; + + clksrc->dev.name = "omap_dm_timer"; + clksrc->dev.rating = 300; + clksrc->dev.read = omap_dm_timer_read_cycles; + clksrc->dev.mask = CLOCKSOURCE_MASK(32); + clksrc->dev.flags = CLOCK_SOURCE_IS_CONTINUOUS; + clksrc->dev.suspend = omap_dm_timer_clocksource_suspend; + clksrc->dev.resume = omap_dm_timer_clocksource_resume; + + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0); + dmtimer_write(timer, OMAP_TIMER_LOAD_REG, 0); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR); + + omap_dm_timer_sched_clock_counter = timer->func_base + _OMAP_TIMER_COUNTER_OFFSET; + sched_clock_register(omap_dm_timer_read_sched_clock, 32, timer->fclk_rate); + + err = clocksource_register_hz(&clksrc->dev, timer->fclk_rate); + if (err) + return dev_err_probe(dev, err, "Could not register as clocksource\n"); + + err = devm_add_action_or_reset(dev, omap_dm_timer_clocksource_unregister, &clksrc->dev); + if (err) + return dev_err_probe(dev, err, "Could not register clocksource_unregister action\n"); + + return 0; +} + +static struct omap_dm_timer_clockevent *to_dm_timer_clockevent(struct clock_event_device *evt) +{ + return container_of(evt, struct omap_dm_timer_clockevent, dev); +} + +static int omap_dm_timer_evt_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt); + struct dmtimer *timer = clkevt->timer; + + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0xffffffff - cycles); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST); + + return 0; +} + +static int omap_dm_timer_evt_shutdown(struct clock_event_device *evt) +{ + struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt); + struct dmtimer *timer = clkevt->timer; + + __omap_dm_timer_stop(timer); + + return 0; +} + +static int omap_dm_timer_evt_set_periodic(struct clock_event_device *evt) +{ + struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt); + struct dmtimer *timer = clkevt->timer; + + omap_dm_timer_evt_shutdown(evt); + + omap_dm_timer_set_load(&timer->cookie, clkevt->period); + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clkevt->period); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, + OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST); + + return 0; +} + +static irqreturn_t omap_dm_timer_evt_interrupt(int irq, void *dev_id) +{ + struct omap_dm_timer_clockevent *clkevt = dev_id; + struct dmtimer *timer = clkevt->timer; + + __omap_dm_timer_write_status(timer, OMAP_TIMER_INT_OVERFLOW); + + clkevt->dev.event_handler(&clkevt->dev); + + return IRQ_HANDLED; +} + +static int omap_dm_timer_setup_clockevent(struct dmtimer *timer) +{ + struct device *dev = &timer->pdev->dev; + struct omap_dm_timer_clockevent *clkevt; + int ret; + + clkevt = devm_kzalloc(dev, sizeof(*clkevt), GFP_KERNEL); + if (!clkevt) + return -ENOMEM; + + timer->reserved = 1; + clkevt->timer = timer; + + clkevt->dev.name = "omap_dm_timer"; + clkevt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + clkevt->dev.rating = 300; + clkevt->dev.set_next_event = omap_dm_timer_evt_set_next_event; + clkevt->dev.set_state_shutdown = omap_dm_timer_evt_shutdown; + clkevt->dev.set_state_periodic = omap_dm_timer_evt_set_periodic; + clkevt->dev.set_state_oneshot = omap_dm_timer_evt_shutdown; + clkevt->dev.set_state_oneshot_stopped = omap_dm_timer_evt_shutdown; + clkevt->dev.tick_resume = omap_dm_timer_evt_shutdown; + clkevt->dev.cpumask = cpu_possible_mask; + clkevt->period = 0xffffffff - DIV_ROUND_CLOSEST(timer->fclk_rate, HZ); + + __omap_dm_timer_init_regs(timer); + __omap_dm_timer_stop(timer); + __omap_dm_timer_enable_posted(timer); + + ret = devm_request_irq(dev, timer->irq, omap_dm_timer_evt_interrupt, + IRQF_TIMER, "omap_dm_timer_clockevent", clkevt); + if (ret) { + dev_err(dev, "Failed to request interrupt: %d\n", ret); + return ret; + } + + __omap_dm_timer_int_enable(timer, OMAP_TIMER_INT_OVERFLOW); + + clockevents_config_and_register(&clkevt->dev, timer->fclk_rate, + 3, + 0xffffffff); + + omap_dm_timer_clockevent_setup = true; + return 0; +} + /** * omap_dm_timer_probe - probe function called for every registered device * @pdev: pointer to current timer platform device @@ -1272,6 +1477,18 @@ static int omap_dm_timer_probe(struct platform_device *pdev) timer->pdev = pdev; + if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk)) { + if (!omap_dm_timer_sched_clock_counter) { + ret = omap_dm_timer_setup_clocksource(timer); + if (ret) + return ret; + } else if (!omap_dm_timer_clockevent_setup) { + ret = omap_dm_timer_setup_clockevent(timer); + if (ret) + return ret; + } + } + pm_runtime_enable(dev); if (!timer->reserved) { diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c index 01aafce20ef8..1f430ffc7bd9 100644 --- a/drivers/comedi/drivers/comedi_test.c +++ b/drivers/comedi/drivers/comedi_test.c @@ -274,6 +274,7 @@ static int waveform_ai_cmdtest(struct comedi_device *dev, /* Step 2a : make sure trigger sources are unique */ err |= comedi_check_trigger_is_unique(cmd->convert_src); + err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); err |= comedi_check_trigger_is_unique(cmd->stop_src); /* Step 2b : and mutually compatible */ @@ -324,10 +325,10 @@ static int waveform_ai_cmdtest(struct comedi_device *dev, arg = min(arg, rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC)); arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC); - if (cmd->scan_begin_arg == TRIG_TIMER) { + if (cmd->scan_begin_src == TRIG_TIMER) { /* limit convert_arg to keep scan_begin_arg in range */ limit = UINT_MAX / cmd->scan_end_arg; - limit = rounddown(limit, (unsigned int)NSEC_PER_SEC); + limit = rounddown(limit, (unsigned int)NSEC_PER_USEC); arg = min(arg, limit); } err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 50bd30ba3d03..0b1dac61b7b5 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -124,7 +124,8 @@ struct counter_device *counter_alloc(size_t sizeof_priv) err_dev_set_name: - counter_chrdev_remove(counter); + put_device(dev); + return NULL; err_chrdev_add: ida_free(&counter_ida, dev->id); diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 47c9b031f1b3..a441668f9e0c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -153,7 +153,7 @@ config ARM_QCOM_CPUFREQ_NVMEM If in doubt, say N. config ARM_QCOM_CPUFREQ_HW - tristate "QCOM CPUFreq HW driver" + tristate "Qualcomm CPUFreq HW driver" depends on ARCH_QCOM || COMPILE_TEST depends on COMMON_CLK help diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index a9093cd5e5d1..d6190b8dde4b 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -5,7 +5,6 @@ config X86_INTEL_PSTATE bool "Intel P state control" - depends on X86 select ACPI_PROCESSOR if ACPI select ACPI_CPPC_LIB if X86_64 && ACPI && SCHED_MC_PRIO select CPU_FREQ_GOV_PERFORMANCE @@ -36,7 +35,7 @@ config X86_PCC_CPUFREQ config X86_AMD_PSTATE bool "AMD Processor P-State driver" - depends on X86 && ACPI + depends on ACPI select ACPI_PROCESSOR select ACPI_CPPC_LIB if X86_64 select CPU_FREQ_GOV_SCHEDUTIL if SMP @@ -72,7 +71,7 @@ config X86_AMD_PSTATE_DEFAULT_MODE config X86_AMD_PSTATE_UT tristate "selftest for AMD Processor P-State driver" - depends on X86 && ACPI_PROCESSOR + depends on ACPI_PROCESSOR depends on X86_AMD_PSTATE default n help @@ -114,32 +113,6 @@ config X86_ACPI_CPUFREQ_CPB By enabling this option the acpi_cpufreq driver provides the old entry in addition to the new boost ones, for compatibility reasons. -config ELAN_CPUFREQ - tristate "AMD Elan SC400 and SC410" - depends on MELAN - help - This adds the CPUFreq driver for AMD Elan SC400 and SC410 - processors. - - You need to specify the processor maximum speed as boot - parameter: elanfreq=maxspeed (in kHz) or as module - parameter "max_freq". - - For details, take a look at <file:Documentation/cpu-freq/>. - - If in doubt, say N. - -config SC520_CPUFREQ - tristate "AMD Elan SC520" - depends on MELAN - help - This adds the CPUFreq driver for AMD Elan SC520 processor. - - For details, take a look at <file:Documentation/cpu-freq/>. - - If in doubt, say N. - - config X86_POWERNOW_K6 tristate "AMD Mobile K6-2/K6-3 PowerNow!" depends on X86_32 diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 385c9fcc65c6..6c7a39b7f8d2 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -40,8 +40,6 @@ obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o obj-$(CONFIG_X86_LONGHAUL) += longhaul.o obj-$(CONFIG_X86_E_POWERSAVER) += e_powersaver.o -obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o -obj-$(CONFIG_SC520_CPUFREQ) += sc520_freq.o obj-$(CONFIG_X86_LONGRUN) += longrun.o obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index 13a23dac477d..735b29f76438 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -302,12 +302,6 @@ static int amd_pstate_ut_epp(u32 index) cpufreq_cpu_put(policy); policy = NULL; - /* disable dynamic EPP before running test */ - if (cpudata->dynamic_epp) { - pr_debug("Dynamic EPP is enabled, disabling it\n"); - amd_pstate_clear_dynamic_epp(policy); - } - buf = (char *)__get_free_page(GFP_KERNEL); if (!buf) return -ENOMEM; @@ -327,6 +321,16 @@ static int amd_pstate_ut_epp(u32 index) orig_policy = cpudata->policy; cpudata->policy = CPUFREQ_POLICY_POWERSAVE; + /* + * Disable dynamic EPP before running test. If "orig_dynamic_epp" is + * true, the driver will do a redundant switch at the end and there + * is no need for enabling it again at the end of the test. + */ + if (cpudata->dynamic_epp) { + pr_debug("Dynamic EPP is enabled, disabling it\n"); + amd_pstate_clear_dynamic_epp(policy); + } + for (epp = 0; epp <= U8_MAX; epp++) { u8 val; diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 62b5d995281d..3a80acee9a7c 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -242,12 +242,10 @@ static int msr_update_perf(struct cpufreq_policy *policy, u8 min_perf, value = prev = READ_ONCE(cpudata->cppc_req_cached); - value &= ~(AMD_CPPC_MAX_PERF_MASK | AMD_CPPC_MIN_PERF_MASK | - AMD_CPPC_DES_PERF_MASK | AMD_CPPC_EPP_PERF_MASK); - value |= FIELD_PREP(AMD_CPPC_MAX_PERF_MASK, max_perf); - value |= FIELD_PREP(AMD_CPPC_DES_PERF_MASK, des_perf); - value |= FIELD_PREP(AMD_CPPC_MIN_PERF_MASK, min_perf); - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_MAX_PERF_MASK, &value, max_perf); + FIELD_MODIFY(AMD_CPPC_DES_PERF_MASK, &value, des_perf); + FIELD_MODIFY(AMD_CPPC_MIN_PERF_MASK, &value, min_perf); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = READ_ONCE(cpudata->perf); @@ -296,8 +294,7 @@ static int msr_set_epp(struct cpufreq_policy *policy, u8 epp) int ret; value = prev = READ_ONCE(cpudata->cppc_req_cached); - value &= ~AMD_CPPC_EPP_PERF_MASK; - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = cpudata->perf; @@ -437,8 +434,7 @@ static int shmem_set_epp(struct cpufreq_policy *policy, u8 epp) } value = READ_ONCE(cpudata->cppc_req_cached); - value &= ~AMD_CPPC_EPP_PERF_MASK; - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); WRITE_ONCE(cpudata->cppc_req_cached, value); return ret; @@ -476,7 +472,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata) if (ret) return ret; - ret = rdmsrl_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req); + ret = rdmsrq_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req); if (ret) return ret; @@ -571,12 +567,10 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf, value = prev = READ_ONCE(cpudata->cppc_req_cached); - value &= ~(AMD_CPPC_MAX_PERF_MASK | AMD_CPPC_MIN_PERF_MASK | - AMD_CPPC_DES_PERF_MASK | AMD_CPPC_EPP_PERF_MASK); - value |= FIELD_PREP(AMD_CPPC_MAX_PERF_MASK, max_perf); - value |= FIELD_PREP(AMD_CPPC_DES_PERF_MASK, des_perf); - value |= FIELD_PREP(AMD_CPPC_MIN_PERF_MASK, min_perf); - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_MAX_PERF_MASK, &value, max_perf); + FIELD_MODIFY(AMD_CPPC_DES_PERF_MASK, &value, des_perf); + FIELD_MODIFY(AMD_CPPC_MIN_PERF_MASK, &value, min_perf); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = READ_ONCE(cpudata->perf); @@ -1086,10 +1080,9 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) perf = READ_ONCE(cpudata->perf); - policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf, - cpudata->nominal_freq, - perf.lowest_perf); - policy->cpuinfo.max_freq = policy->max = cpudata->max_freq; + policy->cpuinfo.min_freq = perf_to_freq(perf, cpudata->nominal_freq, + perf.lowest_perf); + policy->cpuinfo.max_freq = cpudata->max_freq; policy->driver_data = cpudata; ret = amd_pstate_cppc_enable(policy); @@ -1428,7 +1421,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, epp = cpudata->epp_default_dc; } - if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { + if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { pr_debug("EPP cannot be set under performance policy\n"); return -EBUSY; } @@ -1915,10 +1908,9 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) perf = READ_ONCE(cpudata->perf); - policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf, - cpudata->nominal_freq, - perf.lowest_perf); - policy->cpuinfo.max_freq = policy->max = cpudata->max_freq; + policy->cpuinfo.min_freq = perf_to_freq(perf, cpudata->nominal_freq, + perf.lowest_perf); + policy->cpuinfo.max_freq = cpudata->max_freq; policy->driver_data = cpudata; ret = amd_pstate_cppc_enable(policy); diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index e4722e54387b..23e8baa05849 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -84,7 +84,6 @@ struct amd_aperf_mperf { * @hw_prefcore: check whether HW supports preferred core featue. * Only when hw_prefcore and early prefcore param are true, * AMD P-State driver supports preferred core featue. - * @epp_cached: Cached CPPC energy-performance preference value * @policy: Cpufreq policy value * @suspended: If CPU core if offlined * @epp_default_ac: Default EPP value for AC power source diff --git a/drivers/cpufreq/amd_freq_sensitivity.c b/drivers/cpufreq/amd_freq_sensitivity.c index 13fed4b9e02b..739d54dc9f2b 100644 --- a/drivers/cpufreq/amd_freq_sensitivity.c +++ b/drivers/cpufreq/amd_freq_sensitivity.c @@ -51,10 +51,8 @@ static unsigned int amd_powersave_bias_target(struct cpufreq_policy *policy, if (!policy->freq_table) return freq_next; - rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_ACTUAL, - &actual.l, &actual.h); - rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_REFERENCE, - &reference.l, &reference.h); + rdmsrq_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_ACTUAL, &actual.q); + rdmsrq_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_REFERENCE, &reference.q); actual.h &= 0x00ffffff; reference.h &= 0x00ffffff; diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 9396034167e5..638e5bf72185 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -187,10 +187,8 @@ static int apple_soc_cpufreq_set_target(struct cpufreq_policy *policy, reg &= ~priv->info->ps1_mask; reg |= pstate << priv->info->ps1_shift; - if (priv->info->has_ps2) { - reg &= ~APPLE_DVFS_CMD_PS2; - reg |= FIELD_PREP(APPLE_DVFS_CMD_PS2, pstate); - } + if (priv->info->has_ps2) + FIELD_MODIFY(APPLE_DVFS_CMD_PS2, ®, pstate); reg |= APPLE_DVFS_CMD_SET; writeq_relaxed(reg, priv->reg_base + APPLE_DVFS_CMD); diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 7e7f9dfb7a24..f6cea0c54dd9 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -660,8 +660,6 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) * Section 8.4.7.1.1.5 of ACPI 6.1 spec) */ policy->min = cppc_perf_to_khz(caps, caps->lowest_nonlinear_perf); - policy->max = cppc_perf_to_khz(caps, policy->boost_enabled ? - caps->highest_perf : caps->nominal_perf); /* * Set cpuinfo.min_freq to Lowest to make the full range of performance @@ -669,7 +667,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) * nonlinear perf */ policy->cpuinfo.min_freq = cppc_perf_to_khz(caps, caps->lowest_perf); - policy->cpuinfo.max_freq = policy->max; + policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, policy->boost_enabled ? + caps->highest_perf : caps->nominal_perf); policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu); policy->shared_type = cpu_data->shared_type; @@ -982,7 +981,34 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy, return count; } -CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited, +static int cppc_get_perf_limited_filtered(int cpu, u64 *perf_limited) +{ + struct cpufreq_policy *policy; + struct cppc_cpudata *cpu_data; + int ret; + + ret = cppc_get_perf_limited(cpu, perf_limited); + if (ret) + return ret; + + policy = cpufreq_cpu_get_raw(cpu); + if (!policy) + return -EINVAL; + + cpu_data = policy->driver_data; + + /* + * Desired Excursion is ignored when autonomous selection is + * enabled. Clear the bit to avoid exposing meaningless state + * to userspace. + */ + if (cpu_data && cpu_data->perf_ctrls.auto_sel) + *perf_limited &= ~CPPC_PERF_LIMITED_DESIRED_EXCURSION; + + return 0; +} + +CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited_filtered, cppc_set_perf_limited) cpufreq_freq_attr_ro(freqdomain_cpus); diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index fbbbe501cf2d..831102522ad6 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -355,8 +355,8 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy) min_fsb = NFORCE2_MIN_FSB; /* cpuinfo and default policy values */ - policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100; - policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100; + policy->cpuinfo.min_freq = min_fsb * fid * 100; + policy->cpuinfo.max_freq = max_fsb * fid * 100; return 0; } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 44eb1b7e7fc1..507224c9ecd3 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -130,38 +130,11 @@ struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(get_governor_parent_kobj); -static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) -{ - struct kernel_cpustat kcpustat; - u64 cur_wall_time; - u64 idle_time; - u64 busy_time; - - cur_wall_time = jiffies64_to_nsecs(get_jiffies_64()); - - kcpustat_cpu_fetch(&kcpustat, cpu); - - busy_time = kcpustat.cpustat[CPUTIME_USER]; - busy_time += kcpustat.cpustat[CPUTIME_SYSTEM]; - busy_time += kcpustat.cpustat[CPUTIME_IRQ]; - busy_time += kcpustat.cpustat[CPUTIME_SOFTIRQ]; - busy_time += kcpustat.cpustat[CPUTIME_STEAL]; - busy_time += kcpustat.cpustat[CPUTIME_NICE]; - - idle_time = cur_wall_time - busy_time; - if (wall) - *wall = div_u64(cur_wall_time, NSEC_PER_USEC); - - return div_u64(idle_time, NSEC_PER_USEC); -} - u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) { u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL); - if (idle_time == -1ULL) - return get_cpu_idle_time_jiffy(cpu, wall); - else if (!io_busy) + if (!io_busy) idle_time += get_cpu_iowait_time_us(cpu, wall); return idle_time; @@ -1397,6 +1370,40 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy) kfree(policy); } +static int cpufreq_policy_init_qos(struct cpufreq_policy *policy) +{ + unsigned int min_freq, max_freq; + int ret; + + /* Use policy->min/max set by the driver as QoS requests. */ + min_freq = max(FREQ_QOS_MIN_DEFAULT_VALUE, policy->min); + if (policy->max) + max_freq = min(FREQ_QOS_MAX_DEFAULT_VALUE, policy->max); + else + max_freq = FREQ_QOS_MAX_DEFAULT_VALUE; + + if (policy->boost_supported) { + ret = freq_qos_add_request(&policy->constraints, + &policy->boost_freq_req, + FREQ_QOS_MAX, + policy->cpuinfo.max_freq); + if (ret < 0) + return ret; + } + + ret = freq_qos_add_request(&policy->constraints, &policy->min_freq_req, + FREQ_QOS_MIN, min_freq); + if (ret < 0) + return ret; + + ret = freq_qos_add_request(&policy->constraints, &policy->max_freq_req, + FREQ_QOS_MAX, max_freq); + if (ret < 0) + return ret; + + return 0; +} + static int cpufreq_policy_online(struct cpufreq_policy *policy, unsigned int cpu, bool new_policy) { @@ -1442,6 +1449,19 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, if (ret) goto out_offline_policy; + if (new_policy) { + ret = cpufreq_policy_init_qos(policy); + if (ret < 0) + goto out_offline_policy; + } + + /* + * If the driver hasn't set policy->min/max, set them as they + * are used for clamping frequency requests. + */ + policy->min = policy->min ? policy->min : policy->cpuinfo.min_freq; + policy->max = policy->max ? policy->max : policy->cpuinfo.max_freq; + /* related_cpus should at least include policy->cpus. */ cpumask_copy(policy->related_cpus, policy->cpus); } @@ -1458,27 +1478,6 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, add_cpu_dev_symlink(policy, j, get_cpu_device(j)); } - if (policy->boost_supported) { - ret = freq_qos_add_request(&policy->constraints, - &policy->boost_freq_req, - FREQ_QOS_MAX, - policy->cpuinfo.max_freq); - if (ret < 0) - goto out_destroy_policy; - } - - ret = freq_qos_add_request(&policy->constraints, - &policy->min_freq_req, FREQ_QOS_MIN, - FREQ_QOS_MIN_DEFAULT_VALUE); - if (ret < 0) - goto out_destroy_policy; - - ret = freq_qos_add_request(&policy->constraints, - &policy->max_freq_req, FREQ_QOS_MAX, - FREQ_QOS_MAX_DEFAULT_VALUE); - if (ret < 0) - goto out_destroy_policy; - blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_CREATE_POLICY, policy); } @@ -1972,6 +1971,7 @@ void cpufreq_suspend(void) if (!cpufreq_driver) return; + cpus_read_lock(); if (!has_target() && !cpufreq_driver->suspend) goto suspend; @@ -1991,6 +1991,7 @@ void cpufreq_suspend(void) suspend: cpufreq_suspended = true; + cpus_read_unlock(); } /** @@ -2366,9 +2367,13 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, * exactly same freq is called again and so we can save on few function * calls. */ - if (target_freq == policy->cur && - !(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS)) - return 0; + if (target_freq == policy->cur) { + if (!(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS) || + !policy->update_limits) + return 0; + + policy->update_limits = false; + } if (cpufreq_driver->target) { /* @@ -2620,6 +2625,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, { struct cpufreq_policy_data new_data; struct cpufreq_governor *old_gov; + unsigned int freq; int ret; memcpy(&new_data.cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo)); @@ -2652,12 +2658,20 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, * compiler optimizations around them because they may be accessed * concurrently by cpufreq_driver_resolve_freq() during the update. */ - WRITE_ONCE(policy->max, __resolve_freq(policy, new_data.max, - new_data.min, new_data.max, - CPUFREQ_RELATION_H)); - new_data.min = __resolve_freq(policy, new_data.min, new_data.min, - new_data.max, CPUFREQ_RELATION_L); - WRITE_ONCE(policy->min, new_data.min > policy->max ? policy->max : new_data.min); + freq = __resolve_freq(policy, new_data.max, new_data.min, new_data.max, + CPUFREQ_RELATION_H); + if (freq != policy->max) { + WRITE_ONCE(policy->max, freq); + policy->update_limits = true; + } + + freq = __resolve_freq(policy, new_data.min, new_data.min, new_data.max, + CPUFREQ_RELATION_L); + freq = min(freq, policy->max); + if (freq != policy->min) { + WRITE_ONCE(policy->min, freq); + policy->update_limits = true; + } trace_cpu_frequency_limits(policy); diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index df01d33993d8..0b32ae28ec85 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -103,10 +103,6 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) if (load > dbs_data->up_threshold) { dbs_info->down_skip = 0; - /* if we are already at full speed then break out early */ - if (requested_freq == policy->max) - goto out; - requested_freq += freq_step; if (requested_freq > policy->max) requested_freq = policy->max; @@ -124,13 +120,7 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) /* Check for frequency decrease */ if (load < cs_tuners->down_threshold) { - /* - * if we cannot reduce the frequency anymore, break out early - */ - if (requested_freq == policy->min) - goto out; - - if (requested_freq > freq_step) + if (requested_freq > policy->min + freq_step) requested_freq -= freq_step; else requested_freq = policy->min; diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 86f35e451914..710d93ec89b5 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -90,7 +90,14 @@ EXPORT_SYMBOL_GPL(sampling_rate_store); * (that may be a single policy or a bunch of them if governor tunables are * system-wide). * - * Call under the @dbs_data mutex. + * Call under the @dbs_data->attr_set.update_lock. The per-policy + * update_mutex is acquired and released internally for each policy. + * + * Note: prev_cpu_nice is reset here unconditionally alongside prev_cpu_idle. + * When io_is_busy changes, both baselines must be advanced to the same + * timestamp so that the next dbs_update() computes idle_time and nice_delta + * over the same interval, preventing an artificially inflated idle_time when + * ignore_nice_load is enabled. */ void gov_update_cpu_data(struct dbs_data *dbs_data) { @@ -99,14 +106,15 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) list_for_each_entry(policy_dbs, &dbs_data->attr_set.policy_list, list) { unsigned int j; + mutex_lock(&policy_dbs->update_mutex); for_each_cpu(j, policy_dbs->policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_update_time, dbs_data->io_is_busy); - if (dbs_data->ignore_nice_load) - j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + j_cdbs->prev_cpu_nice = kcpustat_field(CPUTIME_NICE, j); } + mutex_unlock(&policy_dbs->update_mutex); } } EXPORT_SYMBOL_GPL(gov_update_cpu_data); @@ -118,6 +126,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) unsigned int ignore_nice = dbs_data->ignore_nice_load; unsigned int max_load = 0, idle_periods = UINT_MAX; unsigned int sampling_rate, io_busy, j; + u64 cur_nice; /* * Sometimes governors may use an additional multiplier to increase @@ -164,12 +173,18 @@ unsigned int dbs_update(struct cpufreq_policy *policy) j_cdbs->prev_cpu_idle = cur_idle_time; - if (ignore_nice) { - u64 cur_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); - + /* + * Always sample cur_nice and advance prev_cpu_nice, regardless + * of ignore_nice. This keeps prev_cpu_nice current so that + * enabling ignore_nice_load via sysfs never produces a + * stale-baseline spike (the delta will be at most one sampling + * interval of accumulated nice time, not since boot). + */ + cur_nice = kcpustat_field(CPUTIME_NICE, j); + if (ignore_nice) idle_time += div_u64(cur_nice - j_cdbs->prev_cpu_nice, NSEC_PER_USEC); - j_cdbs->prev_cpu_nice = cur_nice; - } + + j_cdbs->prev_cpu_nice = cur_nice; if (unlikely(!time_elapsed)) { /* @@ -516,7 +531,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) struct dbs_governor *gov = dbs_governor_of(policy); struct policy_dbs_info *policy_dbs = policy->governor_data; struct dbs_data *dbs_data = policy_dbs->dbs_data; - unsigned int sampling_rate, ignore_nice, j; + unsigned int sampling_rate, j; unsigned int io_busy; if (!policy->cur) @@ -526,9 +541,9 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) policy_dbs->rate_mult = 1; sampling_rate = dbs_data->sampling_rate; - ignore_nice = dbs_data->ignore_nice_load; - io_busy = dbs_data->io_is_busy; + mutex_lock(&policy_dbs->update_mutex); + io_busy = dbs_data->io_is_busy; for_each_cpu(j, policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); @@ -537,10 +552,9 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) * Make the first invocation of dbs_update() compute the load. */ j_cdbs->prev_load = 0; - - if (ignore_nice) - j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + j_cdbs->prev_cpu_nice = kcpustat_field(CPUTIME_NICE, j); } + mutex_unlock(&policy_dbs->update_mutex); gov->start(policy); diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c deleted file mode 100644 index fc5a58088b35..000000000000 --- a/drivers/cpufreq/elanfreq.c +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * elanfreq: cpufreq driver for the AMD ELAN family - * - * (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de> - * - * Parts of this code are (c) Sven Geggus <sven@geggus.net> - * - * All Rights Reserved. - * - * 2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> - -#include <linux/delay.h> -#include <linux/cpufreq.h> - -#include <asm/cpu_device_id.h> -#include <linux/timex.h> -#include <linux/io.h> - -#define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */ -#define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */ - -/* Module parameter */ -static int max_freq; - -struct s_elan_multiplier { - int clock; /* frequency in kHz */ - int val40h; /* PMU Force Mode register */ - int val80h; /* CPU Clock Speed Register */ -}; - -/* - * It is important that the frequencies - * are listed in ascending order here! - */ -static struct s_elan_multiplier elan_multiplier[] = { - {1000, 0x02, 0x18}, - {2000, 0x02, 0x10}, - {4000, 0x02, 0x08}, - {8000, 0x00, 0x00}, - {16000, 0x00, 0x02}, - {33000, 0x00, 0x04}, - {66000, 0x01, 0x04}, - {99000, 0x01, 0x05} -}; - -static struct cpufreq_frequency_table elanfreq_table[] = { - {0, 0, 1000}, - {0, 1, 2000}, - {0, 2, 4000}, - {0, 3, 8000}, - {0, 4, 16000}, - {0, 5, 33000}, - {0, 6, 66000}, - {0, 7, 99000}, - {0, 0, CPUFREQ_TABLE_END}, -}; - - -/** - * elanfreq_get_cpu_frequency: determine current cpu speed - * - * Finds out at which frequency the CPU of the Elan SOC runs - * at the moment. Frequencies from 1 to 33 MHz are generated - * the normal way, 66 and 99 MHz are called "Hyperspeed Mode" - * and have the rest of the chip running with 33 MHz. - */ - -static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu) -{ - u8 clockspeed_reg; /* Clock Speed Register */ - - local_irq_disable(); - outb_p(0x80, REG_CSCIR); - clockspeed_reg = inb_p(REG_CSCDR); - local_irq_enable(); - - if ((clockspeed_reg & 0xE0) == 0xE0) - return 0; - - /* Are we in CPU clock multiplied mode (66/99 MHz)? */ - if ((clockspeed_reg & 0xE0) == 0xC0) { - if ((clockspeed_reg & 0x01) == 0) - return 66000; - else - return 99000; - } - - /* 33 MHz is not 32 MHz... */ - if ((clockspeed_reg & 0xE0) == 0xA0) - return 33000; - - return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000; -} - - -static int elanfreq_target(struct cpufreq_policy *policy, - unsigned int state) -{ - /* - * Access to the Elan's internal registers is indexed via - * 0x22: Chip Setup & Control Register Index Register (CSCI) - * 0x23: Chip Setup & Control Register Data Register (CSCD) - * - */ - - /* - * 0x40 is the Power Management Unit's Force Mode Register. - * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency) - */ - - local_irq_disable(); - outb_p(0x40, REG_CSCIR); /* Disable hyperspeed mode */ - outb_p(0x00, REG_CSCDR); - local_irq_enable(); /* wait till internal pipelines and */ - udelay(1000); /* buffers have cleaned up */ - - local_irq_disable(); - - /* now, set the CPU clock speed register (0x80) */ - outb_p(0x80, REG_CSCIR); - outb_p(elan_multiplier[state].val80h, REG_CSCDR); - - /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */ - outb_p(0x40, REG_CSCIR); - outb_p(elan_multiplier[state].val40h, REG_CSCDR); - udelay(10000); - local_irq_enable(); - - return 0; -} -/* - * Module init and exit code - */ - -static int elanfreq_cpu_init(struct cpufreq_policy *policy) -{ - struct cpuinfo_x86 *c = &cpu_data(0); - struct cpufreq_frequency_table *pos; - - /* capability check */ - if ((c->x86_vendor != X86_VENDOR_AMD) || - (c->x86 != 4) || (c->x86_model != 10)) - return -ENODEV; - - /* max freq */ - if (!max_freq) - max_freq = elanfreq_get_cpu_frequency(0); - - /* table init */ - cpufreq_for_each_entry(pos, elanfreq_table) - if (pos->frequency > max_freq) - pos->frequency = CPUFREQ_ENTRY_INVALID; - - policy->freq_table = elanfreq_table; - return 0; -} - - -#ifndef MODULE -/** - * elanfreq_setup - elanfreq command line parameter parsing - * - * elanfreq command line parameter. Use: - * elanfreq=66000 - * to set the maximum CPU frequency to 66 MHz. Note that in - * case you do not give this boot parameter, the maximum - * frequency will fall back to _current_ CPU frequency which - * might be lower. If you build this as a module, use the - * max_freq module parameter instead. - */ -static int __init elanfreq_setup(char *str) -{ - max_freq = simple_strtoul(str, &str, 0); - pr_warn("You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n"); - return 1; -} -__setup("elanfreq=", elanfreq_setup); -#endif - - -static struct cpufreq_driver elanfreq_driver = { - .get = elanfreq_get_cpu_frequency, - .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, - .verify = cpufreq_generic_frequency_table_verify, - .target_index = elanfreq_target, - .init = elanfreq_cpu_init, - .name = "elanfreq", -}; - -static const struct x86_cpu_id elan_id[] = { - X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 10, NULL), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, elan_id); - -static int __init elanfreq_init(void) -{ - if (!x86_match_cpu(elan_id)) - return -ENODEV; - return cpufreq_register_driver(&elanfreq_driver); -} - - -static void __exit elanfreq_exit(void) -{ - cpufreq_unregister_driver(&elanfreq_driver); -} - - -module_param(max_freq, int, 0444); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, " - "Sven Geggus <sven@geggus.net>"); -MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs"); - -module_init(elanfreq_init); -module_exit(elanfreq_exit); diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 5b364d8da4f9..ea994647abc8 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -49,16 +49,15 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy) max_freq = freq; } - policy->min = policy->cpuinfo.min_freq = min_freq; - policy->max = max_freq; + policy->cpuinfo.min_freq = min_freq; /* * If the driver has set its own cpuinfo.max_freq above max_freq, leave * it as is. */ if (policy->cpuinfo.max_freq < max_freq) - policy->max = policy->cpuinfo.max_freq = max_freq; + policy->cpuinfo.max_freq = max_freq; - if (policy->min == ~0) + if (min_freq == ~0) return -EINVAL; else return 0; diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index d269a4f26f98..d40c9e0bbb74 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -421,7 +421,7 @@ static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy) policy->min = maxfreq / max_duration; else policy->min = maxfreq / POLICY_MIN_DIV; - policy->max = maxfreq; + policy->cpuinfo.min_freq = maxfreq / max_duration; policy->cpuinfo.max_freq = maxfreq; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 1f093e346430..5a0eeb84d382 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2984,10 +2984,12 @@ static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) * from getting to lower performance levels, so force the minimum * performance on CPU offline to prevent that from happening. */ - if (hwp_active) + if (hwp_active) { intel_pstate_hwp_offline(cpu); - else + } else { intel_pstate_set_min_pstate(cpu); + policy->cur = cpu->pstate.min_freq; + } intel_pstate_exit_perf_limits(policy); @@ -3049,9 +3051,6 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - intel_pstate_init_acpi_perf_limits(policy); policy->fast_switch_possible = true; @@ -3824,6 +3823,12 @@ static int __init intel_pstate_init(void) if (no_load) return -ENODEV; + id = x86_match_cpu(intel_hybrid_scaling_factor); + if (id) { + pr_info("HWP-disabled hybrid CPU is not supported\n"); + return -ENODEV; + } + id = x86_match_cpu(intel_pstate_cpu_ids); if (!id) { pr_info("CPU model not supported\n"); diff --git a/drivers/cpufreq/longrun.c b/drivers/cpufreq/longrun.c index 1caaec7c280b..f3aaca0496a4 100644 --- a/drivers/cpufreq/longrun.c +++ b/drivers/cpufreq/longrun.c @@ -14,6 +14,7 @@ #include <asm/msr.h> #include <asm/processor.h> #include <asm/cpu_device_id.h> +#include <asm/cpuid/api.h> static struct cpufreq_driver longrun_driver; diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 69c19233fcd4..c1690aa48193 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -51,24 +51,24 @@ static unsigned int cpufreq_p4_get(unsigned int cpu); static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) { - u32 l, h; + struct msr val; if ((newstate > DC_DISABLE) || (newstate == DC_RESV)) return -EINVAL; - rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h); + rdmsrq_on_cpu(cpu, MSR_IA32_THERM_STATUS, &val.q); - if (l & 0x01) + if (val.l & 0x01) pr_debug("CPU#%d currently thermal throttled\n", cpu); if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate == DC_DFLT)) newstate = DC_38PT; - rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); + rdmsrq_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &val.q); if (newstate == DC_DISABLE) { pr_debug("CPU#%d disabling modulation\n", cpu); - wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h); + wrmsrq_on_cpu(cpu, MSR_IA32_THERM_CONTROL, val.q & ~(1ULL << 4)); } else { pr_debug("CPU#%d setting duty cycle to %d%%\n", cpu, ((125 * newstate) / 10)); @@ -77,9 +77,9 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) * bits 3-1 : duty cycle * bit 0 : reserved */ - l = (l & ~14); - l = l | (1<<4) | ((newstate & 0x7)<<1); - wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h); + val.l = (val.l & ~14); + val.l = val.l | (1<<4) | ((newstate & 0x7)<<1); + wrmsrq_on_cpu(cpu, MSR_IA32_THERM_CONTROL, val.q); } return 0; @@ -205,18 +205,18 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) static unsigned int cpufreq_p4_get(unsigned int cpu) { - u32 l, h; + struct msr val; - rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); + rdmsrq_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &val.q); - if (l & 0x10) { - l = l >> 1; - l &= 0x7; + if (val.l & 0x10) { + val.l = val.l >> 1; + val.l &= 0x7; } else - l = DC_DISABLE; + val.l = DC_DISABLE; - if (l != DC_DISABLE) - return stock_freq * l / 8; + if (val.l != DC_DISABLE) + return stock_freq * val.l / 8; return stock_freq; } diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index ac2e90a65f0c..83e6f73dc9a4 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -352,6 +352,8 @@ static int __init pcc_cpufreq_do_osc(acpi_handle *handle) } kfree(output.pointer); + output.pointer = NULL; + output.length = ACPI_ALLOCATE_BUFFER; capabilities[0] = 0x0; capabilities[1] = 0x1; @@ -551,13 +553,11 @@ static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy) goto out; } - policy->max = policy->cpuinfo.max_freq = - ioread32(&pcch_hdr->nominal) * 1000; - policy->min = policy->cpuinfo.min_freq = - ioread32(&pcch_hdr->minimum_frequency) * 1000; + policy->cpuinfo.max_freq = ioread32(&pcch_hdr->nominal) * 1000; + policy->cpuinfo.min_freq = ioread32(&pcch_hdr->minimum_frequency) * 1000; - pr_debug("init: policy->max is %d, policy->min is %d\n", - policy->max, policy->min); + pr_debug("init: max_freq is %d, min_freq is %d\n", + policy->cpuinfo.max_freq, policy->cpuinfo.min_freq); out: return result; } diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 6b7caf4ae20d..6a930d7e6a5c 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -29,6 +29,7 @@ #include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */ #include <asm/msr.h> #include <asm/cpu_device_id.h> +#include <asm/cpuid/api.h> #ifdef CONFIG_X86_POWERNOW_K7_ACPI #include <linux/acpi.h> diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 4d77eef53fe0..2b791f1ec51b 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -39,6 +39,7 @@ #include <asm/msr.h> #include <asm/cpu_device_id.h> +#include <asm/cpuid/api.h> #include <linux/acpi.h> #include <linux/mutex.h> diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 50ff3b6a6900..06b27cbc59d6 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -185,9 +185,8 @@ static int pxa3xx_cpufreq_init(struct cpufreq_policy *policy) int ret = -EINVAL; /* set default policy and cpuinfo */ - policy->min = policy->cpuinfo.min_freq = 104000; - policy->max = policy->cpuinfo.max_freq = - (cpu_is_pxa320()) ? 806000 : 624000; + policy->cpuinfo.min_freq = 104000; + policy->cpuinfo.max_freq = (cpu_is_pxa320()) ? 806000 : 624000; policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ if (cpu_is_pxa300() || cpu_is_pxa310()) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index ea9a20d27b8f..874ff3fb9d97 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/bitfield.h> @@ -40,6 +41,7 @@ struct qcom_cpufreq_soc_data { u32 reg_intr_clr; u32 reg_current_vote; u32 reg_perf_state; + u32 lut_max_entries; u8 lut_row_size; }; @@ -156,7 +158,7 @@ static unsigned int qcom_cpufreq_get_freq(struct cpufreq_policy *policy) soc_data = qcom_cpufreq.soc_data; index = readl_relaxed(data->base + soc_data->reg_perf_state); - index = min(index, LUT_MAX_ENTRIES - 1); + index = min(index, soc_data->lut_max_entries - 1); return policy->freq_table[index].frequency; } @@ -211,7 +213,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, struct qcom_cpufreq_data *drv_data = policy->driver_data; const struct qcom_cpufreq_soc_data *soc_data = qcom_cpufreq.soc_data; - table = kzalloc_objs(*table, LUT_MAX_ENTRIES + 1); + table = kzalloc_objs(*table, soc_data->lut_max_entries + 1); if (!table) return -ENOMEM; @@ -236,7 +238,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, icc_scaling_enabled = false; } - for (i = 0; i < LUT_MAX_ENTRIES; i++) { + for (i = 0; i < soc_data->lut_max_entries; i++) { data = readl_relaxed(drv_data->base + soc_data->reg_freq_lut + i * soc_data->lut_row_size); src = FIELD_GET(LUT_SRC, data); @@ -405,6 +407,7 @@ static const struct qcom_cpufreq_soc_data qcom_soc_data = { .reg_current_vote = 0x704, .reg_perf_state = 0x920, .lut_row_size = 32, + .lut_max_entries = LUT_MAX_ENTRIES, }; static const struct qcom_cpufreq_soc_data epss_soc_data = { @@ -416,11 +419,25 @@ static const struct qcom_cpufreq_soc_data epss_soc_data = { .reg_intr_clr = 0x308, .reg_perf_state = 0x320, .lut_row_size = 4, + .lut_max_entries = LUT_MAX_ENTRIES, +}; + +static const struct qcom_cpufreq_soc_data shikra_epss_soc_data = { + .reg_enable = 0x0, + .reg_domain_state = 0x20, + .reg_dcvs_ctrl = 0xb0, + .reg_freq_lut = 0x100, + .reg_volt_lut = 0x200, + .reg_intr_clr = 0x308, + .reg_perf_state = 0x320, + .lut_row_size = 4, + .lut_max_entries = 12, }; static const struct of_device_id qcom_cpufreq_hw_match[] = { { .compatible = "qcom,cpufreq-hw", .data = &qcom_soc_data }, { .compatible = "qcom,cpufreq-epss", .data = &epss_soc_data }, + { .compatible = "qcom,shikra-epss", .data = &shikra_epss_soc_data }, {} }; MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match); @@ -578,7 +595,6 @@ static void qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); qcom_cpufreq_hw_lmh_exit(data); kfree(policy->freq_table); - kfree(data); } static void qcom_cpufreq_ready(struct cpufreq_policy *policy) diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index f17bf64c22e2..10106fa13095 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -201,12 +201,13 @@ kernel::of_device_table!( impl platform::Driver for CPUFreqDTDriver { type IdInfo = (); + type Data<'bound> = Self; const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); - fn probe( - pdev: &platform::Device<Core>, - _id_info: Option<&Self::IdInfo>, - ) -> impl PinInit<Self, Error> { + fn probe<'bound>( + pdev: &'bound platform::Device<Core<'_>>, + _id_info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit<Self, Error> + 'bound { cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?; Ok(Self {}) } diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c deleted file mode 100644 index b360f03a116f..000000000000 --- a/drivers/cpufreq/sc520_freq.c +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * sc520_freq.c: cpufreq driver for the AMD Elan sc520 - * - * Copyright (C) 2005 Sean Young <sean@mess.org> - * - * Based on elanfreq.c - * - * 2005-03-30: - initial revision - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> - -#include <linux/delay.h> -#include <linux/cpufreq.h> -#include <linux/timex.h> -#include <linux/io.h> - -#include <asm/cpu_device_id.h> - -#define MMCR_BASE 0xfffef000 /* The default base address */ -#define OFFS_CPUCTL 0x2 /* CPU Control Register */ - -static __u8 __iomem *cpuctl; - -static struct cpufreq_frequency_table sc520_freq_table[] = { - {0, 0x01, 100000}, - {0, 0x02, 133000}, - {0, 0, CPUFREQ_TABLE_END}, -}; - -static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu) -{ - u8 clockspeed_reg = *cpuctl; - - switch (clockspeed_reg & 0x03) { - default: - pr_err("error: cpuctl register has unexpected value %02x\n", - clockspeed_reg); - fallthrough; - case 0x01: - return 100000; - case 0x02: - return 133000; - } -} - -static int sc520_freq_target(struct cpufreq_policy *policy, unsigned int state) -{ - - u8 clockspeed_reg; - - local_irq_disable(); - - clockspeed_reg = *cpuctl & ~0x03; - *cpuctl = clockspeed_reg | sc520_freq_table[state].driver_data; - - local_irq_enable(); - - return 0; -} - -/* - * Module init and exit code - */ - -static int sc520_freq_cpu_init(struct cpufreq_policy *policy) -{ - struct cpuinfo_x86 *c = &cpu_data(0); - - /* capability check */ - if (c->x86_vendor != X86_VENDOR_AMD || - c->x86 != 4 || c->x86_model != 9) - return -ENODEV; - - /* cpuinfo and default policy values */ - policy->cpuinfo.transition_latency = 1000000; /* 1ms */ - policy->freq_table = sc520_freq_table; - - return 0; -} - - -static struct cpufreq_driver sc520_freq_driver = { - .get = sc520_freq_get_cpu_frequency, - .verify = cpufreq_generic_frequency_table_verify, - .target_index = sc520_freq_target, - .init = sc520_freq_cpu_init, - .name = "sc520_freq", -}; - -static const struct x86_cpu_id sc520_ids[] = { - X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 9, NULL), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, sc520_ids); - -static int __init sc520_freq_init(void) -{ - int err; - - if (!x86_match_cpu(sc520_ids)) - return -ENODEV; - - cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1); - if (!cpuctl) { - pr_err("sc520_freq: error: failed to remap memory\n"); - return -ENOMEM; - } - - err = cpufreq_register_driver(&sc520_freq_driver); - if (err) - iounmap(cpuctl); - - return err; -} - - -static void __exit sc520_freq_exit(void) -{ - cpufreq_unregister_driver(&sc520_freq_driver); - iounmap(cpuctl); -} - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sean Young <sean@mess.org>"); -MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU"); - -module_init(sc520_freq_init); -module_exit(sc520_freq_exit); - diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 642ddb9ea217..3c99d7009cbe 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -124,10 +124,8 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) dev_notice(dev, "no frequency table found, falling back " "to rate rounding.\n"); - policy->min = policy->cpuinfo.min_freq = - (clk_round_rate(cpuclk, 1) + 500) / 1000; - policy->max = policy->cpuinfo.max_freq = - (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; + policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000; + policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; } return 0; diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 3e6e85a92212..9237ed8f2b1f 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -322,11 +322,11 @@ static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe) /* Return the current CPU frequency in kHz */ static unsigned int get_cur_freq(unsigned int cpu) { - unsigned l, h; + struct msr val; unsigned clock_freq; - rdmsr_on_cpu(cpu, MSR_IA32_PERF_STATUS, &l, &h); - clock_freq = extract_clock(l, cpu, 0); + rdmsrq_on_cpu(cpu, MSR_IA32_PERF_STATUS, &val.q); + clock_freq = extract_clock(val.l, cpu, 0); if (unlikely(clock_freq == 0)) { /* @@ -335,8 +335,8 @@ static unsigned int get_cur_freq(unsigned int cpu) * P-state transition (like TM2). Get the last freq set * in PERF_CTL. */ - rdmsr_on_cpu(cpu, MSR_IA32_PERF_CTL, &l, &h); - clock_freq = extract_clock(l, cpu, 1); + rdmsrq_on_cpu(cpu, MSR_IA32_PERF_CTL, &val.q); + clock_freq = extract_clock(val.l, cpu, 1); } return clock_freq; } @@ -417,7 +417,8 @@ static void centrino_cpu_exit(struct cpufreq_policy *policy) */ static int centrino_target(struct cpufreq_policy *policy, unsigned int index) { - unsigned int msr, oldmsr = 0, h = 0, cpu = policy->cpu; + unsigned int msr, cpu = policy->cpu; + struct msr oldmsr = { .q = 0 }; int retval = 0; unsigned int j, first_cpu; struct cpufreq_frequency_table *op_points; @@ -459,22 +460,22 @@ static int centrino_target(struct cpufreq_policy *policy, unsigned int index) msr = op_points->driver_data; if (first_cpu) { - rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h); - if (msr == (oldmsr & 0xffff)) { + rdmsrq_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr.q); + if (msr == (oldmsr.l & 0xffff)) { pr_debug("no change needed - msr was and needs " - "to be %x\n", oldmsr); + "to be %x\n", oldmsr.l); retval = 0; goto out; } first_cpu = 0; /* all but 16 LSB are reserved, treat them with care */ - oldmsr &= ~0xffff; + oldmsr.l &= ~0xffff; msr &= 0xffff; - oldmsr |= msr; + oldmsr.l |= msr; } - wrmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, oldmsr, h); + wrmsrq_on_cpu(good_cpu, MSR_IA32_PERF_CTL, oldmsr.q); if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) break; @@ -490,7 +491,7 @@ static int centrino_target(struct cpufreq_policy *policy, unsigned int index) */ for_each_cpu(j, covered_cpus) - wrmsr_on_cpu(j, MSR_IA32_PERF_CTL, oldmsr, h); + wrmsrq_on_cpu(j, MSR_IA32_PERF_CTL, oldmsr.q); } retval = 0; diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c index f8b42e981635..973716c1c29c 100644 --- a/drivers/cpufreq/speedstep-lib.c +++ b/drivers/cpufreq/speedstep-lib.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/cpufreq.h> +#include <asm/cpuid/api.h> #include <asm/msr.h> #include <asm/tsc.h> #include "speedstep-lib.h" diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index a01abc1622eb..0f96492341f2 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -99,6 +99,7 @@ struct ti_cpufreq_soc_data { unsigned long efuse_shift; unsigned long rev_offset; bool multi_regulator; + bool needs_k3_socinfo; /* Backward compatibility hack: Might have missing syscon */ #define TI_QUIRK_SYSCON_MAY_BE_MISSING 0x1 /* Backward compatibility hack: new syscon size is 1 register wide */ @@ -347,6 +348,7 @@ static struct ti_cpufreq_soc_data am625_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, .quirks = TI_QUIRK_SYSCON_IS_SINGLE_REG, }; @@ -356,6 +358,7 @@ static struct ti_cpufreq_soc_data am62a7_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, }; static struct ti_cpufreq_soc_data am62l3_soc_data = { @@ -364,6 +367,7 @@ static struct ti_cpufreq_soc_data am62l3_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, }; static struct ti_cpufreq_soc_data am62p5_soc_data = { @@ -372,6 +376,7 @@ static struct ti_cpufreq_soc_data am62p5_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, }; /** @@ -443,6 +448,11 @@ static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data, goto done; } + /* Defer if k3-socinfo hasn't registered the SoC device yet */ + if (opp_data->soc_data->needs_k3_socinfo) + return dev_err_probe(opp_data->cpu_dev, -EPROBE_DEFER, + "SoC device not registered by k3-socinfo\n"); + ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset, &revision); if (opp_data->soc_data->quirks & TI_QUIRK_SYSCON_MAY_BE_MISSING && ret == -EIO) { diff --git a/drivers/cpufreq/virtual-cpufreq.c b/drivers/cpufreq/virtual-cpufreq.c index 4159f31349b1..dc78b74409af 100644 --- a/drivers/cpufreq/virtual-cpufreq.c +++ b/drivers/cpufreq/virtual-cpufreq.c @@ -164,10 +164,7 @@ static int virt_cpufreq_get_freq_info(struct cpufreq_policy *policy) policy->cpuinfo.min_freq = 1; policy->cpuinfo.max_freq = virt_cpufreq_get_perftbl_entry(policy->cpu, 0); - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - - policy->cur = policy->max; + policy->cur = policy->cpuinfo.max_freq; return 0; } diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index d23b58b81ca3..216a00bad5d7 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -301,7 +301,6 @@ config CRYPTO_DEV_PPC4XX select CRYPTO_CCM select CRYPTO_CTR select CRYPTO_GCM - select CRYPTO_RNG select CRYPTO_SKCIPHER help This option allows you to have support for AMCC crypto acceleration. @@ -316,7 +315,7 @@ config HW_RANDOM_PPC4XX config CRYPTO_DEV_OMAP tristate "Support for OMAP crypto HW accelerators" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST help OMAP processors have various crypto HW accelerators. Select this if you want to use the OMAP modules for any of the crypto algorithms. @@ -352,7 +351,7 @@ config CRYPTO_DEV_OMAP_AES config CRYPTO_DEV_OMAP_DES tristate "Support for OMAP DES/3DES hw engine" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST select CRYPTO_LIB_DES select CRYPTO_SKCIPHER select CRYPTO_ENGINE @@ -375,21 +374,6 @@ config CRYPTO_DEV_SAHARA This option enables support for the SAHARA HW crypto accelerator found in some Freescale i.MX chips. -config CRYPTO_DEV_EXYNOS_RNG - tristate "Exynos HW pseudo random number generator support" - depends on ARCH_EXYNOS || COMPILE_TEST - depends on HAS_IOMEM - select CRYPTO_RNG - help - This driver provides kernel-side support through the - cryptographic API for the pseudo random number generator hardware - found on Exynos SoCs. - - To compile this driver as a module, choose M here: the - module will be called exynos-rng. - - If unsure, say Y. - config CRYPTO_DEV_S5P tristate "Support for Samsung S5PV210/Exynos crypto accelerator" depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST @@ -404,7 +388,6 @@ config CRYPTO_DEV_S5P config CRYPTO_DEV_EXYNOS_HASH bool "Support for Samsung Exynos HASH accelerator" depends on CRYPTO_DEV_S5P - depends on !CRYPTO_DEV_EXYNOS_RNG && CRYPTO_DEV_EXYNOS_RNG!=m select CRYPTO_SHA1 select CRYPTO_MD5 select CRYPTO_SHA256 @@ -412,8 +395,6 @@ config CRYPTO_DEV_EXYNOS_HASH Select this to offload Exynos from HASH MD5/SHA1/SHA256. This will select software SHA1, MD5 and SHA256 as they are needed for small and zero-size messages. - HASH algorithms will be disabled if EXYNOS_RNG - is enabled due to hw conflict. config CRYPTO_DEV_NX bool "Support for IBM PowerPC Nest (NX) cryptographic acceleration" @@ -718,19 +699,6 @@ config CRYPTO_DEV_TEGRA Select this to enable Tegra Security Engine which accelerates various AES encryption/decryption and HASH algorithms. -config CRYPTO_DEV_XILINX_TRNG - tristate "Support for Xilinx True Random Generator" - depends on ZYNQMP_FIRMWARE || COMPILE_TEST - select CRYPTO_DF80090A - select CRYPTO_RNG - select HW_RANDOM - help - Xilinx Versal SoC driver provides kernel-side support for True Random Number - Generator and Pseudo random Number in CTR_DRBG mode as defined in NIST SP800-90A. - - To compile this driver as a module, choose M here: the module - will be called xilinx-trng. - config CRYPTO_DEV_ZYNQMP_AES tristate "Support for Xilinx ZynqMP AES hw accelerator" depends on ZYNQMP_FIRMWARE || COMPILE_TEST @@ -846,7 +814,6 @@ config CRYPTO_DEV_CCREE If unsure say Y. source "drivers/crypto/hisilicon/Kconfig" -source "drivers/crypto/loongson/Kconfig" source "drivers/crypto/amlogic/Kconfig" diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 283bbc650b5b..5a950c7abc39 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA204A) += atmel-sha204a.o obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/ obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/ obj-$(CONFIG_CRYPTO_DEV_CHELSIO) += chelsio/ -obj-$(CONFIG_CRYPTO_DEV_EXYNOS_RNG) += exynos-rng.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_COMMON) += caam/ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o @@ -43,7 +42,6 @@ obj-y += inside-secure/ obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/ obj-y += xilinx/ obj-y += hisilicon/ -obj-y += loongson/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += intel/ obj-y += starfive/ diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig index 7270e5fbc573..06ea0e9fe6f2 100644 --- a/drivers/crypto/allwinner/Kconfig +++ b/drivers/crypto/allwinner/Kconfig @@ -14,7 +14,6 @@ config CRYPTO_DEV_SUN4I_SS select CRYPTO_SHA1 select CRYPTO_AES select CRYPTO_LIB_DES - select CRYPTO_RNG select CRYPTO_SKCIPHER help Some Allwinner SoC have a crypto accelerator named @@ -25,14 +24,6 @@ config CRYPTO_DEV_SUN4I_SS To compile this driver as a module, choose M here: the module will be called sun4i-ss. -config CRYPTO_DEV_SUN4I_SS_PRNG - bool "Support for Allwinner Security System PRNG" - depends on CRYPTO_DEV_SUN4I_SS - select CRYPTO_RNG - help - Select this option if you want to provide kernel-side support for - the Pseudo-Random Number Generator found in the Security System. - config CRYPTO_DEV_SUN4I_SS_DEBUG bool "Enable sun4i-ss stats" depends on CRYPTO_DEV_SUN4I_SS @@ -50,7 +41,6 @@ config CRYPTO_DEV_SUN8I_CE select CRYPTO_CBC select CRYPTO_AES select CRYPTO_DES - select CRYPTO_RNG depends on CRYPTO_DEV_ALLWINNER depends on PM help diff --git a/drivers/crypto/allwinner/sun4i-ss/Makefile b/drivers/crypto/allwinner/sun4i-ss/Makefile index c0a2797d3168..06a9ae81f9f8 100644 --- a/drivers/crypto/allwinner/sun4i-ss/Makefile +++ b/drivers/crypto/allwinner/sun4i-ss/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sun4i-ss.o sun4i-ss-y += sun4i-ss-core.o sun4i-ss-hash.o sun4i-ss-cipher.o -sun4i-ss-$(CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG) += sun4i-ss-prng.o diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c index 58a76e2ba64e..35ef0930e77f 100644 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c +++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c @@ -213,23 +213,6 @@ static struct sun4i_ss_alg_template ss_algs[] = { } } }, -#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG -{ - .type = CRYPTO_ALG_TYPE_RNG, - .alg.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "sun4i_ss_rng", - .cra_priority = 300, - .cra_ctxsize = 0, - .cra_module = THIS_MODULE, - }, - .generate = sun4i_ss_prng_generate, - .seed = sun4i_ss_prng_seed, - .seedsize = SS_SEED_LEN / BITS_PER_BYTE, - } -}, -#endif }; static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v) @@ -247,12 +230,6 @@ static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v) ss_algs[i].stat_req, ss_algs[i].stat_opti, ss_algs[i].stat_fb, ss_algs[i].stat_bytes); break; - case CRYPTO_ALG_TYPE_RNG: - seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n", - ss_algs[i].alg.rng.base.cra_driver_name, - ss_algs[i].alg.rng.base.cra_name, - ss_algs[i].stat_req, ss_algs[i].stat_bytes); - break; case CRYPTO_ALG_TYPE_AHASH: seq_printf(seq, "%s %s reqs=%lu\n", ss_algs[i].alg.hash.halg.base.cra_driver_name, @@ -471,13 +448,6 @@ static int sun4i_ss_probe(struct platform_device *pdev) goto error_alg; } break; - case CRYPTO_ALG_TYPE_RNG: - err = crypto_register_rng(&ss_algs[i].alg.rng); - if (err) { - dev_err(ss->dev, "Fail to register %s\n", - ss_algs[i].alg.rng.base.cra_name); - } - break; } } @@ -497,9 +467,6 @@ error_alg: case CRYPTO_ALG_TYPE_AHASH: crypto_unregister_ahash(&ss_algs[i].alg.hash); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&ss_algs[i].alg.rng); - break; } } error_pm: @@ -520,9 +487,6 @@ static void sun4i_ss_remove(struct platform_device *pdev) case CRYPTO_ALG_TYPE_AHASH: crypto_unregister_ahash(&ss_algs[i].alg.hash); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&ss_algs[i].alg.rng); - break; } } diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c deleted file mode 100644 index 491fcb7b81b4..000000000000 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include "sun4i-ss.h" - -int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct sun4i_ss_alg_template *algt; - struct rng_alg *alg = crypto_rng_alg(tfm); - - algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng); - memcpy(algt->ss->seed, seed, slen); - - return 0; -} - -int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen) -{ - struct sun4i_ss_alg_template *algt; - struct rng_alg *alg = crypto_rng_alg(tfm); - int i, err; - u32 v; - u32 *data = (u32 *)dst; - const u32 mode = SS_OP_PRNG | SS_PRNG_CONTINUE | SS_ENABLED; - size_t len; - struct sun4i_ss_ctx *ss; - unsigned int todo = (dlen / 4) * 4; - - algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng); - ss = algt->ss; - - err = pm_runtime_resume_and_get(ss->dev); - if (err < 0) - return err; - - if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN4I_SS_DEBUG)) { - algt->stat_req++; - algt->stat_bytes += todo; - } - - spin_lock_bh(&ss->slock); - - writel(mode, ss->base + SS_CTL); - - while (todo > 0) { - /* write the seed */ - for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++) - writel(ss->seed[i], ss->base + SS_KEY0 + i * 4); - - /* Read the random data */ - len = min_t(size_t, SS_DATA_LEN / BITS_PER_BYTE, todo); - readsl(ss->base + SS_TXFIFO, data, len / 4); - data += len / 4; - todo -= len; - - /* Update the seed */ - for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++) { - v = readl(ss->base + SS_KEY0 + i * 4); - ss->seed[i] = v; - } - } - - writel(0, ss->base + SS_CTL); - spin_unlock_bh(&ss->slock); - - pm_runtime_put(ss->dev); - - return 0; -} diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h index 6c5d4aa6453c..f7d1c79ac677 100644 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h +++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h @@ -31,8 +31,6 @@ #include <crypto/internal/skcipher.h> #include <crypto/aes.h> #include <crypto/internal/des.h> -#include <crypto/internal/rng.h> -#include <crypto/rng.h> #define SS_CTL 0x00 #define SS_KEY0 0x04 @@ -62,10 +60,6 @@ /* SS_CTL configuration values */ -/* PRNG generator mode - bit 15 */ -#define SS_PRNG_ONESHOT (0 << 15) -#define SS_PRNG_CONTINUE (1 << 15) - /* IV mode for hash */ #define SS_IV_ARBITRARY (1 << 14) @@ -94,14 +88,10 @@ #define SS_OP_3DES (2 << 4) #define SS_OP_SHA1 (3 << 4) #define SS_OP_MD5 (4 << 4) -#define SS_OP_PRNG (5 << 4) /* Data end bit - bit 2 */ #define SS_DATA_END (1 << 2) -/* PRNG start bit - bit 1 */ -#define SS_PRNG_START (1 << 1) - /* SS Enable bit - bit 0 */ #define SS_DISABLED (0 << 0) #define SS_ENABLED (1 << 0) @@ -128,9 +118,6 @@ #define SS_RXFIFO_EMP_INT_ENABLE (1 << 2) #define SS_TXFIFO_AVA_INT_ENABLE (1 << 0) -#define SS_SEED_LEN 192 -#define SS_DATA_LEN 160 - /* * struct ss_variant - Describe SS hardware variant * @sha1_in_be: The SHA1 digest is given by SS in BE, and so need to be inverted. @@ -151,9 +138,6 @@ struct sun4i_ss_ctx { char buf[4 * SS_RX_MAX];/* buffer for linearize SG src */ char bufo[4 * SS_TX_MAX]; /* buffer for linearize SG dst */ spinlock_t slock; /* control the use of the device */ -#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG - u32 seed[SS_SEED_LEN / BITS_PER_LONG]; -#endif struct dentry *dbgfs_dir; struct dentry *dbgfs_stats; }; @@ -164,7 +148,6 @@ struct sun4i_ss_alg_template { union { struct skcipher_alg crypto; struct ahash_alg hash; - struct rng_alg rng; } alg; struct sun4i_ss_ctx *ss; unsigned long stat_req; @@ -231,6 +214,3 @@ int sun4i_ss_des_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen); int sun4i_ss_des3_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen); -int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen); -int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen); diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c index c16bb6ce6ee3..f3b58ed6aed0 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c @@ -676,6 +676,7 @@ static int sun8i_ce_debugfs_show(struct seq_file *seq, void *v) seq_printf(seq, "\tFallback due to SG numbers: %lu\n", ce_algs[i].stat_fb_maxsg); break; +#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_HASH case CRYPTO_ALG_TYPE_AHASH: seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n", ce_algs[i].alg.hash.base.halg.base.cra_driver_name, @@ -692,12 +693,15 @@ static int sun8i_ce_debugfs_show(struct seq_file *seq, void *v) seq_printf(seq, "\tFallback due to SG numbers: %lu\n", ce_algs[i].stat_fb_maxsg); break; +#endif +#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG case CRYPTO_ALG_TYPE_RNG: seq_printf(seq, "%s %s reqs=%lu bytes=%lu\n", ce_algs[i].alg.rng.base.cra_driver_name, ce_algs[i].alg.rng.base.cra_name, ce_algs[i].stat_req, ce_algs[i].stat_bytes); break; +#endif } } #if defined(CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG) && \ @@ -905,6 +909,7 @@ static int sun8i_ce_register_algs(struct sun8i_ce_dev *ce) return err; } break; +#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_HASH case CRYPTO_ALG_TYPE_AHASH: id = ce_algs[i].ce_algo_id; ce_method = ce->variant->alg_hash[id]; @@ -925,6 +930,8 @@ static int sun8i_ce_register_algs(struct sun8i_ce_dev *ce) return err; } break; +#endif +#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG case CRYPTO_ALG_TYPE_RNG: if (ce->variant->prng == CE_ID_NOTSUPP) { dev_info(ce->dev, @@ -942,6 +949,7 @@ static int sun8i_ce_register_algs(struct sun8i_ce_dev *ce) ce_algs[i].ce = NULL; } break; +#endif default: ce_algs[i].ce = NULL; dev_err(ce->dev, "ERROR: tried to register an unknown algo\n"); @@ -963,16 +971,20 @@ static void sun8i_ce_unregister_algs(struct sun8i_ce_dev *ce) ce_algs[i].alg.skcipher.base.base.cra_name); crypto_engine_unregister_skcipher(&ce_algs[i].alg.skcipher); break; +#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_HASH case CRYPTO_ALG_TYPE_AHASH: dev_info(ce->dev, "Unregister %d %s\n", i, ce_algs[i].alg.hash.base.halg.base.cra_name); crypto_engine_unregister_ahash(&ce_algs[i].alg.hash); break; +#endif +#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG case CRYPTO_ALG_TYPE_RNG: dev_info(ce->dev, "Unregister %d %s\n", i, ce_algs[i].alg.rng.base.cra_name); crypto_unregister_rng(&ce_algs[i].alg.rng); break; +#endif } } } diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c index f45685707e0d..59c9bc45ec0f 100644 --- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c +++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c @@ -501,12 +501,15 @@ static int sun8i_ss_debugfs_show(struct seq_file *seq, void *v) seq_printf(seq, "\tFallback due to SG numbers: %lu\n", ss_algs[i].stat_fb_sgnum); break; +#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG case CRYPTO_ALG_TYPE_RNG: seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n", ss_algs[i].alg.rng.base.cra_driver_name, ss_algs[i].alg.rng.base.cra_name, ss_algs[i].stat_req, ss_algs[i].stat_bytes); break; +#endif +#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH case CRYPTO_ALG_TYPE_AHASH: seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n", ss_algs[i].alg.hash.base.halg.base.cra_driver_name, @@ -523,6 +526,7 @@ static int sun8i_ss_debugfs_show(struct seq_file *seq, void *v) seq_printf(seq, "\tFallback due to SG numbers: %lu\n", ss_algs[i].stat_fb_sgnum); break; +#endif } } return 0; @@ -707,6 +711,7 @@ static int sun8i_ss_register_algs(struct sun8i_ss_dev *ss) return err; } break; +#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG case CRYPTO_ALG_TYPE_RNG: err = crypto_register_rng(&ss_algs[i].alg.rng); if (err) { @@ -715,6 +720,8 @@ static int sun8i_ss_register_algs(struct sun8i_ss_dev *ss) ss_algs[i].ss = NULL; } break; +#endif +#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH case CRYPTO_ALG_TYPE_AHASH: id = ss_algs[i].ss_algo_id; ss_method = ss->variant->alg_hash[id]; @@ -735,6 +742,7 @@ static int sun8i_ss_register_algs(struct sun8i_ss_dev *ss) return err; } break; +#endif default: ss_algs[i].ss = NULL; dev_err(ss->dev, "ERROR: tried to register an unknown algo\n"); @@ -756,16 +764,20 @@ static void sun8i_ss_unregister_algs(struct sun8i_ss_dev *ss) ss_algs[i].alg.skcipher.base.base.cra_name); crypto_engine_unregister_skcipher(&ss_algs[i].alg.skcipher); break; +#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG case CRYPTO_ALG_TYPE_RNG: dev_info(ss->dev, "Unregister %d %s\n", i, ss_algs[i].alg.rng.base.cra_name); crypto_unregister_rng(&ss_algs[i].alg.rng); break; +#endif +#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH case CRYPTO_ALG_TYPE_AHASH: dev_info(ss->dev, "Unregister %d %s\n", i, ss_algs[i].alg.hash.base.halg.base.cra_name); crypto_engine_unregister_ahash(&ss_algs[i].alg.hash); break; +#endif } } } diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index b7b6c97d2147..001da785af07 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -31,11 +31,9 @@ #include <crypto/ctr.h> #include <crypto/gcm.h> #include <crypto/sha1.h> -#include <crypto/rng.h> #include <crypto/scatterwalk.h> #include <crypto/skcipher.h> #include <crypto/internal/aead.h> -#include <crypto/internal/rng.h> #include <crypto/internal/skcipher.h> #include "crypto4xx_reg_def.h" #include "crypto4xx_core.h" @@ -985,10 +983,6 @@ static int crypto4xx_register_alg(struct crypto4xx_device *sec_dev, rc = crypto_register_aead(&alg->alg.u.aead); break; - case CRYPTO_ALG_TYPE_RNG: - rc = crypto_register_rng(&alg->alg.u.rng); - break; - default: rc = crypto_register_skcipher(&alg->alg.u.cipher); break; @@ -1014,10 +1008,6 @@ static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev) crypto_unregister_aead(&alg->alg.u.aead); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&alg->alg.u.rng); - break; - default: crypto_unregister_skcipher(&alg->alg.u.cipher); } @@ -1076,69 +1066,6 @@ static irqreturn_t crypto4xx_ce_interrupt_handler_revb(int irq, void *data) PPC4XX_TMO_ERR_INT); } -static int ppc4xx_prng_data_read(struct crypto4xx_device *dev, - u8 *data, unsigned int max) -{ - unsigned int i, curr = 0; - u32 val[2]; - - do { - /* trigger PRN generation */ - writel(PPC4XX_PRNG_CTRL_AUTO_EN, - dev->ce_base + CRYPTO4XX_PRNG_CTRL); - - for (i = 0; i < 1024; i++) { - /* usually 19 iterations are enough */ - if ((readl(dev->ce_base + CRYPTO4XX_PRNG_STAT) & - CRYPTO4XX_PRNG_STAT_BUSY)) - continue; - - val[0] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_0); - val[1] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_1); - break; - } - if (i == 1024) - return -ETIMEDOUT; - - if ((max - curr) >= 8) { - memcpy(data, &val, 8); - data += 8; - curr += 8; - } else { - /* copy only remaining bytes */ - memcpy(data, &val, max - curr); - break; - } - } while (curr < max); - - return curr; -} - -static int crypto4xx_prng_generate(struct crypto_rng *tfm, - const u8 *src, unsigned int slen, - u8 *dstn, unsigned int dlen) -{ - struct rng_alg *alg = crypto_rng_alg(tfm); - struct crypto4xx_alg *amcc_alg; - struct crypto4xx_device *dev; - int ret; - - amcc_alg = container_of(alg, struct crypto4xx_alg, alg.u.rng); - dev = amcc_alg->dev; - - mutex_lock(&dev->core_dev->rng_lock); - ret = ppc4xx_prng_data_read(dev, dstn, dlen); - mutex_unlock(&dev->core_dev->rng_lock); - return ret; -} - - -static int crypto4xx_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - return 0; -} - /* * Supported Crypto Algorithms */ @@ -1268,18 +1195,6 @@ static struct crypto4xx_alg_common crypto4xx_alg[] = { .cra_module = THIS_MODULE, }, } }, - { .type = CRYPTO_ALG_TYPE_RNG, .u.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "crypto4xx_rng", - .cra_priority = 300, - .cra_ctxsize = 0, - .cra_module = THIS_MODULE, - }, - .generate = crypto4xx_prng_generate, - .seed = crypto4xx_prng_seed, - .seedsize = 0, - } }, }; /* @@ -1353,9 +1268,6 @@ static int crypto4xx_probe(struct platform_device *ofdev) core_dev->dev->core_dev = core_dev; core_dev->dev->is_revb = is_revb; core_dev->device = dev; - rc = devm_mutex_init(&ofdev->dev, &core_dev->rng_lock); - if (rc) - return rc; spin_lock_init(&core_dev->lock); INIT_LIST_HEAD(&core_dev->dev->alg_list); ratelimit_default_init(&core_dev->dev->aead_ratelimit); @@ -1382,7 +1294,11 @@ static int crypto4xx_probe(struct platform_device *ofdev) } /* Register for Crypto isr, Crypto Engine IRQ */ - core_dev->irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + core_dev->irq = platform_get_irq(ofdev, 0); + if (core_dev->irq < 0) { + rc = core_dev->irq; + goto err_iomap; + } rc = devm_request_irq(&ofdev->dev, core_dev->irq, is_revb ? crypto4xx_ce_interrupt_handler_revb : crypto4xx_ce_interrupt_handler, diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h index ee36630c670f..66a95733c86d 100644 --- a/drivers/crypto/amcc/crypto4xx_core.h +++ b/drivers/crypto/amcc/crypto4xx_core.h @@ -14,10 +14,8 @@ #define __CRYPTO4XX_CORE_H__ #include <linux/ratelimit.h> -#include <linux/mutex.h> #include <linux/scatterlist.h> #include <crypto/internal/aead.h> -#include <crypto/internal/rng.h> #include <crypto/internal/skcipher.h> #include "crypto4xx_reg_def.h" #include "crypto4xx_sa.h" @@ -108,10 +106,9 @@ struct crypto4xx_core_device { struct crypto4xx_device *dev; struct hwrng *trng; u32 int_status; - u32 irq; + int irq; struct tasklet_struct tasklet; spinlock_t lock; - struct mutex rng_lock; }; struct crypto4xx_ctx { @@ -135,7 +132,6 @@ struct crypto4xx_alg_common { union { struct skcipher_alg cipher; struct aead_alg aead; - struct rng_alg rng; } u; }; diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h index 1038061224da..73d626308a84 100644 --- a/drivers/crypto/amcc/crypto4xx_reg_def.h +++ b/drivers/crypto/amcc/crypto4xx_reg_def.h @@ -90,20 +90,9 @@ #define CRYPTO4XX_BYTE_ORDER_CFG 0x000600d8 #define CRYPTO4XX_ENDIAN_CFG 0x000600d8 -#define CRYPTO4XX_PRNG_STAT 0x00070000 -#define CRYPTO4XX_PRNG_STAT_BUSY 0x1 #define CRYPTO4XX_PRNG_CTRL 0x00070004 #define CRYPTO4XX_PRNG_SEED_L 0x00070008 #define CRYPTO4XX_PRNG_SEED_H 0x0007000c - -#define CRYPTO4XX_PRNG_RES_0 0x00070020 -#define CRYPTO4XX_PRNG_RES_1 0x00070024 -#define CRYPTO4XX_PRNG_RES_2 0x00070028 -#define CRYPTO4XX_PRNG_RES_3 0x0007002C - -#define CRYPTO4XX_PRNG_LFSR_L 0x00070030 -#define CRYPTO4XX_PRNG_LFSR_H 0x00070034 - /* * Initialize CRYPTO ENGINE registers, and memory bases. */ diff --git a/drivers/crypto/amlogic/amlogic-gxl-core.c b/drivers/crypto/amlogic/amlogic-gxl-core.c index 1c18a5b8470e..6cb33949915f 100644 --- a/drivers/crypto/amlogic/amlogic-gxl-core.c +++ b/drivers/crypto/amlogic/amlogic-gxl-core.c @@ -291,8 +291,8 @@ static int meson_crypto_probe(struct platform_device *pdev) return 0; error_alg: meson_unregister_algs(mc); -error_flow: meson_free_chanlist(mc, MAXFLOW - 1); +error_flow: clk_disable_unprepare(mc->busclk); return err; } diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c index 9c380351d2f9..9da9dd6585df 100644 --- a/drivers/crypto/atmel-ecc.c +++ b/drivers/crypto/atmel-ecc.c @@ -56,7 +56,7 @@ static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq, goto free_work_data; /* might want less than we've got */ - n_sz = min_t(size_t, ATMEL_ECC_NIST_P256_N_SIZE, req->dst_len); + n_sz = min(ATMEL_ECC_NIST_P256_N_SIZE, req->dst_len); /* copy the shared secret */ copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, n_sz), @@ -150,7 +150,7 @@ static int atmel_ecdh_generate_public_key(struct kpp_request *req) return -EINVAL; /* might want less than we've got */ - nbytes = min_t(size_t, ATMEL_ECC_PUBKEY_SIZE, req->dst_len); + nbytes = min(ATMEL_ECC_PUBKEY_SIZE, req->dst_len); /* public key was saved at private key generation */ copied = sg_copy_from_buffer(req->dst, @@ -284,15 +284,7 @@ static unsigned int atmel_ecdh_max_size(struct crypto_kpp *tfm) { struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm); - if (ctx->fallback) - return crypto_kpp_maxsize(ctx->fallback); - - /* - * The device only supports NIST P256 ECC keys. The public key size will - * always be the same. Use a macro for the key size to avoid unnecessary - * computations. - */ - return ATMEL_ECC_PUBKEY_SIZE; + return crypto_kpp_maxsize(ctx->fallback); } static struct kpp_alg atmel_ecdh_nist_p256 = { @@ -368,19 +360,16 @@ static void atmel_ecc_remove(struct i2c_client *client) spin_unlock(&driver_data.i2c_list_lock); } -#ifdef CONFIG_OF static const struct of_device_id atmel_ecc_dt_ids[] = { - { - .compatible = "atmel,atecc508a", - }, { - /* sentinel */ - } + { .compatible = "atmel,atecc508a", }, + { .compatible = "atmel,atecc608b", }, + { } }; MODULE_DEVICE_TABLE(of, atmel_ecc_dt_ids); -#endif static const struct i2c_device_id atmel_ecc_id[] = { - { "atecc508a" }, + { .name = "atecc508a" }, + { .name = "atecc608b" }, { } }; MODULE_DEVICE_TABLE(i2c, atmel_ecc_id); @@ -388,7 +377,7 @@ MODULE_DEVICE_TABLE(i2c, atmel_ecc_id); static struct i2c_driver atmel_ecc_driver = { .driver = { .name = "atmel-ecc", - .of_match_table = of_match_ptr(atmel_ecc_dt_ids), + .of_match_table = atmel_ecc_dt_ids, }, .probe = atmel_ecc_probe, .remove = atmel_ecc_remove, diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c index 0e275dbdc8c5..ff19857894d0 100644 --- a/drivers/crypto/atmel-i2c.c +++ b/drivers/crypto/atmel-i2c.c @@ -294,7 +294,7 @@ void atmel_i2c_enqueue(struct atmel_i2c_work_data *work_data, void *areq, int status), void *areq) { - work_data->cbk = (void *)cbk; + work_data->cbk = cbk; work_data->areq = areq; INIT_WORK(&work_data->work, atmel_i2c_work_handler); diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 002b62902553..8e3b8efa8109 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -305,12 +305,12 @@ static size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx) size_t count; while ((ctx->bufcnt < ctx->buflen) && ctx->total) { - count = min(ctx->sg->length - ctx->offset, ctx->total); - count = min(count, ctx->buflen - ctx->bufcnt); + count = min3(ctx->sg->length - ctx->offset, ctx->total, + ctx->buflen - ctx->bufcnt); - if (count <= 0) { + if (count == 0) { /* - * Check if count <= 0 because the buffer is full or + * Check if count == 0 because the buffer is full or * because the sg length is 0. In the latest case, * check if there is another sg in the list, a 0 length * sg doesn't necessarily mean the end of the sg list. @@ -1724,8 +1724,7 @@ static int atmel_sha_hmac_setup(struct atmel_sha_dev *dd, return atmel_sha_hmac_prehash_key(dd, key, keylen); /* Prepare ipad. */ - memcpy((u8 *)hmac->ipad, key, keylen); - memset((u8 *)hmac->ipad + keylen, 0, bs - keylen); + memcpy_and_pad(hmac->ipad, bs, key, keylen, 0); return atmel_sha_hmac_compute_ipad_hash(dd); } diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c index dbb39ed0cea1..4c9af737b33a 100644 --- a/drivers/crypto/atmel-sha204a.c +++ b/drivers/crypto/atmel-sha204a.c @@ -19,6 +19,12 @@ #include <linux/workqueue.h> #include "atmel-i2c.h" +/* + * According to review by Bill Cox [1], the ATSHA204 has very low entropy. + * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html + */ +static const unsigned short atsha204_quality = 1; + static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data, void *areq, int status) { @@ -48,8 +54,8 @@ static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data, if (rng->priv) { work_data = (struct atmel_i2c_work_data *)rng->priv; - max = min(sizeof(work_data->cmd.data), max); - memcpy(data, &work_data->cmd.data, max); + max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max); + memcpy(data, &work_data->cmd.data[RSP_DATA_IDX], max); rng->priv = 0; } else { work_data = kmalloc_obj(*work_data, GFP_ATOMIC); @@ -87,8 +93,8 @@ static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max, if (ret) return ret; - max = min(sizeof(cmd.data), max); - memcpy(data, cmd.data, max); + max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max); + memcpy(data, &cmd.data[RSP_DATA_IDX], max); return max; } @@ -158,6 +164,7 @@ static const struct attribute_group atmel_sha204a_groups = { static int atmel_sha204a_probe(struct i2c_client *client) { struct atmel_i2c_client_priv *i2c_priv; + const unsigned short *quality; int ret; ret = atmel_i2c_probe(client); @@ -171,19 +178,19 @@ static int atmel_sha204a_probe(struct i2c_client *client) i2c_priv->hwrng.name = dev_name(&client->dev); i2c_priv->hwrng.read = atmel_sha204a_rng_read; - /* - * According to review by Bill Cox [1], this HWRNG has very low entropy. - * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html - */ - i2c_priv->hwrng.quality = 1; + quality = i2c_get_match_data(client); + if (quality) + i2c_priv->hwrng.quality = *quality; ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng); - if (ret) - dev_warn(&client->dev, "failed to register RNG (%d)\n", ret); + if (ret) { + dev_err(&client->dev, "failed to register RNG (%d)\n", ret); + return ret; + } ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups); if (ret) { - dev_err(&client->dev, "failed to register sysfs entry\n"); + dev_err(&client->dev, "failed to create sysfs group (%d)\n", ret); return ret; } @@ -194,25 +201,24 @@ static void atmel_sha204a_remove(struct i2c_client *client) { struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &atmel_sha204a_groups); devm_hwrng_unregister(&client->dev, &i2c_priv->hwrng); atmel_i2c_flush_queue(); - sysfs_remove_group(&client->dev.kobj, &atmel_sha204a_groups); - kfree((void *)i2c_priv->hwrng.priv); } -static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = { - { .compatible = "atmel,atsha204", }, - { .compatible = "atmel,atsha204a", }, - { /* sentinel */ } +static const struct of_device_id atmel_sha204a_dt_ids[] = { + { .compatible = "atmel,atsha204" }, + { .compatible = "atmel,atsha204a" }, + { } }; MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids); static const struct i2c_device_id atmel_sha204a_id[] = { - { "atsha204" }, - { "atsha204a" }, - { /* sentinel */ } + { .name = "atsha204", .driver_data = (kernel_ulong_t)&atsha204_quality }, + { .name = "atsha204a", .driver_data = (kernel_ulong_t)NULL }, + { } }; MODULE_DEVICE_TABLE(i2c, atmel_sha204a_id); @@ -222,7 +228,7 @@ static struct i2c_driver atmel_sha204a_driver = { .id_table = atmel_sha204a_id, .driver.name = "atmel-sha204a", - .driver.of_match_table = of_match_ptr(atmel_sha204a_dt_ids), + .driver.of_match_table = atmel_sha204a_dt_ids, }; static int __init atmel_sha204a_init(void) diff --git a/drivers/crypto/axis/artpec6_crypto.c b/drivers/crypto/axis/artpec6_crypto.c index a4793b76300c..2bf8d71c86f4 100644 --- a/drivers/crypto/axis/artpec6_crypto.c +++ b/drivers/crypto/axis/artpec6_crypto.c @@ -706,22 +706,19 @@ artpec6_crypto_setup_out_descr(struct artpec6_crypto_req_common *common, void *dst, unsigned int len, bool eop, bool use_short) { - if (use_short && len < 7) { + dma_addr_t dma_addr; + int ret; + + if (use_short && len < 7) return artpec6_crypto_setup_out_descr_short(common, dst, len, eop); - } else { - int ret; - dma_addr_t dma_addr; - ret = artpec6_crypto_dma_map_single(common, dst, len, - DMA_TO_DEVICE, - &dma_addr); - if (ret) - return ret; + ret = artpec6_crypto_dma_map_single(common, dst, len, DMA_TO_DEVICE, + &dma_addr); + if (ret) + return ret; - return artpec6_crypto_setup_out_descr_phys(common, dma_addr, - len, eop); - } + return artpec6_crypto_setup_out_descr_phys(common, dma_addr, len, eop); } /** artpec6_crypto_setup_in_descr_phys - Setup an in channel with a diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c index 2bce15dc0aa8..240b40ae9cd6 100644 --- a/drivers/crypto/bcm/cipher.c +++ b/drivers/crypto/bcm/cipher.c @@ -4698,9 +4698,9 @@ static void bcm_spu_remove(struct platform_device *pdev) static struct platform_driver bcm_spu_pdriver = { .driver = { - .name = "brcm-spu-crypto", - .of_match_table = of_match_ptr(bcm_spu_dt_ids), - }, + .name = "brcm-spu-crypto", + .of_match_table = bcm_spu_dt_ids, + }, .probe = bcm_spu_probe, .remove = bcm_spu_remove, }; diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 32a6e6e15ee2..ddbd60cf3b3c 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -603,7 +603,7 @@ static int aead_setkey(struct crypto_aead *aead, dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* @@ -639,7 +639,7 @@ static int aead_setkey(struct crypto_aead *aead, dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -680,7 +680,7 @@ static int gcm_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -701,7 +701,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -727,7 +727,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -754,7 +754,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, u32 *desc; const bool is_rfc3686 = alg->caam.rfc3686; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* Here keylen is actual key length */ diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c index 65f6adb6c673..aa779caacfe5 100644 --- a/drivers/crypto/caam/caamalg_qi.c +++ b/drivers/crypto/caam/caamalg_qi.c @@ -212,7 +212,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* @@ -248,7 +248,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -371,7 +371,7 @@ static int gcm_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -475,7 +475,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -581,7 +581,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -631,7 +631,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, const bool is_rfc3686 = alg->caam.rfc3686; int ret = 0; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->cdata.keylen = keylen; diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c index bf10c3dda745..6b47bcc16a50 100644 --- a/drivers/crypto/caam/caamalg_qi2.c +++ b/drivers/crypto/caam/caamalg_qi2.c @@ -301,7 +301,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, dev_dbg(dev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->adata.keylen = keys.authkeylen; @@ -315,7 +315,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen); dma_sync_single_for_device(dev, ctx->key_dma, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -732,7 +732,7 @@ static int gcm_setkey(struct crypto_aead *aead, ret = aes_check_keylen(keylen); if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -828,7 +828,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -927,7 +927,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -955,7 +955,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, u32 *desc; const bool is_rfc3686 = alg->caam.rfc3686; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->cdata.keylen = keylen; diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index ddb2a35aec2d..3cd71810380f 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -505,7 +505,7 @@ static int axcbc_setkey(struct crypto_ahash *ahash, const u8 *key, DMA_TO_DEVICE); ctx->adata.keylen = keylen; - print_hex_dump_debug("axcbc ctx.key@" __stringify(__LINE__)" : ", + print_hex_dump_devel("axcbc ctx.key@" __stringify(__LINE__)" : ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, keylen, 1); return axcbc_set_sh_desc(ahash); @@ -525,7 +525,7 @@ static int acmac_setkey(struct crypto_ahash *ahash, const u8 *key, ctx->adata.key_virt = key; ctx->adata.keylen = keylen; - print_hex_dump_debug("acmac ctx.key@" __stringify(__LINE__)" : ", + print_hex_dump_devel("acmac ctx.key@" __stringify(__LINE__)" : ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); return acmac_set_sh_desc(ahash); diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c index 88cc4fe2a585..de2fcc387477 100644 --- a/drivers/crypto/caam/key_gen.c +++ b/drivers/crypto/caam/key_gen.c @@ -58,7 +58,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, dev_dbg(jrdev, "split keylen %d split keylen padded %d\n", adata->keylen, adata->keylen_pad); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key_in, keylen, 1); if (local_max > max_keylen) @@ -113,7 +113,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, wait_for_completion(&result.completion); ret = result.err; - print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key_out, adata->keylen_pad, 1); } diff --git a/drivers/crypto/cavium/cpt/cptpf_main.c b/drivers/crypto/cavium/cpt/cptpf_main.c index 54de869e5374..9358c1c041d4 100644 --- a/drivers/crypto/cavium/cpt/cptpf_main.c +++ b/drivers/crypto/cavium/cpt/cptpf_main.c @@ -651,6 +651,7 @@ static const struct pci_device_id cpt_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, CPT_81XX_PCI_PF_DEVICE_ID) }, { 0, } /* end of table */ }; +MODULE_DEVICE_TABLE(pci, cpt_id_table); static struct pci_driver cpt_pci_driver = { .name = DRV_NAME, @@ -666,4 +667,3 @@ MODULE_AUTHOR("George Cherian <george.cherian@cavium.com>"); MODULE_DESCRIPTION("Cavium Thunder CPT Physical Function Driver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); -MODULE_DEVICE_TABLE(pci, cpt_id_table); diff --git a/drivers/crypto/cavium/cpt/cptvf_main.c b/drivers/crypto/cavium/cpt/cptvf_main.c index 2c9a2af38876..db76df14c4a0 100644 --- a/drivers/crypto/cavium/cpt/cptvf_main.c +++ b/drivers/crypto/cavium/cpt/cptvf_main.c @@ -835,9 +835,10 @@ static void cptvf_shutdown(struct pci_dev *pdev) /* Supported devices */ static const struct pci_device_id cptvf_id_table[] = { - {PCI_VDEVICE(CAVIUM, CPT_81XX_PCI_VF_DEVICE_ID), 0}, - { 0, } /* end of table */ + { PCI_VDEVICE(CAVIUM, CPT_81XX_PCI_VF_DEVICE_ID) }, + { } /* end of table */ }; +MODULE_DEVICE_TABLE(pci, cptvf_id_table); static struct pci_driver cptvf_pci_driver = { .name = DRV_NAME, @@ -853,4 +854,3 @@ MODULE_AUTHOR("George Cherian <george.cherian@cavium.com>"); MODULE_DESCRIPTION("Cavium Thunder CPT Virtual Function Driver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); -MODULE_DEVICE_TABLE(pci, cptvf_id_table); diff --git a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c index e183b60277ff..de305cbeccbe 100644 --- a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c +++ b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c @@ -108,8 +108,8 @@ static int setup_sgio_components(struct cpt_vf *cptvf, struct buf_ptr *list, sg_cleanup: for (j = 0; j < i; j++) { if (list[j].dma_addr) { - dma_unmap_single(&pdev->dev, list[i].dma_addr, - list[i].size, DMA_BIDIRECTIONAL); + dma_unmap_single(&pdev->dev, list[j].dma_addr, + list[j].size, DMA_BIDIRECTIONAL); } list[j].dma_addr = 0; diff --git a/drivers/crypto/cavium/nitrox/nitrox_main.c b/drivers/crypto/cavium/nitrox/nitrox_main.c index 8664d97261fe..e474c84d8d38 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_main.c +++ b/drivers/crypto/cavium/nitrox/nitrox_main.c @@ -38,9 +38,9 @@ static unsigned int num_devices; * nitrox_pci_tbl - PCI Device ID Table */ static const struct pci_device_id nitrox_pci_tbl[] = { - {PCI_VDEVICE(CAVIUM, CNN55XX_DEV_ID), 0}, + { PCI_VDEVICE(CAVIUM, CNN55XX_DEV_ID) }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, nitrox_pci_tbl); diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c index 5c7f7e02a7d8..b14ce51065d5 100644 --- a/drivers/crypto/ccp/psp-dev.c +++ b/drivers/crypto/ccp/psp-dev.c @@ -316,15 +316,15 @@ void psp_dev_destroy(struct sp_device *sp) if (!psp) return; - sev_dev_destroy(psp); + dbc_dev_destroy(psp); - tee_dev_destroy(psp); + platform_access_dev_destroy(psp); sfs_dev_destroy(psp); - dbc_dev_destroy(psp); + tee_dev_destroy(psp); - platform_access_dev_destroy(psp); + sev_dev_destroy(psp); sp_free_psp_irq(sp, psp); diff --git a/drivers/crypto/ccp/sev-dev-tsm.c b/drivers/crypto/ccp/sev-dev-tsm.c index b07ae529b591..46f2539d2d5a 100644 --- a/drivers/crypto/ccp/sev-dev-tsm.c +++ b/drivers/crypto/ccp/sev-dev-tsm.c @@ -58,13 +58,13 @@ static int stream_enable(struct pci_ide *ide) struct pci_dev *rp = pcie_find_root_port(ide->pdev); int ret; - ret = pci_ide_stream_enable(rp, ide); - if (ret) + ret = pci_ide_stream_enable(ide->pdev, ide); + if (ret && ret != -ENXIO) return ret; - ret = pci_ide_stream_enable(ide->pdev, ide); - if (ret) - pci_ide_stream_disable(rp, ide); + ret = pci_ide_stream_enable(rp, ide); + if (ret && ret != -ENXIO) + pci_ide_stream_disable(ide->pdev, ide); return ret; } @@ -248,12 +248,19 @@ static void dsm_remove(struct pci_tsm *tsm) static int dsm_create(struct tio_dsm *dsm) { struct pci_dev *pdev = dsm->tsm.base_tsm.pdev; - u8 segment_id = pdev->bus ? pci_domain_nr(pdev->bus) : 0; - struct pci_dev *rootport = pcie_find_root_port(pdev); - u16 device_id = pci_dev_id(pdev); + struct pci_dev *rootport; + u8 segment_id; + u16 device_id; u16 root_port_id; u32 lnkcap = 0; + if (!pdev->bus) + return -ENODEV; + + segment_id = pci_domain_nr(pdev->bus); + rootport = pcie_find_root_port(pdev); + device_id = pci_dev_id(pdev); + if (pci_read_config_dword(rootport, pci_pcie_cap(rootport) + PCI_EXP_LNKCAP, &lnkcap)) return -ENODEV; diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index d1e9e0ac63b6..a8eb51ec0ee2 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1328,10 +1328,11 @@ static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg) size_t size; /* - * Ensure the list of HV_FIXED pages that will be passed to firmware - * do not exceed the page-sized argument buffer. + * Ensure the list of HV_FIXED pages passed to the firmware including + * the one about to be written to do not exceed the page-sized argument + * buffer. */ - if ((range_list->num_elements * sizeof(struct sev_data_range) + + if (((range_list->num_elements + 1) * sizeof(struct sev_data_range) + sizeof(struct sev_data_range_list)) > PAGE_SIZE) return -E2BIG; @@ -1355,7 +1356,7 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) { struct sev_data_range_list *snp_range_list __free(kfree) = NULL; struct psp_device *psp = psp_master; - struct sev_data_snp_init_ex data; + struct sev_data_snp_init_ex data = {}; struct sev_device *sev; void *arg = &data; int cmd, rc = 0; @@ -1374,7 +1375,9 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) return -EOPNOTSUPP; } - snp_prepare(); + rc = snp_prepare(); + if (rc) + return rc; /* * Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list @@ -1419,8 +1422,6 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) */ snp_add_hv_fixed_pages(sev, snp_range_list); - memset(&data, 0, sizeof(data)); - if (max_snp_asid) { data.ciphertext_hiding_en = 1; data.max_snp_asid = max_snp_asid; @@ -1487,6 +1488,8 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) &snp_panic_notifier); if (data.tio_en) { + struct page *page; + /* * This executes with the sev_cmd_mutex held so down the stack * snp_reclaim_pages(locked=false) might be needed (which is extremely @@ -1494,12 +1497,14 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) * Instead of exporting __snp_alloc_firmware_pages(), allocate a page * for this one call here. */ - void *tio_status = page_address(__snp_alloc_firmware_pages( - GFP_KERNEL_ACCOUNT | __GFP_ZERO, 0, true)); + page = __snp_alloc_firmware_pages(GFP_KERNEL_ACCOUNT | __GFP_ZERO, + 0, true); + if (page) { + void *tio_status = page_address(page); - if (tio_status) { sev_tsm_init_locked(sev, tio_status); - __snp_free_firmware_pages(virt_to_page(tio_status), 0, true); + + __snp_free_firmware_pages(page, 0, true); } } @@ -1716,29 +1721,11 @@ static int sev_get_platform_state(int *state, int *error) static int sev_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_required) { - struct sev_platform_init_args init_args = {0}; int rc; - rc = _sev_platform_init_locked(&init_args); - if (rc) { - argp->error = SEV_RET_INVALID_PLATFORM_STATE; - return rc; - } - - *shutdown_required = true; - - return 0; -} - -static int snp_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_required) -{ - int error, rc; - - rc = __sev_snp_init_locked(&error, 0); - if (rc) { - argp->error = SEV_RET_INVALID_PLATFORM_STATE; + rc = __sev_platform_init_locked(&argp->error); + if (rc) return rc; - } *shutdown_required = true; @@ -2301,7 +2288,8 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) /* Userspace wants to query the certificate length. */ if (!input.pdh_cert_address || !input.pdh_cert_len || - !input.cert_chain_address) + !input.cert_chain_address || + !input.cert_chain_len) goto cmd; /* Allocate a physically contiguous buffer to store the PDH blob. */ @@ -2440,24 +2428,13 @@ cleanup: static int sev_ioctl_do_snp_commit(struct sev_issue_cmd *argp) { - struct sev_device *sev = psp_master->sev_data; struct sev_data_snp_commit buf; - bool shutdown_required = false; - int ret, error; - - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - return ret; - } + int ret; buf.len = sizeof(buf); ret = __sev_do_cmd_locked(SEV_CMD_SNP_COMMIT, &buf, &argp->error); - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - return ret; } @@ -2465,8 +2442,6 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writable { struct sev_device *sev = psp_master->sev_data; struct sev_user_data_snp_config config; - bool shutdown_required = false; - int ret, error; if (!argp->data) return -EINVAL; @@ -2474,30 +2449,21 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writable if (!writable) return -EPERM; + if (!sev->snp_initialized) + return -ENODEV; + if (copy_from_user(&config, (void __user *)argp->data, sizeof(config))) return -EFAULT; - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - return ret; - } - - ret = __sev_do_cmd_locked(SEV_CMD_SNP_CONFIG, &config, &argp->error); - - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - - return ret; + return __sev_do_cmd_locked(SEV_CMD_SNP_CONFIG, &config, &argp->error); } static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) { struct sev_device *sev = psp_master->sev_data; struct sev_user_data_snp_vlek_load input; - bool shutdown_required = false; - int ret, error; void *blob; + int ret; if (!argp->data) return -EINVAL; @@ -2505,6 +2471,9 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) if (!writable) return -EPERM; + if (!sev->snp_initialized) + return -ENODEV; + if (copy_from_user(&input, u64_to_user_ptr(argp->data), sizeof(input))) return -EFAULT; @@ -2518,18 +2487,7 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) input.vlek_wrapped_address = __psp_pa(blob); - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - goto cleanup; - } - ret = __sev_do_cmd_locked(SEV_CMD_SNP_VLEK_LOAD, &input, &argp->error); - - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - -cleanup: kfree(blob); return ret; diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c index 6ac805d99ccb..ede6ff9ad0c2 100644 --- a/drivers/crypto/ccp/sp-pci.c +++ b/drivers/crypto/ccp/sp-pci.c @@ -552,21 +552,21 @@ static const struct sp_dev_vdata dev_vdata[] = { }; static const struct pci_device_id sp_pci_table[] = { - { PCI_VDEVICE(AMD, 0x1537), (kernel_ulong_t)&dev_vdata[0] }, - { PCI_VDEVICE(AMD, 0x1456), (kernel_ulong_t)&dev_vdata[1] }, - { PCI_VDEVICE(AMD, 0x1468), (kernel_ulong_t)&dev_vdata[2] }, - { PCI_VDEVICE(AMD, 0x1486), (kernel_ulong_t)&dev_vdata[3] }, - { PCI_VDEVICE(AMD, 0x15DF), (kernel_ulong_t)&dev_vdata[4] }, - { PCI_VDEVICE(AMD, 0x14CA), (kernel_ulong_t)&dev_vdata[5] }, - { PCI_VDEVICE(AMD, 0x15C7), (kernel_ulong_t)&dev_vdata[6] }, - { PCI_VDEVICE(AMD, 0x1649), (kernel_ulong_t)&dev_vdata[6] }, - { PCI_VDEVICE(AMD, 0x1134), (kernel_ulong_t)&dev_vdata[7] }, - { PCI_VDEVICE(AMD, 0x17E0), (kernel_ulong_t)&dev_vdata[7] }, - { PCI_VDEVICE(AMD, 0x156E), (kernel_ulong_t)&dev_vdata[8] }, - { PCI_VDEVICE(AMD, 0x17D8), (kernel_ulong_t)&dev_vdata[8] }, - { PCI_VDEVICE(AMD, 0x115A), (kernel_ulong_t)&dev_vdata[9] }, + { PCI_VDEVICE(AMD, 0x1537), .driver_data = (kernel_ulong_t)&dev_vdata[0] }, + { PCI_VDEVICE(AMD, 0x1456), .driver_data = (kernel_ulong_t)&dev_vdata[1] }, + { PCI_VDEVICE(AMD, 0x1468), .driver_data = (kernel_ulong_t)&dev_vdata[2] }, + { PCI_VDEVICE(AMD, 0x1486), .driver_data = (kernel_ulong_t)&dev_vdata[3] }, + { PCI_VDEVICE(AMD, 0x15DF), .driver_data = (kernel_ulong_t)&dev_vdata[4] }, + { PCI_VDEVICE(AMD, 0x14CA), .driver_data = (kernel_ulong_t)&dev_vdata[5] }, + { PCI_VDEVICE(AMD, 0x15C7), .driver_data = (kernel_ulong_t)&dev_vdata[6] }, + { PCI_VDEVICE(AMD, 0x1649), .driver_data = (kernel_ulong_t)&dev_vdata[6] }, + { PCI_VDEVICE(AMD, 0x1134), .driver_data = (kernel_ulong_t)&dev_vdata[7] }, + { PCI_VDEVICE(AMD, 0x17E0), .driver_data = (kernel_ulong_t)&dev_vdata[7] }, + { PCI_VDEVICE(AMD, 0x156E), .driver_data = (kernel_ulong_t)&dev_vdata[8] }, + { PCI_VDEVICE(AMD, 0x17D8), .driver_data = (kernel_ulong_t)&dev_vdata[8] }, + { PCI_VDEVICE(AMD, 0x115A), .driver_data = (kernel_ulong_t)&dev_vdata[9] }, /* Last entry must be zero */ - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, sp_pci_table); diff --git a/drivers/crypto/ccree/cc_aead.c b/drivers/crypto/ccree/cc_aead.c index 81533681f7fb..088c4603047f 100644 --- a/drivers/crypto/ccree/cc_aead.c +++ b/drivers/crypto/ccree/cc_aead.c @@ -3,11 +3,12 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/rtnetlink.h> +#include <linux/string.h> #include <crypto/algapi.h> #include <crypto/internal/aead.h> #include <crypto/authenc.h> #include <crypto/gcm.h> -#include <linux/rtnetlink.h> #include <crypto/internal/des.h> #include "cc_driver.h" #include "cc_buffer_mgr.h" @@ -2569,11 +2570,9 @@ static struct cc_crypto_alg *cc_create_aead_alg(struct cc_alg_template *tmpl, alg = &tmpl->template_aead; - if (snprintf(alg->base.cra_name, CRYPTO_MAX_ALG_NAME, "%s", - tmpl->name) >= CRYPTO_MAX_ALG_NAME) + if (strscpy(alg->base.cra_name, tmpl->name) < 0) return ERR_PTR(-EINVAL); - if (snprintf(alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", - tmpl->driver_name) >= CRYPTO_MAX_ALG_NAME) + if (strscpy(alg->base.cra_driver_name, tmpl->driver_name) < 0) return ERR_PTR(-EINVAL); alg->base.cra_module = THIS_MODULE; diff --git a/drivers/crypto/ccree/cc_cipher.c b/drivers/crypto/ccree/cc_cipher.c index e2cbfdf7a0e4..5339b3796c80 100644 --- a/drivers/crypto/ccree/cc_cipher.c +++ b/drivers/crypto/ccree/cc_cipher.c @@ -3,6 +3,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/string.h> #include <crypto/algapi.h> #include <crypto/internal/skcipher.h> #include <crypto/internal/des.h> @@ -1386,11 +1387,9 @@ static struct cc_crypto_alg *cc_create_alg(const struct cc_alg_template *tmpl, memcpy(alg, &tmpl->template_skcipher, sizeof(*alg)); - if (snprintf(alg->base.cra_name, CRYPTO_MAX_ALG_NAME, "%s", - tmpl->name) >= CRYPTO_MAX_ALG_NAME) + if (strscpy(alg->base.cra_name, tmpl->name) < 0) return ERR_PTR(-EINVAL); - if (snprintf(alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", - tmpl->driver_name) >= CRYPTO_MAX_ALG_NAME) + if (strscpy(alg->base.cra_driver_name, tmpl->driver_name) < 0) return ERR_PTR(-EINVAL); alg->base.cra_module = THIS_MODULE; diff --git a/drivers/crypto/ccree/cc_hash.c b/drivers/crypto/ccree/cc_hash.c index 73179bf725a7..5de721a2ca30 100644 --- a/drivers/crypto/ccree/cc_hash.c +++ b/drivers/crypto/ccree/cc_hash.c @@ -3,6 +3,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/string.h> #include <crypto/algapi.h> #include <crypto/hash.h> #include <crypto/md5.h> @@ -1835,16 +1836,12 @@ static struct cc_hash_alg *cc_alloc_hash_alg(struct cc_hash_template *template, alg = &halg->halg.base; if (keyed) { - snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", - template->mac_name); - snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", - template->mac_driver_name); + strscpy(alg->cra_name, template->mac_name); + strscpy(alg->cra_driver_name, template->mac_driver_name); } else { halg->setkey = NULL; - snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", - template->name); - snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", - template->driver_name); + strscpy(alg->cra_name, template->name); + strscpy(alg->cra_driver_name, template->driver_name); } alg->cra_module = THIS_MODULE; alg->cra_ctxsize = sizeof(struct cc_hash_ctx) + crypto_dma_padding(); diff --git a/drivers/crypto/exynos-rng.c b/drivers/crypto/exynos-rng.c deleted file mode 100644 index 2aaa98f9b44e..000000000000 --- a/drivers/crypto/exynos-rng.c +++ /dev/null @@ -1,399 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * exynos-rng.c - Random Number Generator driver for the Exynos - * - * Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org> - * - * Loosely based on old driver from drivers/char/hw_random/exynos-rng.c: - * Copyright (C) 2012 Samsung Electronics - * Jonghwa Lee <jonghwa3.lee@samsung.com> - */ - -#include <linux/clk.h> -#include <linux/crypto.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/of.h> -#include <linux/platform_device.h> - -#include <crypto/internal/rng.h> - -#define EXYNOS_RNG_CONTROL 0x0 -#define EXYNOS_RNG_STATUS 0x10 - -#define EXYNOS_RNG_SEED_CONF 0x14 -#define EXYNOS_RNG_GEN_PRNG BIT(1) - -#define EXYNOS_RNG_SEED_BASE 0x140 -#define EXYNOS_RNG_SEED(n) (EXYNOS_RNG_SEED_BASE + (n * 0x4)) -#define EXYNOS_RNG_OUT_BASE 0x160 -#define EXYNOS_RNG_OUT(n) (EXYNOS_RNG_OUT_BASE + (n * 0x4)) - -/* EXYNOS_RNG_CONTROL bit fields */ -#define EXYNOS_RNG_CONTROL_START 0x18 -/* EXYNOS_RNG_STATUS bit fields */ -#define EXYNOS_RNG_STATUS_SEED_SETTING_DONE BIT(1) -#define EXYNOS_RNG_STATUS_RNG_DONE BIT(5) - -/* Five seed and output registers, each 4 bytes */ -#define EXYNOS_RNG_SEED_REGS 5 -#define EXYNOS_RNG_SEED_SIZE (EXYNOS_RNG_SEED_REGS * 4) - -enum exynos_prng_type { - EXYNOS_PRNG_UNKNOWN = 0, - EXYNOS_PRNG_EXYNOS4, - EXYNOS_PRNG_EXYNOS5, -}; - -/* - * Driver re-seeds itself with generated random numbers to hinder - * backtracking of the original seed. - * - * Time for next re-seed in ms. - */ -#define EXYNOS_RNG_RESEED_TIME 1000 -#define EXYNOS_RNG_RESEED_BYTES 65536 - -/* - * In polling mode, do not wait infinitely for the engine to finish the work. - */ -#define EXYNOS_RNG_WAIT_RETRIES 100 - -/* Context for crypto */ -struct exynos_rng_ctx { - struct exynos_rng_dev *rng; -}; - -/* Device associated memory */ -struct exynos_rng_dev { - struct device *dev; - enum exynos_prng_type type; - void __iomem *mem; - struct clk *clk; - struct mutex lock; - /* Generated numbers stored for seeding during resume */ - u8 seed_save[EXYNOS_RNG_SEED_SIZE]; - unsigned int seed_save_len; - /* Time of last seeding in jiffies */ - unsigned long last_seeding; - /* Bytes generated since last seeding */ - unsigned long bytes_seeding; -}; - -static struct exynos_rng_dev *exynos_rng_dev; - -static u32 exynos_rng_readl(struct exynos_rng_dev *rng, u32 offset) -{ - return readl_relaxed(rng->mem + offset); -} - -static void exynos_rng_writel(struct exynos_rng_dev *rng, u32 val, u32 offset) -{ - writel_relaxed(val, rng->mem + offset); -} - -static int exynos_rng_set_seed(struct exynos_rng_dev *rng, - const u8 *seed, unsigned int slen) -{ - u32 val; - int i; - - /* Round seed length because loop iterates over full register size */ - slen = ALIGN_DOWN(slen, 4); - - if (slen < EXYNOS_RNG_SEED_SIZE) - return -EINVAL; - - for (i = 0; i < slen ; i += 4) { - unsigned int seed_reg = (i / 4) % EXYNOS_RNG_SEED_REGS; - - val = seed[i] << 24; - val |= seed[i + 1] << 16; - val |= seed[i + 2] << 8; - val |= seed[i + 3] << 0; - - exynos_rng_writel(rng, val, EXYNOS_RNG_SEED(seed_reg)); - } - - val = exynos_rng_readl(rng, EXYNOS_RNG_STATUS); - if (!(val & EXYNOS_RNG_STATUS_SEED_SETTING_DONE)) { - dev_warn(rng->dev, "Seed setting not finished\n"); - return -EIO; - } - - rng->last_seeding = jiffies; - rng->bytes_seeding = 0; - - return 0; -} - -/* - * Start the engine and poll for finish. Then read from output registers - * filling the 'dst' buffer up to 'dlen' bytes or up to size of generated - * random data (EXYNOS_RNG_SEED_SIZE). - * - * On success: return 0 and store number of read bytes under 'read' address. - * On error: return -ERRNO. - */ -static int exynos_rng_get_random(struct exynos_rng_dev *rng, - u8 *dst, unsigned int dlen, - unsigned int *read) -{ - int retry = EXYNOS_RNG_WAIT_RETRIES; - - if (rng->type == EXYNOS_PRNG_EXYNOS4) { - exynos_rng_writel(rng, EXYNOS_RNG_CONTROL_START, - EXYNOS_RNG_CONTROL); - } else if (rng->type == EXYNOS_PRNG_EXYNOS5) { - exynos_rng_writel(rng, EXYNOS_RNG_GEN_PRNG, - EXYNOS_RNG_SEED_CONF); - } - - while (!(exynos_rng_readl(rng, - EXYNOS_RNG_STATUS) & EXYNOS_RNG_STATUS_RNG_DONE) && --retry) - cpu_relax(); - - if (!retry) - return -ETIMEDOUT; - - /* Clear status bit */ - exynos_rng_writel(rng, EXYNOS_RNG_STATUS_RNG_DONE, - EXYNOS_RNG_STATUS); - *read = min_t(size_t, dlen, EXYNOS_RNG_SEED_SIZE); - memcpy_fromio(dst, rng->mem + EXYNOS_RNG_OUT_BASE, *read); - rng->bytes_seeding += *read; - - return 0; -} - -/* Re-seed itself from time to time */ -static void exynos_rng_reseed(struct exynos_rng_dev *rng) -{ - unsigned long next_seeding = rng->last_seeding + \ - msecs_to_jiffies(EXYNOS_RNG_RESEED_TIME); - unsigned long now = jiffies; - unsigned int read = 0; - u8 seed[EXYNOS_RNG_SEED_SIZE]; - - if (time_before(now, next_seeding) && - rng->bytes_seeding < EXYNOS_RNG_RESEED_BYTES) - return; - - if (exynos_rng_get_random(rng, seed, sizeof(seed), &read)) - return; - - exynos_rng_set_seed(rng, seed, read); - - /* Let others do some of their job. */ - mutex_unlock(&rng->lock); - mutex_lock(&rng->lock); -} - -static int exynos_rng_generate(struct crypto_rng *tfm, - const u8 *src, unsigned int slen, - u8 *dst, unsigned int dlen) -{ - struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct exynos_rng_dev *rng = ctx->rng; - unsigned int read = 0; - int ret; - - ret = clk_prepare_enable(rng->clk); - if (ret) - return ret; - - mutex_lock(&rng->lock); - do { - ret = exynos_rng_get_random(rng, dst, dlen, &read); - if (ret) - break; - - dlen -= read; - dst += read; - - exynos_rng_reseed(rng); - } while (dlen > 0); - mutex_unlock(&rng->lock); - - clk_disable_unprepare(rng->clk); - - return ret; -} - -static int exynos_rng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct exynos_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct exynos_rng_dev *rng = ctx->rng; - int ret; - - ret = clk_prepare_enable(rng->clk); - if (ret) - return ret; - - mutex_lock(&rng->lock); - ret = exynos_rng_set_seed(ctx->rng, seed, slen); - mutex_unlock(&rng->lock); - - clk_disable_unprepare(rng->clk); - - return ret; -} - -static int exynos_rng_kcapi_init(struct crypto_tfm *tfm) -{ - struct exynos_rng_ctx *ctx = crypto_tfm_ctx(tfm); - - ctx->rng = exynos_rng_dev; - - return 0; -} - -static struct rng_alg exynos_rng_alg = { - .generate = exynos_rng_generate, - .seed = exynos_rng_seed, - .seedsize = EXYNOS_RNG_SEED_SIZE, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "exynos_rng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct exynos_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = exynos_rng_kcapi_init, - } -}; - -static int exynos_rng_probe(struct platform_device *pdev) -{ - struct exynos_rng_dev *rng; - int ret; - - if (exynos_rng_dev) - return -EEXIST; - - rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); - if (!rng) - return -ENOMEM; - - rng->type = (uintptr_t)of_device_get_match_data(&pdev->dev); - - mutex_init(&rng->lock); - - rng->dev = &pdev->dev; - rng->clk = devm_clk_get(&pdev->dev, "secss"); - if (IS_ERR(rng->clk)) { - dev_err(&pdev->dev, "Couldn't get clock.\n"); - return PTR_ERR(rng->clk); - } - - rng->mem = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(rng->mem)) - return PTR_ERR(rng->mem); - - platform_set_drvdata(pdev, rng); - - exynos_rng_dev = rng; - - ret = crypto_register_rng(&exynos_rng_alg); - if (ret) { - dev_err(&pdev->dev, - "Couldn't register rng crypto alg: %d\n", ret); - exynos_rng_dev = NULL; - } - - return ret; -} - -static void exynos_rng_remove(struct platform_device *pdev) -{ - crypto_unregister_rng(&exynos_rng_alg); - - exynos_rng_dev = NULL; -} - -static int __maybe_unused exynos_rng_suspend(struct device *dev) -{ - struct exynos_rng_dev *rng = dev_get_drvdata(dev); - int ret; - - /* If we were never seeded then after resume it will be the same */ - if (!rng->last_seeding) - return 0; - - rng->seed_save_len = 0; - ret = clk_prepare_enable(rng->clk); - if (ret) - return ret; - - mutex_lock(&rng->lock); - - /* Get new random numbers and store them for seeding on resume. */ - exynos_rng_get_random(rng, rng->seed_save, sizeof(rng->seed_save), - &(rng->seed_save_len)); - - mutex_unlock(&rng->lock); - - dev_dbg(rng->dev, "Stored %u bytes for seeding on system resume\n", - rng->seed_save_len); - - clk_disable_unprepare(rng->clk); - - return 0; -} - -static int __maybe_unused exynos_rng_resume(struct device *dev) -{ - struct exynos_rng_dev *rng = dev_get_drvdata(dev); - int ret; - - /* Never seeded so nothing to do */ - if (!rng->last_seeding) - return 0; - - ret = clk_prepare_enable(rng->clk); - if (ret) - return ret; - - mutex_lock(&rng->lock); - - ret = exynos_rng_set_seed(rng, rng->seed_save, rng->seed_save_len); - - mutex_unlock(&rng->lock); - - clk_disable_unprepare(rng->clk); - - return ret; -} - -static SIMPLE_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_suspend, - exynos_rng_resume); - -static const struct of_device_id exynos_rng_dt_match[] = { - { - .compatible = "samsung,exynos4-rng", - .data = (const void *)EXYNOS_PRNG_EXYNOS4, - }, { - .compatible = "samsung,exynos5250-prng", - .data = (const void *)EXYNOS_PRNG_EXYNOS5, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, exynos_rng_dt_match); - -static struct platform_driver exynos_rng_driver = { - .driver = { - .name = "exynos-rng", - .pm = &exynos_rng_pm_ops, - .of_match_table = exynos_rng_dt_match, - }, - .probe = exynos_rng_probe, - .remove = exynos_rng_remove, -}; - -module_platform_driver(exynos_rng_driver); - -MODULE_DESCRIPTION("Exynos H/W Random Number Generator driver"); -MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/crypto/hisilicon/Kconfig b/drivers/crypto/hisilicon/Kconfig index 1e6d772f4bb6..aeff08ccbadd 100644 --- a/drivers/crypto/hisilicon/Kconfig +++ b/drivers/crypto/hisilicon/Kconfig @@ -75,11 +75,3 @@ config CRYPTO_DEV_HISI_HPRE help Support for HiSilicon HPRE(High Performance RSA Engine) accelerator, which can accelerate RSA and DH algorithms. - -config CRYPTO_DEV_HISI_TRNG - tristate "Support for HISI TRNG Driver" - depends on ARM64 && ACPI - select HW_RANDOM - select CRYPTO_RNG - help - Support for HiSilicon TRNG Driver. diff --git a/drivers/crypto/hisilicon/Makefile b/drivers/crypto/hisilicon/Makefile index 8595a5a5d228..e1068ee9f973 100644 --- a/drivers/crypto/hisilicon/Makefile +++ b/drivers/crypto/hisilicon/Makefile @@ -5,4 +5,3 @@ obj-$(CONFIG_CRYPTO_DEV_HISI_SEC2) += sec2/ obj-$(CONFIG_CRYPTO_DEV_HISI_QM) += hisi_qm.o hisi_qm-objs = qm.o sgl.o debugfs.o obj-$(CONFIG_CRYPTO_DEV_HISI_ZIP) += zip/ -obj-$(CONFIG_CRYPTO_DEV_HISI_TRNG) += trng/ diff --git a/drivers/crypto/hisilicon/hpre/hpre_main.c b/drivers/crypto/hisilicon/hpre/hpre_main.c index 357ab5e5887e..b6903fce6071 100644 --- a/drivers/crypto/hisilicon/hpre/hpre_main.c +++ b/drivers/crypto/hisilicon/hpre/hpre_main.c @@ -55,6 +55,8 @@ #define HPRE_RAS_FE_ENB 0x301418 #define HPRE_OOO_SHUTDOWN_SEL 0x301a3c #define HPRE_HAC_RAS_FE_ENABLE 0 +#define HPRE_RAS_MASK_ALL GENMASK(31, 0) +#define HPRE_RAS_CLEAR_ALL GENMASK(31, 0) #define HPRE_CORE_ENB (HPRE_CLSTR_BASE + HPRE_CORE_EN_OFFSET) #define HPRE_CORE_INI_CFG (HPRE_CLSTR_BASE + HPRE_CORE_INI_CFG_OFFSET) @@ -820,11 +822,8 @@ static void hpre_master_ooo_ctrl(struct hisi_qm *qm, bool enable) static void hpre_hw_error_disable(struct hisi_qm *qm) { - struct hisi_qm_err_mask *dev_err = &qm->err_info.dev_err; - u32 err_mask = dev_err->ce | dev_err->nfe | dev_err->fe; - /* disable hpre hw error interrupts */ - writel(err_mask, qm->io_base + HPRE_INT_MASK); + writel(HPRE_RAS_MASK_ALL, qm->io_base + HPRE_INT_MASK); /* disable HPRE block master OOO when nfe occurs on Kunpeng930 */ hpre_master_ooo_ctrl(qm, false); } @@ -835,7 +834,7 @@ static void hpre_hw_error_enable(struct hisi_qm *qm) u32 err_mask = dev_err->ce | dev_err->nfe | dev_err->fe; /* clear HPRE hw error source if having */ - writel(err_mask, qm->io_base + HPRE_HAC_SOURCE_INT); + writel(HPRE_RAS_CLEAR_ALL, qm->io_base + HPRE_HAC_SOURCE_INT); /* configure error type */ writel(dev_err->ce, qm->io_base + HPRE_RAS_CE_ENB); @@ -1631,12 +1630,10 @@ static int hpre_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_qm_del_list; } - if (qm->uacce) { - ret = uacce_register(qm->uacce); - if (ret) { - pci_err(pdev, "failed to register uacce (%d)!\n", ret); - goto err_with_alg_register; - } + ret = hisi_qm_register_uacce(qm); + if (ret) { + pci_err(pdev, "failed to register uacce (%d)!\n", ret); + goto err_with_alg_register; } if (qm->fun_type == QM_HW_PF && vfs_num) { diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index 3ca47e2a9719..a951d2ef7833 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -128,7 +128,6 @@ #define QM_ABNORMAL_INT_SOURCE 0x100000 #define QM_ABNORMAL_INT_MASK 0x100004 -#define QM_ABNORMAL_INT_MASK_VALUE 0x7fff #define QM_ABNORMAL_INT_STATUS 0x100008 #define QM_ABNORMAL_INT_SET 0x10000c #define QM_ABNORMAL_INF00 0x100010 @@ -153,6 +152,8 @@ #define QM_DB_TIMEOUT BIT(10) #define QM_OF_FIFO_OF BIT(11) #define QM_RAS_AXI_ERROR (BIT(0) | BIT(1) | BIT(12)) +#define QM_RAS_MASK_ALL GENMASK(31, 0) +#define QM_RAS_CLEAR_ALL GENMASK(31, 0) #define QM_RESET_WAIT_TIMEOUT 400 #define QM_PEH_VENDOR_ID 0x1000d8 @@ -246,6 +247,20 @@ #define QM_QOS_MAX_CIR_U 6 #define QM_AUTOSUSPEND_DELAY 3000 +#define QM_DB_DROP_ALL_FUNC_ENABLE GENMASK(63, 0) +#define QM_DB_DROP_ALL_FUNC_DISABLE 0 +#define QM_DEV_DB_DROP 0x0100250 +#define QM_FUN_DB_DROP 0x0038 + +/* qm function err mask */ +#define QM_FUNC_AXI_ERR_ST0 0x100280 +#define QM_RAS_FUNC_ERROR (BIT(0) | BIT(1)) +#define QM_FUNC_RAS_CLEAR_ALL GENMASK(63, 0) + +/* qm isolation state mask */ +#define QM_ISOLATED_STATE BIT(31) +#define QM_ISOLATED_THRESHOLD_MASK GENMASK(15, 0) + /* abnormal status value for stopping queue */ #define QM_STOP_QUEUE_FAIL 1 #define QM_DUMP_SQC_FAIL 3 @@ -286,6 +301,20 @@ enum qm_alg_type { ALG_TYPE_1, }; +/* + * Message format for QM_VF_GET_ISOLATE and QM_PF_SET_ISOLATE commands + * + * These commands use a 32-bit command field (cmd) and 32-bit data field (data) + * + * Command behavior: + * - QM_VF_GET_ISOLATE: VF requests isolation status and threshold + * - QM_PF_SET_ISOLATE: PF sets isolation status and threshold + * + * Data field bit layout: + * - bit31 (MSB): Isolation status flag (1 = isolated, 0 = non-isolated) + * - bit15-0 (16 LSB): Isolation threshold value + * - bit30-16 (15 bits): Reserved + */ enum qm_ifc_cmd { QM_PF_FLR_PREPARE = 0x01, QM_PF_SRST_PREPARE, @@ -296,6 +325,9 @@ enum qm_ifc_cmd { QM_VF_START_FAIL, QM_PF_SET_QOS, QM_VF_GET_QOS, + QM_FUNCTION_RESET, + QM_VF_GET_ISOLATE, + QM_PF_SET_ISOLATE, }; enum qm_basic_type { @@ -473,6 +505,7 @@ static struct qm_typical_qos_table shaper_cbs_s[] = { static void qm_irqs_unregister(struct hisi_qm *qm); static int qm_reset_device(struct hisi_qm *qm); static void hisi_qm_stop_qp(struct hisi_qp *qp); +static int qm_restart(struct hisi_qm *qm); int hisi_qm_q_num_set(const char *val, const struct kernel_param *kp, unsigned int device) @@ -549,6 +582,29 @@ static int qm_wait_reset_finish(struct hisi_qm *qm) return 0; } +static void qm_fun_db_ctrl(struct hisi_qm *qm, bool enable) +{ + u32 val; + + if (qm->ver >= QM_HW_V5) { + val = readl(qm->io_base + QM_FUN_DB_DROP); + val = enable ? (val | BIT(0)) : (val & ~BIT(0)); + + writel(val, qm->io_base + QM_FUN_DB_DROP); + } +} + +static void qm_dev_db_ctrl(struct hisi_qm *qm, bool enable) +{ + u64 val; + + if (qm->ver >= QM_HW_V5 && qm->fun_type == QM_HW_PF) { + val = enable ? QM_DB_DROP_ALL_FUNC_ENABLE : QM_DB_DROP_ALL_FUNC_DISABLE; + + writeq(val, qm->io_base + QM_DEV_DB_DROP); + } +} + static int qm_reset_prepare_ready(struct hisi_qm *qm) { struct pci_dev *pdev = qm->pdev; @@ -1151,18 +1207,20 @@ static void qm_reset_function(struct hisi_qm *qm) return; } + dev_info(dev, "function reset start...\n"); ret = hisi_qm_stop(qm, QM_DOWN); if (ret) { dev_err(dev, "failed to stop qm when reset function\n"); goto clear_bit; } - ret = hisi_qm_start(qm); + ret = qm_restart(qm); if (ret) dev_err(dev, "failed to start qm when reset function\n"); clear_bit: qm_reset_bit_clear(qm); + dev_info(dev, "function reset end...\n"); } static irqreturn_t qm_aeq_thread(int irq, void *data) @@ -1175,6 +1233,11 @@ static irqreturn_t qm_aeq_thread(int irq, void *data) atomic64_inc(&qm->debug.dfx.aeq_irq_cnt); + if (qm_pm_get_sync(qm)) { + dev_err(&qm->pdev->dev, "failed to get runtime PM for aeq handle\n"); + return IRQ_HANDLED; + } + while (QM_AEQE_PHASE(dw0) == qm->status.aeqc_phase) { type = (dw0 >> QM_AEQE_TYPE_SHIFT) & QM_AEQE_TYPE_MASK; qp_id = dw0 & QM_AEQE_CQN_MASK; @@ -1210,6 +1273,8 @@ static irqreturn_t qm_aeq_thread(int irq, void *data) qm_db(qm, 0, QM_DOORBELL_CMD_AEQ, qm->status.aeq_head, 0); + qm_pm_put_sync(qm); + return IRQ_HANDLED; } @@ -1468,7 +1533,7 @@ static int qm_get_vft_v2(struct hisi_qm *qm, u32 *base, u32 *number) static void qm_hw_error_init_v1(struct hisi_qm *qm) { - writel(QM_ABNORMAL_INT_MASK_VALUE, qm->io_base + QM_ABNORMAL_INT_MASK); + writel(QM_RAS_MASK_ALL, qm->io_base + QM_ABNORMAL_INT_MASK); } static void qm_hw_error_cfg(struct hisi_qm *qm) @@ -1477,7 +1542,9 @@ static void qm_hw_error_cfg(struct hisi_qm *qm) qm->error_mask = qm_err->nfe | qm_err->ce | qm_err->fe; /* clear QM hw residual error source */ - writel(qm->error_mask, qm->io_base + QM_ABNORMAL_INT_SOURCE); + writel(QM_RAS_CLEAR_ALL, qm->io_base + QM_ABNORMAL_INT_SOURCE); + if (qm->ver >= QM_HW_V5) + writeq(QM_FUNC_RAS_CLEAR_ALL, qm->io_base + QM_FUNC_AXI_ERR_ST0); /* configure error type */ writel(qm_err->ce, qm->io_base + QM_RAS_CE_ENABLE); @@ -1488,43 +1555,28 @@ static void qm_hw_error_cfg(struct hisi_qm *qm) static void qm_hw_error_init_v2(struct hisi_qm *qm) { - u32 irq_unmask; - qm_hw_error_cfg(qm); - irq_unmask = ~qm->error_mask; - irq_unmask &= readl(qm->io_base + QM_ABNORMAL_INT_MASK); - writel(irq_unmask, qm->io_base + QM_ABNORMAL_INT_MASK); + writel(~qm->error_mask, qm->io_base + QM_ABNORMAL_INT_MASK); } static void qm_hw_error_uninit_v2(struct hisi_qm *qm) { - u32 irq_mask = qm->error_mask; - - irq_mask |= readl(qm->io_base + QM_ABNORMAL_INT_MASK); - writel(irq_mask, qm->io_base + QM_ABNORMAL_INT_MASK); + writel(QM_RAS_MASK_ALL, qm->io_base + QM_ABNORMAL_INT_MASK); } static void qm_hw_error_init_v3(struct hisi_qm *qm) { - u32 irq_unmask; - qm_hw_error_cfg(qm); /* enable close master ooo when hardware error happened */ writel(qm->err_info.qm_err.shutdown_mask, qm->io_base + QM_OOO_SHUTDOWN_SEL); - - irq_unmask = ~qm->error_mask; - irq_unmask &= readl(qm->io_base + QM_ABNORMAL_INT_MASK); - writel(irq_unmask, qm->io_base + QM_ABNORMAL_INT_MASK); + writel(~qm->error_mask, qm->io_base + QM_ABNORMAL_INT_MASK); } static void qm_hw_error_uninit_v3(struct hisi_qm *qm) { - u32 irq_mask = qm->error_mask; - - irq_mask |= readl(qm->io_base + QM_ABNORMAL_INT_MASK); - writel(irq_mask, qm->io_base + QM_ABNORMAL_INT_MASK); + writel(QM_RAS_MASK_ALL, qm->io_base + QM_ABNORMAL_INT_MASK); /* disable close master ooo when hardware error happened */ writel(0x0, qm->io_base + QM_OOO_SHUTDOWN_SEL); @@ -1583,6 +1635,15 @@ static enum acc_err_result qm_hw_error_handle_v2(struct hisi_qm *qm) qm->err_status.is_qm_ecc_mbit = true; qm_log_hw_error(qm, error_status); + /* Trigger func reset only when error is detected in bit 0 or bit 1. */ + if ((qm->ver >= QM_HW_V5) && + (error_status & QM_RAS_FUNC_ERROR) && + (error_status & qm_err->reset_mask) == 0) { + writel(error_status, qm->io_base + QM_ABNORMAL_INT_SOURCE); + writel(qm_err->nfe, qm->io_base + QM_RAS_NFE_ENABLE); + return ACC_ERR_NEED_FUNC_RESET; + } + if (error_status & qm_err->reset_mask) { /* Disable the same error reporting until device is recovered. */ writel(qm_err->nfe & (~error_status), qm->io_base + QM_RAS_NFE_ENABLE); @@ -1734,7 +1795,7 @@ err_unlock: return ret; } -static int qm_ping_all_vfs(struct hisi_qm *qm, enum qm_ifc_cmd cmd) +static int qm_ping_all_vfs(struct hisi_qm *qm, enum qm_ifc_cmd cmd, u32 data) { struct device *dev = &qm->pdev->dev; u32 vfs_num = qm->vfs_num; @@ -1743,7 +1804,7 @@ static int qm_ping_all_vfs(struct hisi_qm *qm, enum qm_ifc_cmd cmd) int ret; u32 i; - ret = qm->ops->set_ifc_begin(qm, cmd, 0, QM_MB_PING_ALL_VFS); + ret = qm->ops->set_ifc_begin(qm, cmd, data, QM_MB_PING_ALL_VFS); if (ret) { dev_err(dev, "failed to send command(0x%x) to all vfs!\n", cmd); qm->ops->set_ifc_end(qm); @@ -2779,6 +2840,7 @@ static enum uacce_dev_state hisi_qm_get_isolate_state(struct uacce_device *uacce static int hisi_qm_isolate_threshold_write(struct uacce_device *uacce, u32 num) { struct hisi_qm *qm = uacce->priv; + int ret; /* Must be set by PF */ if (uacce->is_vf) @@ -2792,6 +2854,18 @@ static int hisi_qm_isolate_threshold_write(struct uacce_device *uacce, u32 num) /* After the policy is updated, need to reset the hardware err list */ qm_hw_err_destroy(qm); + + if (!qm->vfs_num) { + mutex_unlock(&qm->isolate_data.isolate_lock); + return 0; + } + + /* Notify all VFs to update the isolation threshold. */ + if (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) { + ret = qm_ping_all_vfs(qm, QM_PF_SET_ISOLATE, qm->isolate_data.err_threshold); + if (ret) + dev_err(&qm->pdev->dev, "failed to send command to all VFs set isolate!\n"); + } mutex_unlock(&qm->isolate_data.isolate_lock); return 0; @@ -2802,7 +2876,7 @@ static u32 hisi_qm_isolate_threshold_read(struct uacce_device *uacce) struct hisi_qm *qm = uacce->priv; struct hisi_qm *pf_qm; - if (uacce->is_vf) { + if (uacce->is_vf && !test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) { pf_qm = pci_get_drvdata(pci_physfn(qm->pdev)); return pf_qm->isolate_data.err_threshold; } @@ -2889,7 +2963,10 @@ static int qm_alloc_uacce(struct hisi_qm *qm) return -EINVAL; } - uacce->is_vf = pdev->is_virtfn; + if (qm->fun_type == QM_HW_PF) + uacce->is_vf = false; + else + uacce->is_vf = true; uacce->priv = qm; if (qm->ver == QM_HW_V1) @@ -2918,6 +2995,25 @@ static int qm_alloc_uacce(struct hisi_qm *qm) return 0; } +int hisi_qm_register_uacce(struct hisi_qm *qm) +{ + int ret; + + if (!qm->uacce) + return 0; + + dev_info(&qm->pdev->dev, "qm register to uacce\n"); + + if (qm->fun_type == QM_HW_VF && test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) { + ret = qm_ping_pf(qm, QM_VF_GET_ISOLATE); + if (ret) + dev_err(&qm->pdev->dev, "failed to send cmd to PF to get isolate!\n"); + } + + return uacce_register(qm->uacce); +} +EXPORT_SYMBOL_GPL(hisi_qm_register_uacce); + /** * qm_frozen() - Try to froze QM to cut continuous queue request. If * there is user on the QM, return failure without doing anything. @@ -2988,9 +3084,9 @@ void hisi_qm_wait_task_finish(struct hisi_qm *qm, struct hisi_qm_list *qm_list) msleep(WAIT_PERIOD); } - while (test_bit(QM_RST_SCHED, &qm->misc_ctl) || - test_bit(QM_RESETTING, &qm->misc_ctl)) - msleep(WAIT_PERIOD); + /* Cancel possible RAS reset process during the uninstallation procedure. */ + if (qm->fun_type == QM_HW_PF) + cancel_work_sync(&qm->rst_work); if (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) flush_work(&qm->cmd_process); @@ -3366,6 +3462,9 @@ static int __hisi_qm_start(struct hisi_qm *qm) if (ret) return ret; + /* Enables the doorbell function when the device is enabled. */ + qm_dev_db_ctrl(qm, false); + qm_fun_db_ctrl(qm, false); qm_init_prefetch(qm); qm_enable_eq_aeq_interrupts(qm); @@ -3473,7 +3572,7 @@ static void qm_invalid_queues(struct hisi_qm *qm) if (qm->status.stop_reason == QM_NORMAL) return; - if (qm->status.stop_reason == QM_DOWN) + if (qm->status.stop_reason == QM_DOWN || qm->status.stop_reason == QM_SHUTDOWN) hisi_qm_cache_wb(qm); for (i = 0; i < qm->qp_num; i++) { @@ -3517,6 +3616,8 @@ int hisi_qm_stop(struct hisi_qm *qm, enum qm_stop_reason r) if (qm->status.stop_reason != QM_NORMAL) { hisi_qm_set_hw_reset(qm, QM_RESET_STOP_TX_OFFSET); + if (qm->status.stop_reason != QM_SHUTDOWN) + qm_fun_db_ctrl(qm, true); /* * When performing soft reset, the hardware will no longer * do tasks, and the tasks in the device will be flushed @@ -4302,10 +4403,13 @@ static enum acc_err_result qm_process_dev_error(struct hisi_qm *qm) /* log device error */ dev_ret = qm_dev_err_handle(qm); + if (qm_ret == ACC_ERR_NEED_RESET || dev_ret == ACC_ERR_NEED_RESET) + return ACC_ERR_NEED_RESET; - return (qm_ret == ACC_ERR_NEED_RESET || - dev_ret == ACC_ERR_NEED_RESET) ? - ACC_ERR_NEED_RESET : ACC_ERR_RECOVERED; + if (qm_ret == ACC_ERR_NEED_FUNC_RESET) + return ACC_ERR_NEED_FUNC_RESET; + + return ACC_ERR_RECOVERED; } /** @@ -4330,7 +4434,7 @@ pci_ers_result_t hisi_qm_dev_err_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_DISCONNECT; ret = qm_process_dev_error(qm); - if (ret == ACC_ERR_NEED_RESET) + if (ret == ACC_ERR_NEED_RESET || ret == ACC_ERR_NEED_FUNC_RESET) return PCI_ERS_RESULT_NEED_RESET; return PCI_ERS_RESULT_RECOVERED; @@ -4484,7 +4588,7 @@ static int qm_try_stop_vfs(struct hisi_qm *qm, enum qm_ifc_cmd cmd, /* Kunpeng930 supports to notify VFs to stop before PF reset */ if (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) { - ret = qm_ping_all_vfs(qm, cmd); + ret = qm_ping_all_vfs(qm, cmd, 0); if (ret) pci_err(pdev, "failed to send command to all VFs before PF reset!\n"); } else { @@ -4540,7 +4644,7 @@ static int qm_controller_reset_prepare(struct hisi_qm *qm) if (ret) pci_err(pdev, "failed to stop by vfs in soft reset!\n"); - clear_bit(QM_RST_SCHED, &qm->misc_ctl); + qm_dev_db_ctrl(qm, true); return 0; } @@ -4671,6 +4775,7 @@ restart_fail: static int qm_try_start_vfs(struct hisi_qm *qm, enum qm_ifc_cmd cmd) { struct pci_dev *pdev = qm->pdev; + u32 data; int ret; if (!qm->vfs_num) @@ -4684,7 +4789,11 @@ static int qm_try_start_vfs(struct hisi_qm *qm, enum qm_ifc_cmd cmd) /* Kunpeng930 supports to notify VFs to start after PF reset. */ if (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) { - ret = qm_ping_all_vfs(qm, cmd); + data = qm->isolate_data.err_threshold; + if (qm->isolate_data.is_isolate) + data |= QM_ISOLATED_STATE; + /* Broadcasting isolate info via RAS to all VFs. */ + ret = qm_ping_all_vfs(qm, cmd, data); if (ret) pci_warn(pdev, "failed to send cmd to all VFs after PF reset!\n"); } else { @@ -4854,7 +4963,6 @@ static int qm_controller_reset(struct hisi_qm *qm) if (ret) { hisi_qm_set_hw_reset(qm, QM_RESET_STOP_TX_OFFSET); hisi_qm_set_hw_reset(qm, QM_RESET_STOP_RX_OFFSET); - clear_bit(QM_RST_SCHED, &qm->misc_ctl); return ret; } @@ -4917,8 +5025,6 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev) u32 delay = 0; int ret; - hisi_qm_dev_err_uninit(pf_qm); - /* * Check whether there is an ECC mbit error, If it occurs, need to * wait for soft reset to fix it. @@ -4935,6 +5041,8 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev) return; } + hisi_qm_dev_err_uninit(pf_qm); + /* PF obtains the information of VF by querying the register. */ if (qm->fun_type == QM_HW_PF) qm_cmd_uninit(qm); @@ -4946,16 +5054,25 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev) ret = hisi_qm_stop(qm, QM_DOWN); if (ret) { pci_err(pdev, "Failed to stop QM, ret = %d.\n", ret); - hisi_qm_set_hw_reset(qm, QM_RESET_STOP_TX_OFFSET); - hisi_qm_set_hw_reset(qm, QM_RESET_STOP_RX_OFFSET); - return; + goto err_prepare; } ret = qm_wait_vf_prepare_finish(qm); if (ret) pci_err(pdev, "failed to stop by vfs in FLR!\n"); + qm_dev_db_ctrl(qm, true); + pci_info(pdev, "FLR resetting...\n"); + + return; + +err_prepare: + pci_info(pdev, "FLR resetting prepare failed!\n"); + atomic_set(&qm->status.flags, QM_STOP); + hisi_qm_set_hw_reset(qm, QM_RESET_STOP_TX_OFFSET); + hisi_qm_set_hw_reset(qm, QM_RESET_STOP_RX_OFFSET); + qm_dev_db_ctrl(qm, true); } EXPORT_SYMBOL_GPL(hisi_qm_reset_prepare); @@ -5027,14 +5144,13 @@ static irqreturn_t qm_rsvd_irq(int irq, void *data) static irqreturn_t qm_abnormal_irq(int irq, void *data) { struct hisi_qm *qm = data; - enum acc_err_result ret; atomic64_inc(&qm->debug.dfx.abnormal_irq_cnt); - ret = qm_process_dev_error(qm); - if (ret == ACC_ERR_NEED_RESET && - !test_bit(QM_DRIVER_REMOVING, &qm->misc_ctl) && - !test_and_set_bit(QM_RST_SCHED, &qm->misc_ctl)) + + if (!test_bit(QM_DRIVER_REMOVING, &qm->misc_ctl)) schedule_work(&qm->rst_work); + else + pci_warn(qm->pdev, "Driver is down, need to reload driver!\n"); return IRQ_HANDLED; } @@ -5050,27 +5166,72 @@ void hisi_qm_dev_shutdown(struct pci_dev *pdev) struct hisi_qm *qm = pci_get_drvdata(pdev); int ret; - ret = hisi_qm_stop(qm, QM_DOWN); + ret = hisi_qm_stop(qm, QM_SHUTDOWN); if (ret) dev_err(&pdev->dev, "Fail to stop qm in shutdown!\n"); } EXPORT_SYMBOL_GPL(hisi_qm_dev_shutdown); +static u64 qm_get_function_mask(struct hisi_qm *qm) +{ + return readq(qm->io_base + QM_FUNC_AXI_ERR_ST0); +} + +static void qm_clear_function_mask(struct hisi_qm *qm, u64 func_mask) +{ + /* Register write 1 clear */ + writeq(func_mask, qm->io_base + QM_FUNC_AXI_ERR_ST0); +} + +static void qm_function_reset(struct hisi_qm *qm) +{ + struct device *dev = &qm->pdev->dev; + u64 func_mask; + u32 fun_num; + int ret; + + func_mask = qm_get_function_mask(qm); + if (!func_mask) { + dev_info(dev, "no function need reset!\n"); + return; + } + + for (fun_num = 1; fun_num <= qm->vfs_num; fun_num++) { + if (func_mask & BIT(fun_num)) { + ret = qm_ping_single_vf(qm, QM_FUNCTION_RESET, 0, fun_num); + /* When function ping fail, user decides the VF reset method. */ + if (ret) + dev_err(dev, "failed to send command(0x%x) to VF(%u)!\n", + (unsigned int)QM_FUNCTION_RESET, fun_num); + } + } + + if (func_mask & BIT(0)) { + dev_info(dev, "function reset start...\n"); + qm_reset_function(qm); + dev_info(dev, "function reset end!\n"); + } + + qm_clear_function_mask(qm, func_mask); +} + static void hisi_qm_controller_reset(struct work_struct *rst_work) { struct hisi_qm *qm = container_of(rst_work, struct hisi_qm, rst_work); + enum acc_err_result err_result; int ret; ret = qm_pm_get_sync(qm); if (ret) { - clear_bit(QM_RST_SCHED, &qm->misc_ctl); + dev_err(&qm->pdev->dev, "failed to get runtime PM for controller\n"); return; } - /* reset pcie device controller */ - ret = qm_controller_reset(qm); - if (ret) - dev_err(&qm->pdev->dev, "controller reset failed (%d)\n", ret); + err_result = qm_process_dev_error(qm); + if (err_result == ACC_ERR_NEED_RESET) + (void)qm_controller_reset(qm); + else if (err_result == ACC_ERR_NEED_FUNC_RESET) + qm_function_reset(qm); qm_pm_put_sync(qm); } @@ -5117,7 +5278,7 @@ static void qm_pf_reset_vf_done(struct hisi_qm *qm) int ret; pci_restore_state(pdev); - ret = hisi_qm_start(qm); + ret = qm_restart(qm); if (ret) { dev_err(&pdev->dev, "failed to start QM, ret = %d.\n", ret); cmd = QM_VF_START_FAIL; @@ -5131,10 +5292,22 @@ static void qm_pf_reset_vf_done(struct hisi_qm *qm) qm_reset_bit_clear(qm); } -static int qm_wait_pf_reset_finish(struct hisi_qm *qm) +static void qm_vf_update_isolate_info(struct hisi_qm *qm, u32 data) +{ + /* Updating the local isolation status. */ + mutex_lock(&qm->isolate_data.isolate_lock); + if (data & QM_ISOLATED_STATE) + qm->isolate_data.is_isolate = true; + else + qm->isolate_data.is_isolate = false; + qm->isolate_data.err_threshold = data & QM_ISOLATED_THRESHOLD_MASK; + mutex_unlock(&qm->isolate_data.isolate_lock); +} + +static int qm_wait_pf_reset_finish(struct hisi_qm *qm, enum qm_stop_reason stop_reason) { struct device *dev = &qm->pdev->dev; - u32 val, cmd; + u32 val, cmd, data; int ret; /* Wait for reset to finish */ @@ -5151,7 +5324,7 @@ static int qm_wait_pf_reset_finish(struct hisi_qm *qm) * Whether message is got successfully, * VF needs to ack PF by clearing the interrupt. */ - ret = qm->ops->get_ifc(qm, &cmd, NULL, 0); + ret = qm->ops->get_ifc(qm, &cmd, &data, 0); qm_clear_cmd_interrupt(qm, 0); if (ret) { dev_err(dev, "failed to get command from PF in reset done!\n"); @@ -5160,10 +5333,14 @@ static int qm_wait_pf_reset_finish(struct hisi_qm *qm) if (cmd != QM_PF_RESET_DONE) { dev_err(dev, "the command(0x%x) is not reset done!\n", cmd); - ret = -EINVAL; + return -EINVAL; } - return ret; + /* The VF processes the device isolation information received from the RAS reset. */ + if (stop_reason == QM_SOFT_RESET) + qm_vf_update_isolate_info(qm, data); + + return 0; } static void qm_pf_reset_vf_process(struct hisi_qm *qm, @@ -5178,7 +5355,7 @@ static void qm_pf_reset_vf_process(struct hisi_qm *qm, qm_cmd_uninit(qm); qm_pf_reset_vf_prepare(qm, stop_reason); - ret = qm_wait_pf_reset_finish(qm); + ret = qm_wait_pf_reset_finish(qm, stop_reason); if (ret) goto err_get_status; @@ -5189,10 +5366,31 @@ static void qm_pf_reset_vf_process(struct hisi_qm *qm, return; err_get_status: + if (stop_reason == QM_SOFT_RESET) { + /* Update local isolation status on PF-VF reset failure. */ + mutex_lock(&qm->isolate_data.isolate_lock); + qm->isolate_data.is_isolate = true; + mutex_unlock(&qm->isolate_data.isolate_lock); + } qm_cmd_init(qm); qm_reset_bit_clear(qm); } +static void qm_vf_get_isolate_data(struct hisi_qm *qm, u32 fun_num) +{ + u32 data = qm->isolate_data.err_threshold; + struct device *dev = &qm->pdev->dev; + int ret; + + if (qm->isolate_data.is_isolate) + data |= QM_ISOLATED_STATE; + + ret = qm_ping_single_vf(qm, QM_PF_SET_ISOLATE, data, fun_num); + if (ret) + dev_err(dev, "failed to send command(0x%x) to VF(%u)!\n", + (unsigned int)QM_PF_SET_ISOLATE, fun_num); +} + static void qm_handle_cmd_msg(struct hisi_qm *qm, u32 fun_num) { struct device *dev = &qm->pdev->dev; @@ -5224,6 +5422,18 @@ static void qm_handle_cmd_msg(struct hisi_qm *qm, u32 fun_num) case QM_PF_SET_QOS: qm->mb_qos = data; break; + case QM_FUNCTION_RESET: + dev_info(dev, "function reset start...\n"); + qm_reset_function(qm); + dev_info(dev, "function reset end!\n"); + break; + case QM_VF_GET_ISOLATE: + /* Read the isolation policy of the PF during VF initialization. */ + qm_vf_get_isolate_data(qm, fun_num); + break; + case QM_PF_SET_ISOLATE: + qm_vf_update_isolate_info(qm, data); + break; default: dev_err(dev, "unsupported command(0x%x) sent by function(%u)!\n", cmd, fun_num); break; diff --git a/drivers/crypto/hisilicon/sec2/sec_crypto.c b/drivers/crypto/hisilicon/sec2/sec_crypto.c index 2471a4dd0b50..77e0e03cbcab 100644 --- a/drivers/crypto/hisilicon/sec2/sec_crypto.c +++ b/drivers/crypto/hisilicon/sec2/sec_crypto.c @@ -20,7 +20,7 @@ #include "sec.h" #include "sec_crypto.h" -#define SEC_PRIORITY 4001 +#define SEC_PRIORITY 80 #define SEC_XTS_MIN_KEY_SIZE (2 * AES_MIN_KEY_SIZE) #define SEC_XTS_MID_KEY_SIZE (3 * AES_MIN_KEY_SIZE) #define SEC_XTS_MAX_KEY_SIZE (2 * AES_MAX_KEY_SIZE) diff --git a/drivers/crypto/hisilicon/sec2/sec_main.c b/drivers/crypto/hisilicon/sec2/sec_main.c index 056bd8f4da5a..752565200c16 100644 --- a/drivers/crypto/hisilicon/sec2/sec_main.c +++ b/drivers/crypto/hisilicon/sec2/sec_main.c @@ -48,6 +48,7 @@ #define SEC_OOO_SHUTDOWN_SEL 0x301014 #define SEC_RAS_DISABLE 0x0 #define SEC_AXI_ERROR_MASK (BIT(0) | BIT(1)) +#define SEC_RAS_CLEAR_ALL GENMASK(31, 0) #define SEC_MEM_START_INIT_REG 0x301100 #define SEC_MEM_INIT_DONE_REG 0x301104 @@ -752,7 +753,7 @@ static void sec_hw_error_enable(struct hisi_qm *qm) } /* clear SEC hw error source if having */ - writel(err_mask, qm->io_base + SEC_CORE_INT_SOURCE); + writel(SEC_RAS_CLEAR_ALL, qm->io_base + SEC_CORE_INT_SOURCE); /* enable RAS int */ writel(dev_err->ce, qm->io_base + SEC_RAS_CE_REG); @@ -1449,12 +1450,10 @@ static int sec_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_qm_del_list; } - if (qm->uacce) { - ret = uacce_register(qm->uacce); - if (ret) { - pci_err(pdev, "failed to register uacce (%d)!\n", ret); - goto err_alg_unregister; - } + ret = hisi_qm_register_uacce(qm); + if (ret) { + pci_err(pdev, "failed to register uacce (%d)!\n", ret); + goto err_alg_unregister; } if (qm->fun_type == QM_HW_PF && vfs_num) { diff --git a/drivers/crypto/hisilicon/trng/Makefile b/drivers/crypto/hisilicon/trng/Makefile deleted file mode 100644 index d909079f351c..000000000000 --- a/drivers/crypto/hisilicon/trng/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_CRYPTO_DEV_HISI_TRNG) += hisi-trng-v2.o -hisi-trng-v2-objs = trng.o diff --git a/drivers/crypto/hisilicon/trng/trng.c b/drivers/crypto/hisilicon/trng/trng.c deleted file mode 100644 index 5ca0b90859a8..000000000000 --- a/drivers/crypto/hisilicon/trng/trng.c +++ /dev/null @@ -1,390 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2019 HiSilicon Limited. */ - -#include <crypto/internal/rng.h> -#include <linux/acpi.h> -#include <linux/crypto.h> -#include <linux/err.h> -#include <linux/hw_random.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/platform_device.h> -#include <linux/random.h> - -#define HISI_TRNG_REG 0x00F0 -#define HISI_TRNG_BYTES 4 -#define HISI_TRNG_QUALITY 512 -#define HISI_TRNG_VERSION 0x01B8 -#define HISI_TRNG_VER_V1 GENMASK(31, 0) -#define SLEEP_US 10 -#define TIMEOUT_US 10000 -#define SW_DRBG_NUM_SHIFT 2 -#define SW_DRBG_KEY_BASE 0x082C -#define SW_DRBG_SEED(n) (SW_DRBG_KEY_BASE - ((n) << SW_DRBG_NUM_SHIFT)) -#define SW_DRBG_SEED_REGS_NUM 12 -#define SW_DRBG_SEED_SIZE 48 -#define SW_DRBG_BLOCKS 0x0830 -#define SW_DRBG_INIT 0x0834 -#define SW_DRBG_GEN 0x083c -#define SW_DRBG_STATUS 0x0840 -#define SW_DRBG_BLOCKS_NUM 4095 -#define SW_DRBG_DATA_BASE 0x0850 -#define SW_DRBG_DATA_NUM 4 -#define SW_DRBG_DATA(n) (SW_DRBG_DATA_BASE - ((n) << SW_DRBG_NUM_SHIFT)) -#define SW_DRBG_BYTES 16 -#define SW_DRBG_ENABLE_SHIFT 12 -#define SEED_SHIFT_24 24 -#define SEED_SHIFT_16 16 -#define SEED_SHIFT_8 8 -#define SW_MAX_RANDOM_BYTES 65520 - -struct hisi_trng_list { - struct mutex lock; - struct list_head list; - bool is_init; -}; - -struct hisi_trng { - void __iomem *base; - struct hisi_trng_list *trng_list; - struct list_head list; - struct hwrng rng; - u32 ver; - u32 ctx_num; - /* The bytes of the random number generated since the last seeding. */ - u32 random_bytes; - struct mutex lock; -}; - -struct hisi_trng_ctx { - struct hisi_trng *trng; -}; - -static atomic_t trng_active_devs; -static struct hisi_trng_list trng_devices; -static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait); - -static int hisi_trng_set_seed(struct hisi_trng *trng, const u8 *seed) -{ - u32 val, seed_reg, i; - int ret; - - writel(0x0, trng->base + SW_DRBG_BLOCKS); - - for (i = 0; i < SW_DRBG_SEED_SIZE; - i += SW_DRBG_SEED_SIZE / SW_DRBG_SEED_REGS_NUM) { - val = seed[i] << SEED_SHIFT_24; - val |= seed[i + 1UL] << SEED_SHIFT_16; - val |= seed[i + 2UL] << SEED_SHIFT_8; - val |= seed[i + 3UL]; - - seed_reg = (i >> SW_DRBG_NUM_SHIFT) % SW_DRBG_SEED_REGS_NUM; - writel(val, trng->base + SW_DRBG_SEED(seed_reg)); - } - - writel(SW_DRBG_BLOCKS_NUM | (0x1 << SW_DRBG_ENABLE_SHIFT), - trng->base + SW_DRBG_BLOCKS); - writel(0x1, trng->base + SW_DRBG_INIT); - ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, - val, val & BIT(0), SLEEP_US, TIMEOUT_US); - if (ret) { - pr_err("failed to init trng(%d)\n", ret); - return -EIO; - } - - trng->random_bytes = 0; - - return 0; -} - -static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); - struct hisi_trng *trng = ctx->trng; - int ret; - - if (slen < SW_DRBG_SEED_SIZE) { - pr_err("slen(%u) is not matched with trng(%d)\n", slen, - SW_DRBG_SEED_SIZE); - return -EINVAL; - } - - mutex_lock(&trng->lock); - ret = hisi_trng_set_seed(trng, seed); - mutex_unlock(&trng->lock); - - return ret; -} - -static int hisi_trng_reseed(struct hisi_trng *trng) -{ - u8 seed[SW_DRBG_SEED_SIZE]; - int size; - - if (!trng->random_bytes) - return 0; - - size = hisi_trng_read(&trng->rng, seed, SW_DRBG_SEED_SIZE, false); - if (size != SW_DRBG_SEED_SIZE) - return -EIO; - - return hisi_trng_set_seed(trng, seed); -} - -static int hisi_trng_get_bytes(struct hisi_trng *trng, u8 *dstn, unsigned int dlen) -{ - u32 data[SW_DRBG_DATA_NUM]; - u32 currsize = 0; - u32 val = 0; - int ret; - u32 i; - - ret = hisi_trng_reseed(trng); - if (ret) - return ret; - - do { - ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, - val, val & BIT(1), SLEEP_US, TIMEOUT_US); - if (ret) { - pr_err("failed to generate random number(%d)!\n", ret); - break; - } - - for (i = 0; i < SW_DRBG_DATA_NUM; i++) - data[i] = readl(trng->base + SW_DRBG_DATA(i)); - - if (dlen - currsize >= SW_DRBG_BYTES) { - memcpy(dstn + currsize, data, SW_DRBG_BYTES); - currsize += SW_DRBG_BYTES; - } else { - memcpy(dstn + currsize, data, dlen - currsize); - currsize = dlen; - } - - trng->random_bytes += SW_DRBG_BYTES; - writel(0x1, trng->base + SW_DRBG_GEN); - } while (currsize < dlen); - - return ret; -} - -static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dstn, unsigned int dlen) -{ - struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); - struct hisi_trng *trng = ctx->trng; - unsigned int currsize = 0; - unsigned int block_size; - int ret; - - if (!dstn || !dlen) { - pr_err("output is error, dlen %u!\n", dlen); - return -EINVAL; - } - - do { - block_size = min_t(unsigned int, dlen - currsize, SW_MAX_RANDOM_BYTES); - mutex_lock(&trng->lock); - ret = hisi_trng_get_bytes(trng, dstn + currsize, block_size); - mutex_unlock(&trng->lock); - if (ret) - return ret; - currsize += block_size; - } while (currsize < dlen); - - return 0; -} - -static int hisi_trng_init(struct crypto_tfm *tfm) -{ - struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); - struct hisi_trng *trng; - u32 ctx_num = ~0; - - mutex_lock(&trng_devices.lock); - list_for_each_entry(trng, &trng_devices.list, list) { - if (trng->ctx_num < ctx_num) { - ctx_num = trng->ctx_num; - ctx->trng = trng; - } - } - ctx->trng->ctx_num++; - mutex_unlock(&trng_devices.lock); - - return 0; -} - -static void hisi_trng_exit(struct crypto_tfm *tfm) -{ - struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); - - mutex_lock(&trng_devices.lock); - ctx->trng->ctx_num--; - mutex_unlock(&trng_devices.lock); -} - -static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) -{ - struct hisi_trng *trng; - int currsize = 0; - u32 val = 0; - int ret; - - trng = container_of(rng, struct hisi_trng, rng); - - do { - ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, - val, SLEEP_US, TIMEOUT_US); - if (ret) - return currsize; - - if (max - currsize >= HISI_TRNG_BYTES) { - memcpy(buf + currsize, &val, HISI_TRNG_BYTES); - currsize += HISI_TRNG_BYTES; - if (currsize == max) - return currsize; - continue; - } - - /* copy remaining bytes */ - memcpy(buf + currsize, &val, max - currsize); - currsize = max; - } while (currsize < max); - - return currsize; -} - -static struct rng_alg hisi_trng_alg = { - .generate = hisi_trng_generate, - .seed = hisi_trng_seed, - .seedsize = SW_DRBG_SEED_SIZE, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "hisi_stdrng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct hisi_trng_ctx), - .cra_module = THIS_MODULE, - .cra_init = hisi_trng_init, - .cra_exit = hisi_trng_exit, - }, -}; - -static void hisi_trng_add_to_list(struct hisi_trng *trng) -{ - mutex_lock(&trng_devices.lock); - list_add_tail(&trng->list, &trng_devices.list); - mutex_unlock(&trng_devices.lock); -} - -static int hisi_trng_del_from_list(struct hisi_trng *trng) -{ - int ret = -EBUSY; - - mutex_lock(&trng_devices.lock); - if (!trng->ctx_num) { - list_del(&trng->list); - ret = 0; - } - mutex_unlock(&trng_devices.lock); - - return ret; -} - -static int hisi_trng_probe(struct platform_device *pdev) -{ - struct hisi_trng *trng; - int ret; - - trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); - if (!trng) - return -ENOMEM; - - platform_set_drvdata(pdev, trng); - - trng->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(trng->base)) - return PTR_ERR(trng->base); - - trng->ctx_num = 0; - trng->random_bytes = SW_MAX_RANDOM_BYTES; - mutex_init(&trng->lock); - trng->ver = readl(trng->base + HISI_TRNG_VERSION); - if (!trng_devices.is_init) { - INIT_LIST_HEAD(&trng_devices.list); - mutex_init(&trng_devices.lock); - trng_devices.is_init = true; - } - - hisi_trng_add_to_list(trng); - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_inc_return(&trng_active_devs) == 1) { - ret = crypto_register_rng(&hisi_trng_alg); - if (ret) { - dev_err(&pdev->dev, - "failed to register crypto(%d)\n", ret); - atomic_dec_return(&trng_active_devs); - goto err_remove_from_list; - } - } - - trng->rng.name = pdev->name; - trng->rng.read = hisi_trng_read; - trng->rng.quality = HISI_TRNG_QUALITY; - ret = devm_hwrng_register(&pdev->dev, &trng->rng); - if (ret) { - dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret); - goto err_crypto_unregister; - } - - return ret; - -err_crypto_unregister: - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_dec_return(&trng_active_devs) == 0) - crypto_unregister_rng(&hisi_trng_alg); - -err_remove_from_list: - hisi_trng_del_from_list(trng); - return ret; -} - -static void hisi_trng_remove(struct platform_device *pdev) -{ - struct hisi_trng *trng = platform_get_drvdata(pdev); - - /* Wait until the task is finished */ - while (hisi_trng_del_from_list(trng)) - ; - - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_dec_return(&trng_active_devs) == 0) - crypto_unregister_rng(&hisi_trng_alg); -} - -static const struct acpi_device_id hisi_trng_acpi_match[] = { - { "HISI02B3", 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); - -static struct platform_driver hisi_trng_driver = { - .probe = hisi_trng_probe, - .remove = hisi_trng_remove, - .driver = { - .name = "hisi-trng-v2", - .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), - }, -}; - -module_platform_driver(hisi_trng_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); -MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); -MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); diff --git a/drivers/crypto/hisilicon/zip/zip_main.c b/drivers/crypto/hisilicon/zip/zip_main.c index 44df9c859bd8..706a73656977 100644 --- a/drivers/crypto/hisilicon/zip/zip_main.c +++ b/drivers/crypto/hisilicon/zip/zip_main.c @@ -64,7 +64,8 @@ #define HZIP_OOO_SHUTDOWN_SEL 0x30120C #define HZIP_SRAM_ECC_ERR_NUM_SHIFT 16 #define HZIP_SRAM_ECC_ERR_ADDR_SHIFT 24 -#define HZIP_CORE_INT_MASK_ALL GENMASK(12, 0) +#define HZIP_CORE_INT_MASK_ALL GENMASK(31, 0) +#define HZIP_CORE_RAS_CLEAR_ALL GENMASK(31, 0) #define HZIP_AXI_ERROR_MASK (BIT(2) | BIT(3)) #define HZIP_SQE_SIZE 128 #define HZIP_PF_DEF_Q_NUM 64 @@ -696,7 +697,7 @@ static void hisi_zip_hw_error_enable(struct hisi_qm *qm) } /* clear ZIP hw error source if having */ - writel(err_mask, qm->io_base + HZIP_CORE_INT_SOURCE); + writel(HZIP_CORE_RAS_CLEAR_ALL, qm->io_base + HZIP_CORE_INT_SOURCE); /* configure error type */ writel(dev_err->ce, qm->io_base + HZIP_CORE_INT_RAS_CE_ENB); @@ -713,11 +714,8 @@ static void hisi_zip_hw_error_enable(struct hisi_qm *qm) static void hisi_zip_hw_error_disable(struct hisi_qm *qm) { - struct hisi_qm_err_mask *dev_err = &qm->err_info.dev_err; - u32 err_mask = dev_err->ce | dev_err->nfe | dev_err->fe; - /* disable ZIP hw error interrupts */ - writel(err_mask, qm->io_base + HZIP_CORE_INT_MASK_REG); + writel(HZIP_CORE_INT_MASK_ALL, qm->io_base + HZIP_CORE_INT_MASK_REG); hisi_zip_master_ooo_ctrl(qm, false); @@ -1559,12 +1557,10 @@ static int hisi_zip_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_qm_del_list; } - if (qm->uacce) { - ret = uacce_register(qm->uacce); - if (ret) { - pci_err(pdev, "failed to register uacce (%d)!\n", ret); - goto err_qm_alg_unregister; - } + ret = hisi_qm_register_uacce(qm); + if (ret) { + pci_err(pdev, "failed to register uacce (%d)!\n", ret); + goto err_qm_alg_unregister; } if (qm->fun_type == QM_HW_PF && vfs_num > 0) { diff --git a/drivers/crypto/inside-secure/eip93/eip93-main.c b/drivers/crypto/inside-secure/eip93/eip93-main.c index 7dccfdeb7b11..1a8dabc4ada4 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-main.c +++ b/drivers/crypto/inside-secure/eip93/eip93-main.c @@ -433,13 +433,14 @@ static int eip93_crypto_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(eip93->dev, eip93->irq, eip93_irq_handler, NULL, IRQF_ONESHOT, dev_name(eip93->dev), eip93); + if (ret) + return ret; eip93->ring = devm_kcalloc(eip93->dev, 1, sizeof(*eip93->ring), GFP_KERNEL); if (!eip93->ring) return -ENOMEM; ret = eip93_desc_init(eip93); - if (ret) return ret; diff --git a/drivers/crypto/inside-secure/eip93/eip93-regs.h b/drivers/crypto/inside-secure/eip93/eip93-regs.h index 96285ca6fbbe..96d28c6651bd 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-regs.h +++ b/drivers/crypto/inside-secure/eip93/eip93-regs.h @@ -103,7 +103,7 @@ #define EIP93_PE_TARGET_COMMAND_NO_RDR_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x2) #define EIP93_PE_TARGET_COMMAND_WITH_RDR_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x1) #define EIP93_PE_DIRECT_HOST_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x0) -#define EIP93_PE_CONFIG_RST_RING BIT(2) +#define EIP93_PE_CONFIG_RST_RING BIT(1) #define EIP93_PE_CONFIG_RST_PE BIT(0) #define EIP93_REG_PE_STATUS 0x104 #define EIP93_REG_PE_BUF_THRESH 0x10c diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c index fb4936e7afa2..52809e57361a 100644 --- a/drivers/crypto/inside-secure/safexcel.c +++ b/drivers/crypto/inside-secure/safexcel.c @@ -1475,7 +1475,7 @@ static int safexcel_probe_generic(void *pdev, peid = version & 255; /* Detect EIP206 processing pipe */ - version = readl(EIP197_PE(priv) + + EIP197_PE_VERSION(0)); + version = readl(EIP197_PE(priv) + EIP197_PE_VERSION(0)); if (EIP197_REG_LO16(version) != EIP206_VERSION_LE) { dev_err(priv->dev, "EIP%d: EIP206 not detected\n", peid); return -ENODEV; @@ -1893,7 +1893,7 @@ static int safexcel_pci_probe(struct pci_dev *pdev, ent->vendor, ent->device, ent->subvendor, ent->subdevice, ent->driver_data); - priv = kzalloc_obj(*priv); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/drivers/crypto/intel/ixp4xx/ixp4xx_crypto.c b/drivers/crypto/intel/ixp4xx/ixp4xx_crypto.c index fcc0cf4df637..5b90cf0fb0e4 100644 --- a/drivers/crypto/intel/ixp4xx/ixp4xx_crypto.c +++ b/drivers/crypto/intel/ixp4xx/ixp4xx_crypto.c @@ -884,8 +884,9 @@ static struct buffer_desc *chainup_buffers(struct device *dev, ptr = sg_virt(sg); next_buf = dma_pool_alloc(buffer_pool, flags, &next_buf_phys); if (!next_buf) { - buf = NULL; - break; + buf->next = NULL; + buf->phys_next = 0; + return NULL; } sg_dma_address(sg) = dma_map_single(dev, ptr, len, dir); buf->next = next_buf; @@ -983,7 +984,7 @@ static int ablk_perform(struct skcipher_request *req, int encrypt) unsigned int nbytes = req->cryptlen; enum dma_data_direction src_direction = DMA_BIDIRECTIONAL; struct ablk_ctx *req_ctx = skcipher_request_ctx(req); - struct buffer_desc src_hook; + struct buffer_desc *buf, src_hook; struct device *dev = &pdev->dev; unsigned int offset; gfp_t flags = req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? @@ -1025,22 +1026,24 @@ static int ablk_perform(struct skcipher_request *req, int encrypt) /* This was never tested by Intel * for more than one dst buffer, I think. */ req_ctx->dst = NULL; - if (!chainup_buffers(dev, req->dst, nbytes, &dst_hook, - flags, DMA_FROM_DEVICE)) - goto free_buf_dest; - src_direction = DMA_TO_DEVICE; + buf = chainup_buffers(dev, req->dst, nbytes, &dst_hook, + flags, DMA_FROM_DEVICE); req_ctx->dst = dst_hook.next; crypt->dst_buf = dst_hook.phys_next; + if (!buf) + goto free_buf_dest; + src_direction = DMA_TO_DEVICE; } else { req_ctx->dst = NULL; } req_ctx->src = NULL; - if (!chainup_buffers(dev, req->src, nbytes, &src_hook, flags, - src_direction)) - goto free_buf_src; - + buf = chainup_buffers(dev, req->src, nbytes, &src_hook, flags, + src_direction); req_ctx->src = src_hook.next; crypt->src_buf = src_hook.phys_next; + if (!buf) + goto free_buf_src; + crypt->ctl_flags |= CTL_FLAG_PERFORM_ABLK; qmgr_put_entry(send_qid, crypt_virt2phys(crypt)); BUG_ON(qmgr_stat_overflow(send_qid)); diff --git a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c index cfa00daeb4fb..0f0827e2b0bd 100644 --- a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c @@ -101,7 +101,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Enable PCI device */ ret = pcim_enable_device(pdev); if (ret) { - dev_err(&pdev->dev, "Can't enable PCI device.\n"); + pci_err(pdev, "Can't enable PCI device.\n"); goto out_err; } @@ -131,7 +131,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ret = pcim_request_all_regions(pdev, pci_name(pdev)); if (ret) { - dev_err(&pdev->dev, "Failed to request PCI regions.\n"); + pci_err(pdev, "Failed to request PCI regions.\n"); goto out_err; } @@ -140,16 +140,14 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar = &accel_pci_dev->pci_bars[i++]; bar->virt_addr = pcim_iomap(pdev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to ioremap PCI region.\n"); + pci_err(pdev, "Failed to ioremap PCI region.\n"); ret = -ENOMEM; goto out_err; } } - pci_set_master(pdev); - if (pci_save_state(pdev)) { - dev_err(&pdev->dev, "Failed to save pci state.\n"); + pci_err(pdev, "Failed to save pci state.\n"); ret = -ENOMEM; goto out_err; } @@ -210,6 +208,5 @@ MODULE_AUTHOR("Intel"); MODULE_FIRMWARE(ADF_420XX_FW); MODULE_FIRMWARE(ADF_420XX_MMP); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_SOFTDEP("pre: crypto-intel_qat"); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c index c9be5dcddb27..aa95f762cb4b 100644 --- a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c @@ -103,7 +103,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Enable PCI device */ ret = pcim_enable_device(pdev); if (ret) { - dev_err(&pdev->dev, "Can't enable PCI device.\n"); + pci_err(pdev, "Can't enable PCI device.\n"); goto out_err; } @@ -133,7 +133,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ret = pcim_request_all_regions(pdev, pci_name(pdev)); if (ret) { - dev_err(&pdev->dev, "Failed to request PCI regions.\n"); + pci_err(pdev, "Failed to request PCI regions.\n"); goto out_err; } @@ -142,16 +142,14 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar = &accel_pci_dev->pci_bars[i++]; bar->virt_addr = pcim_iomap(pdev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to ioremap PCI region.\n"); + pci_err(pdev, "Failed to ioremap PCI region.\n"); ret = -ENOMEM; goto out_err; } } - pci_set_master(pdev); - if (pci_save_state(pdev)) { - dev_err(&pdev->dev, "Failed to save pci state.\n"); + pci_err(pdev, "Failed to save pci state.\n"); ret = -ENOMEM; goto out_err; } @@ -214,6 +212,5 @@ MODULE_FIRMWARE(ADF_402XX_FW); MODULE_FIRMWARE(ADF_4XXX_MMP); MODULE_FIRMWARE(ADF_402XX_MMP); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_SOFTDEP("pre: crypto-intel_qat"); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c index 205680797e2c..388087e64540 100644 --- a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c +++ b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c @@ -752,10 +752,12 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) unsigned long mask; u32 caps = 0; u32 fusectl1; + u32 fusectl0; if (adf_6xxx_is_wcy(GET_HW_DATA(accel_dev))) return get_accel_cap_wcy(accel_dev); + fusectl0 = GET_HW_DATA(accel_dev)->fuses[ADF_FUSECTL0]; fusectl1 = GET_HW_DATA(accel_dev)->fuses[ADF_FUSECTL1]; /* Read accelerator capabilities mask */ @@ -785,7 +787,8 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) capabilities_asym = ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC | ICP_ACCEL_CAPABILITIES_SM2 | - ICP_ACCEL_CAPABILITIES_ECEDMONT; + ICP_ACCEL_CAPABILITIES_ECEDMONT | + ICP_ACCEL_CAPABILITIES_KPT; if (fusectl1 & ICP_ACCEL_GEN6_MASK_PKE_SLICE) { capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC; @@ -793,6 +796,9 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev) capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_ECEDMONT; } + if (fusectl0 & ADF_GEN6_KPT_FUSE_BIT) + capabilities_asym &= ~ICP_ACCEL_CAPABILITIES_KPT; + capabilities_dc = ICP_ACCEL_CAPABILITIES_COMPRESSION | ICP_ACCEL_CAPABILITIES_LZ4_COMPRESSION | ICP_ACCEL_CAPABILITIES_LZ4S_COMPRESSION | @@ -960,6 +966,18 @@ static int dev_config(struct adf_accel_dev *accel_dev) return ret; } +static void adf_gen6_init_kpt(struct adf_kpt_hw_data *kpt_data) +{ + kpt_data->max_swk_cnt_per_fn_pasid = ADF_6XXX_KPT_MAX_SWK_COUNT_PER_FNPASID; + kpt_data->max_swk_ttl = ADF_6XXX_KPT_MAX_SWK_TTL; + + kpt_data->user_input.enable = false; + kpt_data->user_input.swk_shared = ADF_6XXX_KPT_DEFAULT_SWK_SHARED_MODE; + kpt_data->user_input.swk_max_ttl = ADF_6XXX_KPT_DEFAULT_SWK_TTL; + kpt_data->user_input.swk_cnt_per_fn = ADF_6XXX_KPT_DEFAULT_SWK_CNT_PER_FN; + kpt_data->user_input.swk_cnt_per_pasid = ADF_6XXX_KPT_DEFAULT_SWK_CNT_PER_PASID; +} + static void adf_gen6_init_rl_data(struct adf_rl_hw_data *rl_data) { rl_data->pciout_tb_offset = ADF_GEN6_RL_TOKEN_PCIEOUT_BUCKET_OFFSET; @@ -1057,6 +1075,7 @@ void adf_init_hw_data_6xxx(struct adf_hw_device_data *hw_data) adf_gen6_init_tl_data(&hw_data->tl_data); adf_gen6_init_rl_data(&hw_data->rl_data); adf_gen6_init_anti_rb(&hw_data->anti_rb_data); + adf_gen6_init_kpt(&hw_data->kpt_data); } void adf_clean_hw_data_6xxx(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h index e4d433bdd379..2719ddbc1e05 100644 --- a/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h +++ b/drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h @@ -58,6 +58,7 @@ /* Fuse bits */ #define ADF_GEN6_ANTI_RB_FUSE_BIT BIT(24) +#define ADF_GEN6_KPT_FUSE_BIT BIT(16) /* * Watchdog timers @@ -164,6 +165,14 @@ /* Clock frequency */ #define ADF_6XXX_AE_FREQ (1000 * HZ_PER_MHZ) +/* KPT */ +#define ADF_6XXX_KPT_MAX_SWK_COUNT_PER_FNPASID 128 +#define ADF_6XXX_KPT_MAX_SWK_TTL 31536000 +#define ADF_6XXX_KPT_DEFAULT_SWK_SHARED_MODE 1 +#define ADF_6XXX_KPT_DEFAULT_SWK_TTL 0 +#define ADF_6XXX_KPT_DEFAULT_SWK_CNT_PER_FN 0 +#define ADF_6XXX_KPT_DEFAULT_SWK_CNT_PER_PASID 0 + enum icp_qat_gen6_slice_mask { ICP_ACCEL_GEN6_MASK_UCS_SLICE = BIT(0), ICP_ACCEL_GEN6_MASK_AUTH_SLICE = BIT(1), diff --git a/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c index c52462a48c34..319b4251ad46 100644 --- a/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c @@ -13,6 +13,7 @@ #include <adf_cfg.h> #include <adf_common_drv.h> #include <adf_dbgfs.h> +#include <adf_sysfs_kpt.h> #include "adf_gen6_shared.h" #include "adf_6xxx_hw_data.h" @@ -189,8 +190,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - /* * The PCI config space is saved at this point and will be restored * after a Function Level Reset (FLR) as the FLR does not completely @@ -219,6 +218,11 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return ret; ret = adf_sysfs_init(accel_dev); + if (ret) + return ret; + + if (!(hw_data->fuses[ADF_FUSECTL0] & ADF_GEN6_KPT_FUSE_BIT)) + ret = adf_sysfs_init_kpt(accel_dev); return ret; } diff --git a/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c index bceb5dd8b148..e816cc00632f 100644 --- a/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c @@ -162,15 +162,14 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar->size = pci_resource_len(pdev, bar_nr); bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr); + pci_err(pdev, "Failed to map BAR %d\n", bar_nr); ret = -EFAULT; goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { - dev_err(&pdev->dev, "Failed to save pci state\n"); + pci_err(pdev, "Failed to save pci state\n"); ret = -ENOMEM; goto out_err_free_reg; } @@ -256,5 +255,4 @@ MODULE_AUTHOR("Intel"); MODULE_FIRMWARE(ADF_C3XXX_FW); MODULE_FIRMWARE(ADF_C3XXX_MMP); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c b/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c index c622793e94a8..1c77f0a1882b 100644 --- a/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c @@ -158,12 +158,11 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar->size = pci_resource_len(pdev, bar_nr); bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr); + pci_err(pdev, "Failed to map BAR %d\n", bar_nr); ret = -EFAULT; goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); @@ -225,5 +224,4 @@ module_exit(adfdrv_release); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel"); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/intel/qat/qat_c62x/adf_drv.c b/drivers/crypto/intel/qat/qat_c62x/adf_drv.c index 23ccb72b6ea2..f48f3b437545 100644 --- a/drivers/crypto/intel/qat/qat_c62x/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c62x/adf_drv.c @@ -162,15 +162,14 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar->size = pci_resource_len(pdev, bar_nr); bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr); + pci_err(pdev, "Failed to map BAR %d\n", bar_nr); ret = -EFAULT; goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { - dev_err(&pdev->dev, "Failed to save pci state\n"); + pci_err(pdev, "Failed to save pci state\n"); ret = -ENOMEM; goto out_err_free_reg; } @@ -256,5 +255,4 @@ MODULE_AUTHOR("Intel"); MODULE_FIRMWARE(ADF_C62X_FW); MODULE_FIRMWARE(ADF_C62X_MMP); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c b/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c index 4840d44bbd5b..b96f19e31d05 100644 --- a/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c @@ -158,12 +158,11 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar->size = pci_resource_len(pdev, bar_nr); bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr); + pci_err(pdev, "Failed to map BAR %d\n", bar_nr); ret = -EFAULT; goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); @@ -225,5 +224,4 @@ module_exit(adfdrv_release); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel"); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/intel/qat/qat_common/Makefile b/drivers/crypto/intel/qat/qat_common/Makefile index 9478111c8437..306ec71bb6fa 100644 --- a/drivers/crypto/intel/qat/qat_common/Makefile +++ b/drivers/crypto/intel/qat/qat_common/Makefile @@ -9,7 +9,6 @@ intel_qat-y := adf_accel_engine.o \ adf_cfg.o \ adf_cfg_services.o \ adf_clock.o \ - adf_ctl_drv.o \ adf_dc.o \ adf_dev_mgr.o \ adf_gen2_config.o \ @@ -26,11 +25,14 @@ intel_qat-y := adf_accel_engine.o \ adf_hw_arbiter.o \ adf_init.o \ adf_isr.o \ + adf_kpt.o \ + adf_module.o \ adf_mstate_mgr.o \ adf_rl_admin.o \ adf_rl.o \ adf_sysfs.o \ adf_sysfs_anti_rb.o \ + adf_sysfs_kpt.o \ adf_sysfs_ras_counters.o \ adf_sysfs_rl.o \ adf_timer.o \ diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index 03a4e9690208..f2d70641c377 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -14,6 +14,7 @@ #include "adf_anti_rb.h" #include "adf_cfg_common.h" #include "adf_dc.h" +#include "adf_kpt.h" #include "adf_rl.h" #include "adf_telemetry.h" #include "adf_pfvf_msg.h" @@ -335,6 +336,7 @@ struct adf_hw_device_data { struct adf_rl_hw_data rl_data; struct adf_tl_hw_data tl_data; struct adf_anti_rb_hw_data anti_rb_data; + struct adf_kpt_hw_data kpt_data; struct qat_migdev_ops vfmig_ops; const char *fw_name; const char *fw_mmp_name; @@ -480,6 +482,8 @@ struct adf_accel_dev { struct { /* protects VF2PF interrupts access */ spinlock_t vf2pf_ints_lock; + /* prevents VF2PF handling from racing with VF state teardown */ + bool vf2pf_disabled; /* vf_info is non-zero when SR-IOV is init'ed */ struct adf_accel_vf_info *vf_info; } pf; diff --git a/drivers/crypto/intel/qat/qat_common/adf_admin.c b/drivers/crypto/intel/qat/qat_common/adf_admin.c index 841aa802c79e..1eea74c2a8a5 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_admin.c +++ b/drivers/crypto/intel/qat/qat_common/adf_admin.c @@ -13,6 +13,7 @@ #include "adf_common_drv.h" #include "adf_cfg.h" #include "adf_heartbeat.h" +#include "adf_kpt.h" #include "icp_qat_fw_init_admin.h" #define ADF_ADMIN_MAILBOX_STRIDE 0x1000 @@ -606,6 +607,44 @@ int adf_send_admin_arb_commit(struct adf_accel_dev *accel_dev) return adf_send_admin_svn(accel_dev, ICP_QAT_FW_SVN_COMMIT, &resp); } +static_assert(sizeof(struct icp_qat_fw_init_admin_kpt_cfg) < PAGE_SIZE); + +static void adf_cfg_kpt_config(struct adf_accel_dev *accel_dev, + struct icp_qat_fw_init_admin_kpt_cfg *kpt_config) +{ + struct adf_kpt_interface_data *user_data = GET_KPT_USER_DATA(accel_dev); + + kpt_config->swk_cnt_per_fn = user_data->swk_cnt_per_fn; + kpt_config->swk_cnt_per_pasid = user_data->swk_cnt_per_pasid; + kpt_config->swk_ttl_in_secs = user_data->swk_max_ttl; + kpt_config->swk_shared_disable = !user_data->swk_shared; +} + +int adf_send_admin_kpt_init(struct adf_accel_dev *accel_dev, void *init_cfg, + size_t init_cfg_sz, dma_addr_t init_ptr) +{ + struct icp_qat_fw_init_admin_kpt_cfg *kpt_config = init_cfg; + u32 ae_mask = GET_HW_DATA(accel_dev)->admin_ae_mask; + struct icp_qat_fw_init_admin_resp resp = { }; + struct icp_qat_fw_init_admin_req req = { }; + int ret; + + if (!kpt_config || init_cfg_sz < sizeof(*kpt_config)) + return -EINVAL; + + adf_cfg_kpt_config(accel_dev, kpt_config); + + req.cmd_id = ICP_QAT_FW_KPT_ENABLE; + req.init_cfg_ptr = init_ptr; + req.init_cfg_sz = sizeof(*kpt_config); + + ret = adf_send_admin(accel_dev, &req, &resp, ae_mask); + if (ret) + dev_err(&GET_DEV(accel_dev), "Failed to send KPT init admin message\n"); + + return ret; +} + int adf_init_admin_comms(struct adf_accel_dev *accel_dev) { struct adf_admin_comms *admin; diff --git a/drivers/crypto/intel/qat/qat_common/adf_admin.h b/drivers/crypto/intel/qat/qat_common/adf_admin.h index 9704219f2eb7..44eb6dc92e45 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_admin.h +++ b/drivers/crypto/intel/qat/qat_common/adf_admin.h @@ -29,5 +29,7 @@ int adf_send_admin_tl_start(struct adf_accel_dev *accel_dev, int adf_send_admin_tl_stop(struct adf_accel_dev *accel_dev); int adf_send_admin_arb_query(struct adf_accel_dev *accel_dev, int cmd, u8 *svn); int adf_send_admin_arb_commit(struct adf_accel_dev *accel_dev); +int adf_send_admin_kpt_init(struct adf_accel_dev *accel_dev, void *init_cfg, + size_t init_cfg_sz, dma_addr_t init_ptr); #endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index ed01fb9ad74e..d58cd7fbf707 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -17,36 +17,80 @@ struct adf_fatal_error_data { static struct workqueue_struct *device_reset_wq; static struct workqueue_struct *device_sriov_wq; -static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, - pci_channel_state_t state) +static pci_ers_result_t reset_prepare(struct pci_dev *pdev) { struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); - dev_info(&pdev->dev, "Acceleration driver hardware error detected.\n"); if (!accel_dev) { - dev_err(&pdev->dev, "Can't find acceleration device\n"); + pci_err(pdev, "Can't find acceleration device\n"); return PCI_ERS_RESULT_DISCONNECT; } - if (state == pci_channel_io_perm_failure) { - dev_err(&pdev->dev, "Can't recover from device error\n"); - return PCI_ERS_RESULT_DISCONNECT; - } + if (!adf_dev_started(accel_dev)) + return PCI_ERS_RESULT_CAN_RECOVER; set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); if (accel_dev->hw_device->exit_arb) { dev_dbg(&pdev->dev, "Disabling arbitration\n"); accel_dev->hw_device->exit_arb(accel_dev); } - adf_error_notifier(accel_dev); - adf_pf2vf_notify_fatal_error(accel_dev); adf_dev_restarting_notify(accel_dev); - pci_clear_master(pdev); adf_dev_down(accel_dev); return PCI_ERS_RESULT_NEED_RESET; } +static pci_ers_result_t reset_done(struct pci_dev *pdev) +{ + struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); + int res; + + if (!accel_dev) { + pci_err(pdev, "Can't find acceleration device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (!adf_devmgr_in_reset(accel_dev)) + goto reset_complete; + + pci_restore_state(pdev); + res = adf_dev_up(accel_dev, false); + if (res && res != -EALREADY) + return PCI_ERS_RESULT_DISCONNECT; + + adf_reenable_sriov(accel_dev); + adf_pf2vf_notify_restarted(accel_dev); + adf_dev_restarted_notify(accel_dev); + clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); + +reset_complete: + pci_info(pdev, "Device reset completed successfully\n"); + + return PCI_ERS_RESULT_RECOVERED; +} + +static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); + + pci_info(pdev, "Acceleration driver hardware error detected.\n"); + if (!accel_dev) { + pci_err(pdev, "Can't find acceleration device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (state == pci_channel_io_perm_failure) { + pci_err(pdev, "Can't recover from device error\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + adf_error_notifier(accel_dev); + adf_pf2vf_notify_fatal_error(accel_dev); + + return reset_prepare(pdev); +} + /* reset dev data */ struct adf_reset_dev_data { int mode; @@ -72,10 +116,9 @@ void adf_reset_sbr(struct adf_accel_dev *accel_dev) parent = pdev; if (!pci_wait_for_pending_transaction(pdev)) - dev_info(&GET_DEV(accel_dev), - "Transaction still in progress. Proceeding\n"); + pci_info(pdev, "Transaction still in progress. Proceeding\n"); - dev_info(&GET_DEV(accel_dev), "Secondary bus reset\n"); + pci_info(pdev, "Secondary bus reset\n"); pci_read_config_word(parent, PCI_BRIDGE_CONTROL, &bridge_ctl); bridge_ctl |= PCI_BRIDGE_CTL_BUS_RESET; @@ -106,6 +149,13 @@ void adf_dev_restore(struct adf_accel_dev *accel_dev) } } +void adf_set_bme(struct adf_accel_dev *accel_dev) +{ + struct pci_dev *pdev = accel_to_pci_dev(accel_dev); + + pci_set_master(pdev); +} + static void adf_device_sriov_worker(struct work_struct *work) { struct adf_sriov_dev_data *sriov_data = @@ -156,13 +206,14 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev, struct adf_reset_dev_data *reset_data; if (!adf_dev_started(accel_dev) || - test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) + test_and_set_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) return 0; - set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); reset_data = kzalloc_obj(*reset_data); - if (!reset_data) + if (!reset_data) { + clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); return -ENOMEM; + } reset_data->accel_dev = accel_dev; init_completion(&reset_data->compl); reset_data->mode = mode; @@ -190,38 +241,31 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev, static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) { - struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); - int res = 0; - - if (!accel_dev) { - pr_err("QAT: Can't find acceleration device\n"); - return PCI_ERS_RESULT_DISCONNECT; - } + return reset_done(pdev); +} - if (!pdev->is_busmaster) - pci_set_master(pdev); - pci_restore_state(pdev); - res = adf_dev_up(accel_dev, false); - if (res && res != -EALREADY) - return PCI_ERS_RESULT_DISCONNECT; +static void adf_resume(struct pci_dev *pdev) +{ + pci_info(pdev, "Acceleration driver reset completed\n"); + pci_info(pdev, "Device is up and running\n"); +} - adf_reenable_sriov(accel_dev); - adf_pf2vf_notify_restarted(accel_dev); - adf_dev_restarted_notify(accel_dev); - clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); - return PCI_ERS_RESULT_RECOVERED; +static void adf_reset_prepare(struct pci_dev *pdev) +{ + reset_prepare(pdev); } -static void adf_resume(struct pci_dev *pdev) +static void adf_reset_done(struct pci_dev *pdev) { - dev_info(&pdev->dev, "Acceleration driver reset completed\n"); - dev_info(&pdev->dev, "Device is up and running\n"); + reset_done(pdev); } const struct pci_error_handlers adf_err_handler = { .error_detected = adf_error_detected, .slot_reset = adf_slot_reset, .resume = adf_resume, + .reset_prepare = adf_reset_prepare, + .reset_done = adf_reset_done, }; EXPORT_SYMBOL_GPL(adf_err_handler); diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg.c b/drivers/crypto/intel/qat/qat_common/adf_cfg.c index c202209f17d5..ea5d72d5090c 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg.c +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg.c @@ -103,16 +103,6 @@ static void adf_cfg_section_del_all(struct list_head *head); static void adf_cfg_section_del_all_except(struct list_head *head, const char *section_name); -void adf_cfg_del_all(struct adf_accel_dev *accel_dev) -{ - struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg; - - down_write(&dev_cfg_data->lock); - adf_cfg_section_del_all(&dev_cfg_data->sec_list); - up_write(&dev_cfg_data->lock); - clear_bit(ADF_STATUS_CONFIGURED, &accel_dev->status); -} - void adf_cfg_del_all_except(struct adf_accel_dev *accel_dev, const char *section_name) { diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg.h b/drivers/crypto/intel/qat/qat_common/adf_cfg.h index 2afa6f0d15c5..108032b39242 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg.h +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg.h @@ -34,7 +34,6 @@ void adf_cfg_dev_remove(struct adf_accel_dev *accel_dev); void adf_cfg_dev_dbgfs_add(struct adf_accel_dev *accel_dev); void adf_cfg_dev_dbgfs_rm(struct adf_accel_dev *accel_dev); int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name); -void adf_cfg_del_all(struct adf_accel_dev *accel_dev); void adf_cfg_del_all_except(struct adf_accel_dev *accel_dev, const char *section_name); int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev, diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h index 81e9e9d7eccd..d63f4dcccbb5 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h @@ -4,18 +4,11 @@ #define ADF_CFG_COMMON_H_ #include <linux/types.h> -#include <linux/ioctl.h> #define ADF_CFG_MAX_STR_LEN 64 #define ADF_CFG_MAX_KEY_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN #define ADF_CFG_MAX_VAL_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN #define ADF_CFG_MAX_SECTION_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN -#define ADF_CFG_BASE_DEC 10 -#define ADF_CFG_BASE_HEX 16 -#define ADF_CFG_ALL_DEVICES 0xFE -#define ADF_CFG_NO_DEVICE 0xFF -#define ADF_CFG_AFFINITY_WHATEVER 0xFF -#define MAX_DEVICE_NAME_SIZE 32 #define ADF_MAX_DEVICES (32 * 32) #define ADF_DEVS_ARRAY_SIZE BITS_TO_LONGS(ADF_MAX_DEVICES) @@ -51,29 +44,4 @@ enum adf_device_type { DEV_420XX, DEV_6XXX, }; - -struct adf_dev_status_info { - enum adf_device_type type; - __u32 accel_id; - __u32 instance_id; - __u8 num_ae; - __u8 num_accel; - __u8 num_logical_accel; - __u8 banks_per_accel; - __u8 state; - __u8 bus; - __u8 dev; - __u8 fun; - char name[MAX_DEVICE_NAME_SIZE]; -}; - -#define ADF_CTL_IOC_MAGIC 'a' -#define IOCTL_CONFIG_SYS_RESOURCE_PARAMETERS _IOW(ADF_CTL_IOC_MAGIC, 0, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_STOP_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 1, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_START_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 2, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_STATUS_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 3, __u32) -#define IOCTL_GET_NUM_DEVICES _IOW(ADF_CTL_IOC_MAGIC, 4, __s32) #endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c b/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c index 7d00bcb41ce7..1af6da8b263f 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c +++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_services.c @@ -94,10 +94,9 @@ static int adf_service_mask_to_string(unsigned long mask, char *buf, size_t len) for_each_set_bit(bit, &mask, SVC_COUNT) { if (offset) offset += scnprintf(buf + offset, len - offset, - ADF_SERVICES_DELIMITER); - - offset += scnprintf(buf + offset, len - offset, "%s", - adf_cfg_services[bit]); + ADF_SERVICES_DELIMITER "%s", adf_cfg_services[bit]); + else + offset += scnprintf(buf, len, "%s", adf_cfg_services[bit]); } return 0; diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h deleted file mode 100644 index 421f4fb8b4dd..000000000000 --- a/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */ -/* Copyright(c) 2014 - 2020 Intel Corporation */ -#ifndef ADF_CFG_USER_H_ -#define ADF_CFG_USER_H_ - -#include "adf_cfg_common.h" -#include "adf_cfg_strings.h" - -struct adf_user_cfg_key_val { - char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES]; - char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES]; - union { - struct adf_user_cfg_key_val *next; - __u64 padding3; - }; - enum adf_cfg_val_type type; -} __packed; - -struct adf_user_cfg_section { - char name[ADF_CFG_MAX_SECTION_LEN_IN_BYTES]; - union { - struct adf_user_cfg_key_val *params; - __u64 padding1; - }; - union { - struct adf_user_cfg_section *next; - __u64 padding3; - }; -} __packed; - -struct adf_user_cfg_ctl_data { - union { - struct adf_user_cfg_section *config_section; - __u64 padding; - }; - __u8 device_id; -} __packed; -#endif diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index 7b8b295ac459..762a0b5e774a 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -9,13 +9,6 @@ #include "icp_qat_fw_loader_handle.h" #include "icp_qat_hal.h" -#define ADF_MAJOR_VERSION 0 -#define ADF_MINOR_VERSION 6 -#define ADF_BUILD_VERSION 0 -#define ADF_DRV_VERSION __stringify(ADF_MAJOR_VERSION) "." \ - __stringify(ADF_MINOR_VERSION) "." \ - __stringify(ADF_BUILD_VERSION) - #define ADF_STATUS_RESTARTING 0 #define ADF_STATUS_STARTING 1 #define ADF_STATUS_CONFIGURED 2 @@ -68,10 +61,7 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev, void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev, struct adf_accel_dev *pf); struct list_head *adf_devmgr_get_head(void); -struct adf_accel_dev *adf_devmgr_get_dev_by_id(u32 id); struct adf_accel_dev *adf_devmgr_pci_to_accel_dev(struct pci_dev *pci_dev); -int adf_devmgr_verify_id(u32 id); -void adf_devmgr_get_num_dev(u32 *num); int adf_devmgr_in_reset(struct adf_accel_dev *accel_dev); int adf_dev_started(struct adf_accel_dev *accel_dev); int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev); @@ -87,12 +77,12 @@ extern const struct pci_error_handlers adf_err_handler; void adf_reset_sbr(struct adf_accel_dev *accel_dev); void adf_reset_flr(struct adf_accel_dev *accel_dev); void adf_dev_restore(struct adf_accel_dev *accel_dev); +void adf_set_bme(struct adf_accel_dev *accel_dev); int adf_init_aer(void); void adf_exit_aer(void); int adf_init_arb(struct adf_accel_dev *accel_dev); void adf_exit_arb(struct adf_accel_dev *accel_dev); void adf_update_ring_arb(struct adf_etr_ring_data *ring); -int adf_disable_arb_thd(struct adf_accel_dev *accel_dev, u32 ae, u32 thr); int adf_dev_get(struct adf_accel_dev *accel_dev); void adf_dev_put(struct adf_accel_dev *accel_dev); @@ -121,6 +111,7 @@ void qat_comp_alg_callback(void *resp); int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_isr_resource_free(struct adf_accel_dev *accel_dev); +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev); int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev); @@ -194,6 +185,7 @@ int adf_sriov_configure(struct pci_dev *pdev, int numvfs); void adf_disable_sriov(struct adf_accel_dev *accel_dev); void adf_reenable_sriov(struct adf_accel_dev *accel_dev); void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask); +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs); void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_vf2pf_msg(struct adf_accel_dev *accel_dev, u32 vf_nr); diff --git a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c deleted file mode 100644 index c2e6f0cb7480..000000000000 --- a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c +++ /dev/null @@ -1,466 +0,0 @@ -// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) -/* Copyright(c) 2014 - 2020 Intel Corporation */ - -#include <crypto/algapi.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/bitops.h> -#include <linux/pci.h> -#include <linux/cdev.h> -#include <linux/uaccess.h> - -#include "adf_accel_devices.h" -#include "adf_common_drv.h" -#include "adf_cfg.h" -#include "adf_cfg_common.h" -#include "adf_cfg_user.h" - -#define ADF_CFG_MAX_SECTION 512 -#define ADF_CFG_MAX_KEY_VAL 256 - -#define DEVICE_NAME "qat_adf_ctl" - -static DEFINE_MUTEX(adf_ctl_lock); -static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); - -static const struct file_operations adf_ctl_ops = { - .owner = THIS_MODULE, - .unlocked_ioctl = adf_ctl_ioctl, - .compat_ioctl = compat_ptr_ioctl, -}; - -static const struct class adf_ctl_class = { - .name = DEVICE_NAME, -}; - -struct adf_ctl_drv_info { - unsigned int major; - struct cdev drv_cdev; -}; - -static struct adf_ctl_drv_info adf_ctl_drv; - -static void adf_chr_drv_destroy(void) -{ - device_destroy(&adf_ctl_class, MKDEV(adf_ctl_drv.major, 0)); - cdev_del(&adf_ctl_drv.drv_cdev); - class_unregister(&adf_ctl_class); - unregister_chrdev_region(MKDEV(adf_ctl_drv.major, 0), 1); -} - -static int adf_chr_drv_create(void) -{ - dev_t dev_id; - struct device *drv_device; - int ret; - - if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME)) { - pr_err("QAT: unable to allocate chrdev region\n"); - return -EFAULT; - } - - ret = class_register(&adf_ctl_class); - if (ret) - goto err_chrdev_unreg; - - adf_ctl_drv.major = MAJOR(dev_id); - cdev_init(&adf_ctl_drv.drv_cdev, &adf_ctl_ops); - if (cdev_add(&adf_ctl_drv.drv_cdev, dev_id, 1)) { - pr_err("QAT: cdev add failed\n"); - goto err_class_destr; - } - - drv_device = device_create(&adf_ctl_class, NULL, - MKDEV(adf_ctl_drv.major, 0), - NULL, DEVICE_NAME); - if (IS_ERR(drv_device)) { - pr_err("QAT: failed to create device\n"); - goto err_cdev_del; - } - return 0; -err_cdev_del: - cdev_del(&adf_ctl_drv.drv_cdev); -err_class_destr: - class_unregister(&adf_ctl_class); -err_chrdev_unreg: - unregister_chrdev_region(dev_id, 1); - return -EFAULT; -} - -static struct adf_user_cfg_ctl_data *adf_ctl_alloc_resources(unsigned long arg) -{ - struct adf_user_cfg_ctl_data *cfg_data; - - cfg_data = memdup_user((void __user *)arg, sizeof(*cfg_data)); - if (IS_ERR(cfg_data)) - pr_err("QAT: failed to copy from user cfg_data.\n"); - return cfg_data; -} - -static int adf_add_key_value_data(struct adf_accel_dev *accel_dev, - const char *section, - const struct adf_user_cfg_key_val *key_val) -{ - if (key_val->type == ADF_HEX) { - long *ptr = (long *)key_val->val; - long val = *ptr; - - if (adf_cfg_add_key_value_param(accel_dev, section, - key_val->key, (void *)val, - key_val->type)) { - dev_err(&GET_DEV(accel_dev), - "failed to add hex keyvalue.\n"); - return -EFAULT; - } - } else { - if (adf_cfg_add_key_value_param(accel_dev, section, - key_val->key, key_val->val, - key_val->type)) { - dev_err(&GET_DEV(accel_dev), - "failed to add keyvalue.\n"); - return -EFAULT; - } - } - return 0; -} - -static int adf_copy_key_value_data(struct adf_accel_dev *accel_dev, - struct adf_user_cfg_ctl_data *ctl_data) -{ - struct adf_user_cfg_key_val key_val; - struct adf_user_cfg_key_val *params_head; - struct adf_user_cfg_section section, *section_head; - int i, j; - - section_head = ctl_data->config_section; - - for (i = 0; section_head && i < ADF_CFG_MAX_SECTION; i++) { - if (copy_from_user(§ion, (void __user *)section_head, - sizeof(*section_head))) { - dev_err(&GET_DEV(accel_dev), - "failed to copy section info\n"); - goto out_err; - } - - if (adf_cfg_section_add(accel_dev, section.name)) { - dev_err(&GET_DEV(accel_dev), - "failed to add section.\n"); - goto out_err; - } - - params_head = section.params; - - for (j = 0; params_head && j < ADF_CFG_MAX_KEY_VAL; j++) { - if (copy_from_user(&key_val, (void __user *)params_head, - sizeof(key_val))) { - dev_err(&GET_DEV(accel_dev), - "Failed to copy keyvalue.\n"); - goto out_err; - } - if (adf_add_key_value_data(accel_dev, section.name, - &key_val)) { - goto out_err; - } - params_head = key_val.next; - } - section_head = section.next; - } - return 0; -out_err: - adf_cfg_del_all(accel_dev); - return -EFAULT; -} - -static int adf_ctl_ioctl_dev_config(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - struct adf_user_cfg_ctl_data *ctl_data; - struct adf_accel_dev *accel_dev; - int ret = 0; - - ctl_data = adf_ctl_alloc_resources(arg); - if (IS_ERR(ctl_data)) - return PTR_ERR(ctl_data); - - accel_dev = adf_devmgr_get_dev_by_id(ctl_data->device_id); - if (!accel_dev) { - ret = -EFAULT; - goto out; - } - - if (adf_dev_started(accel_dev)) { - ret = -EFAULT; - goto out; - } - - if (adf_copy_key_value_data(accel_dev, ctl_data)) { - ret = -EFAULT; - goto out; - } - set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status); -out: - kfree(ctl_data); - return ret; -} - -static int adf_ctl_is_device_in_use(int id) -{ - struct adf_accel_dev *dev; - - list_for_each_entry(dev, adf_devmgr_get_head(), list) { - if (id == dev->accel_id || id == ADF_CFG_ALL_DEVICES) { - if (adf_devmgr_in_reset(dev) || adf_dev_in_use(dev)) { - dev_info(&GET_DEV(dev), - "device qat_dev%d is busy\n", - dev->accel_id); - return -EBUSY; - } - } - } - return 0; -} - -static void adf_ctl_stop_devices(u32 id) -{ - struct adf_accel_dev *accel_dev; - - list_for_each_entry(accel_dev, adf_devmgr_get_head(), list) { - if (id == accel_dev->accel_id || id == ADF_CFG_ALL_DEVICES) { - if (!adf_dev_started(accel_dev)) - continue; - - /* First stop all VFs */ - if (!accel_dev->is_vf) - continue; - - adf_dev_down(accel_dev); - } - } - - list_for_each_entry(accel_dev, adf_devmgr_get_head(), list) { - if (id == accel_dev->accel_id || id == ADF_CFG_ALL_DEVICES) { - if (!adf_dev_started(accel_dev)) - continue; - - adf_dev_down(accel_dev); - } - } -} - -static int adf_ctl_ioctl_dev_stop(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - int ret; - struct adf_user_cfg_ctl_data *ctl_data; - - ctl_data = adf_ctl_alloc_resources(arg); - if (IS_ERR(ctl_data)) - return PTR_ERR(ctl_data); - - if (adf_devmgr_verify_id(ctl_data->device_id)) { - pr_err("QAT: Device %d not found\n", ctl_data->device_id); - ret = -ENODEV; - goto out; - } - - ret = adf_ctl_is_device_in_use(ctl_data->device_id); - if (ret) - goto out; - - if (ctl_data->device_id == ADF_CFG_ALL_DEVICES) - pr_info("QAT: Stopping all acceleration devices.\n"); - else - pr_info("QAT: Stopping acceleration device qat_dev%d.\n", - ctl_data->device_id); - - adf_ctl_stop_devices(ctl_data->device_id); - -out: - kfree(ctl_data); - return ret; -} - -static int adf_ctl_ioctl_dev_start(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - int ret; - struct adf_user_cfg_ctl_data *ctl_data; - struct adf_accel_dev *accel_dev; - - ctl_data = adf_ctl_alloc_resources(arg); - if (IS_ERR(ctl_data)) - return PTR_ERR(ctl_data); - - ret = -ENODEV; - accel_dev = adf_devmgr_get_dev_by_id(ctl_data->device_id); - if (!accel_dev) - goto out; - - dev_info(&GET_DEV(accel_dev), - "Starting acceleration device qat_dev%d.\n", - ctl_data->device_id); - - ret = adf_dev_up(accel_dev, false); - - if (ret) { - dev_err(&GET_DEV(accel_dev), "Failed to start qat_dev%d\n", - ctl_data->device_id); - adf_dev_down(accel_dev); - } -out: - kfree(ctl_data); - return ret; -} - -static int adf_ctl_ioctl_get_num_devices(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - u32 num_devices = 0; - - adf_devmgr_get_num_dev(&num_devices); - if (copy_to_user((void __user *)arg, &num_devices, sizeof(num_devices))) - return -EFAULT; - - return 0; -} - -static int adf_ctl_ioctl_get_status(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - struct adf_hw_device_data *hw_data; - struct adf_dev_status_info dev_info; - struct adf_accel_dev *accel_dev; - - if (copy_from_user(&dev_info, (void __user *)arg, - sizeof(struct adf_dev_status_info))) { - pr_err("QAT: failed to copy from user.\n"); - return -EFAULT; - } - - accel_dev = adf_devmgr_get_dev_by_id(dev_info.accel_id); - if (!accel_dev) - return -ENODEV; - - hw_data = accel_dev->hw_device; - dev_info.state = adf_dev_started(accel_dev) ? DEV_UP : DEV_DOWN; - dev_info.num_ae = hw_data->get_num_aes(hw_data); - dev_info.num_accel = hw_data->get_num_accels(hw_data); - dev_info.num_logical_accel = hw_data->num_logical_accel; - dev_info.banks_per_accel = hw_data->num_banks - / hw_data->num_logical_accel; - strscpy(dev_info.name, hw_data->dev_class->name, sizeof(dev_info.name)); - dev_info.instance_id = hw_data->instance_id; - dev_info.type = hw_data->dev_class->type; - dev_info.bus = accel_to_pci_dev(accel_dev)->bus->number; - dev_info.dev = PCI_SLOT(accel_to_pci_dev(accel_dev)->devfn); - dev_info.fun = PCI_FUNC(accel_to_pci_dev(accel_dev)->devfn); - - if (copy_to_user((void __user *)arg, &dev_info, - sizeof(struct adf_dev_status_info))) { - dev_err(&GET_DEV(accel_dev), "failed to copy status.\n"); - return -EFAULT; - } - return 0; -} - -static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -{ - int ret; - - if (mutex_lock_interruptible(&adf_ctl_lock)) - return -EFAULT; - - switch (cmd) { - case IOCTL_CONFIG_SYS_RESOURCE_PARAMETERS: - ret = adf_ctl_ioctl_dev_config(fp, cmd, arg); - break; - - case IOCTL_STOP_ACCEL_DEV: - ret = adf_ctl_ioctl_dev_stop(fp, cmd, arg); - break; - - case IOCTL_START_ACCEL_DEV: - ret = adf_ctl_ioctl_dev_start(fp, cmd, arg); - break; - - case IOCTL_GET_NUM_DEVICES: - ret = adf_ctl_ioctl_get_num_devices(fp, cmd, arg); - break; - - case IOCTL_STATUS_ACCEL_DEV: - ret = adf_ctl_ioctl_get_status(fp, cmd, arg); - break; - default: - pr_err_ratelimited("QAT: Invalid ioctl %d\n", cmd); - ret = -EFAULT; - break; - } - mutex_unlock(&adf_ctl_lock); - return ret; -} - -static int __init adf_register_ctl_device_driver(void) -{ - if (adf_chr_drv_create()) - goto err_chr_dev; - - if (adf_init_misc_wq()) - goto err_misc_wq; - - if (adf_init_aer()) - goto err_aer; - - if (adf_init_pf_wq()) - goto err_pf_wq; - - if (adf_init_vf_wq()) - goto err_vf_wq; - - if (qat_crypto_register()) - goto err_crypto_register; - - if (qat_compression_register()) - goto err_compression_register; - - return 0; - -err_compression_register: - qat_crypto_unregister(); -err_crypto_register: - adf_exit_vf_wq(); -err_vf_wq: - adf_exit_pf_wq(); -err_pf_wq: - adf_exit_aer(); -err_aer: - adf_exit_misc_wq(); -err_misc_wq: - adf_chr_drv_destroy(); -err_chr_dev: - mutex_destroy(&adf_ctl_lock); - return -EFAULT; -} - -static void __exit adf_unregister_ctl_device_driver(void) -{ - adf_chr_drv_destroy(); - adf_exit_misc_wq(); - adf_exit_aer(); - adf_exit_vf_wq(); - adf_exit_pf_wq(); - qat_crypto_unregister(); - qat_compression_unregister(); - adf_clean_vf_map(false); - mutex_destroy(&adf_ctl_lock); -} - -module_init(adf_register_ctl_device_driver); -module_exit(adf_unregister_ctl_device_driver); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_AUTHOR("Intel"); -MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_ALIAS_CRYPTO("intel_qat"); -MODULE_VERSION(ADF_DRV_VERSION); -MODULE_IMPORT_NS("CRYPTO_INTERNAL"); diff --git a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c index e050de16ab5d..161863841a52 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c +++ b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c @@ -45,19 +45,6 @@ static struct vf_id_map *adf_find_vf(u32 bdf) return NULL; } -static int adf_get_vf_real_id(u32 fake) -{ - struct list_head *itr; - - list_for_each(itr, &vfs_table) { - struct vf_id_map *ptr = - list_entry(itr, struct vf_id_map, list); - if (ptr->fake_id == fake) - return ptr->id; - } - return -1; -} - /** * adf_clean_vf_map() - Cleans VF id mappings * @vf: flag indicating whether mappings is cleaned @@ -304,63 +291,6 @@ struct adf_accel_dev *adf_devmgr_pci_to_accel_dev(struct pci_dev *pci_dev) } EXPORT_SYMBOL_GPL(adf_devmgr_pci_to_accel_dev); -struct adf_accel_dev *adf_devmgr_get_dev_by_id(u32 id) -{ - struct list_head *itr; - int real_id; - - mutex_lock(&table_lock); - real_id = adf_get_vf_real_id(id); - if (real_id < 0) - goto unlock; - - id = real_id; - - list_for_each(itr, &accel_table) { - struct adf_accel_dev *ptr = - list_entry(itr, struct adf_accel_dev, list); - if (ptr->accel_id == id) { - mutex_unlock(&table_lock); - return ptr; - } - } -unlock: - mutex_unlock(&table_lock); - return NULL; -} - -int adf_devmgr_verify_id(u32 id) -{ - if (id == ADF_CFG_ALL_DEVICES) - return 0; - - if (adf_devmgr_get_dev_by_id(id)) - return 0; - - return -ENODEV; -} - -static int adf_get_num_dettached_vfs(void) -{ - struct list_head *itr; - int vfs = 0; - - mutex_lock(&table_lock); - list_for_each(itr, &vfs_table) { - struct vf_id_map *ptr = - list_entry(itr, struct vf_id_map, list); - if (ptr->bdf != ~0 && !ptr->attached) - vfs++; - } - mutex_unlock(&table_lock); - return vfs; -} - -void adf_devmgr_get_num_dev(u32 *num) -{ - *num = num_devices - adf_get_num_dettached_vfs(); -} - /** * adf_dev_in_use() - Check whether accel_dev is currently in use * @accel_dev: Pointer to acceleration device. diff --git a/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c b/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c index a3b474bdef6c..023c5f1e78b0 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c +++ b/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c @@ -64,10 +64,8 @@ int adf_heartbeat_inject_error(struct adf_accel_dev *accel_dev) if (ret) return ret; - /* Configure worker threads to stop processing any packet */ - ret = adf_disable_arb_thd(accel_dev, rand_ae, rand_thr); - if (ret) - return ret; + /* Disable arbiter to stop processing any packet */ + hw_device->exit_arb(accel_dev); /* Change HB counters memory to simulate a hang */ adf_set_hb_counters_fail(accel_dev, rand_ae, rand_thr); diff --git a/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c b/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c index f93d9cca70ce..dd9a31c20bc9 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c +++ b/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c @@ -99,28 +99,3 @@ void adf_exit_arb(struct adf_accel_dev *accel_dev) csr_ops->write_csr_ring_srv_arb_en(csr, i, 0); } EXPORT_SYMBOL_GPL(adf_exit_arb); - -int adf_disable_arb_thd(struct adf_accel_dev *accel_dev, u32 ae, u32 thr) -{ - void __iomem *csr = accel_dev->transport->banks[0].csr_addr; - struct adf_hw_device_data *hw_data = accel_dev->hw_device; - const u32 *thd_2_arb_cfg; - struct arb_info info; - u32 ae_thr_map; - - if (ADF_AE_STRAND0_THREAD == thr || ADF_AE_STRAND1_THREAD == thr) - thr = ADF_AE_ADMIN_THREAD; - - hw_data->get_arb_info(&info); - thd_2_arb_cfg = hw_data->get_arb_mapping(accel_dev); - if (!thd_2_arb_cfg) - return -EFAULT; - - /* Disable scheduling for this particular AE and thread */ - ae_thr_map = *(thd_2_arb_cfg + ae); - ae_thr_map &= ~(GENMASK(3, 0) << (thr * BIT(2))); - - WRITE_CSR_ARB_WT2SAM(csr, info.arb_offset, info.wt2sam_offset, ae, - ae_thr_map); - return 0; -} diff --git a/drivers/crypto/intel/qat/qat_common/adf_init.c b/drivers/crypto/intel/qat/qat_common/adf_init.c index f8088388cf12..3e39c53814de 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_init.c +++ b/drivers/crypto/intel/qat/qat_common/adf_init.c @@ -10,6 +10,7 @@ #include "adf_dbgfs.h" #include "adf_heartbeat.h" #include "adf_rl.h" +#include "adf_kpt.h" #include "adf_sysfs_anti_rb.h" #include "adf_sysfs_ras_counters.h" #include "adf_telemetry.h" @@ -74,6 +75,8 @@ static int adf_dev_init(struct adf_accel_dev *accel_dev) return -EFAULT; } + adf_set_bme(accel_dev); + if (!test_bit(ADF_STATUS_CONFIGURED, &accel_dev->status) && !accel_dev->is_vf) { dev_err(&GET_DEV(accel_dev), "Device not configured\n"); @@ -153,15 +156,18 @@ static int adf_dev_init(struct adf_accel_dev *accel_dev) * This is to facilitate any ordering dependencies between services * prior to starting any of the accelerators. */ + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_INIT)) { dev_err(&GET_DEV(accel_dev), "Failed to initialise service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); return 0; } @@ -214,6 +220,13 @@ static int adf_dev_start(struct adf_accel_dev *accel_dev) return -EFAULT; } + /* Enable Key Protection Technology (KPT) */ + ret = adf_enable_kpt(accel_dev); + if (ret) { + dev_err(&GET_DEV(accel_dev), "Failed to enable KPT\n"); + return ret; + } + if (hw_data->start_timer) { ret = hw_data->start_timer(accel_dev); if (ret) { @@ -231,15 +244,18 @@ static int adf_dev_start(struct adf_accel_dev *accel_dev) if (ret && ret != -EOPNOTSUPP) return ret; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_START)) { dev_err(&GET_DEV(accel_dev), "Failed to start service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->start_status); } + mutex_unlock(&service_lock); clear_bit(ADF_STATUS_STARTING, &accel_dev->status); set_bit(ADF_STATUS_STARTED, &accel_dev->status); @@ -313,6 +329,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) qat_comp_algs_unregister(hw_data->accel_capabilities_ext_mask); clear_bit(ADF_STATUS_COMP_ALGS_REGISTERED, &accel_dev->status); + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (!test_bit(accel_dev->accel_id, service->start_status)) continue; @@ -324,6 +341,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) clear_bit(accel_dev->accel_id, service->start_status); } } + mutex_unlock(&service_lock); if (hw_data->stop_timer) hw_data->stop_timer(accel_dev); @@ -373,6 +391,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) &accel_dev->status); } + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (!test_bit(accel_dev->accel_id, service->init_status)) continue; @@ -383,6 +402,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) else clear_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); adf_rl_exit(accel_dev); @@ -417,12 +437,14 @@ int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_RESTARTING)) dev_err(&GET_DEV(accel_dev), "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } @@ -430,12 +452,14 @@ int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_RESTARTED)) dev_err(&GET_DEV(accel_dev), "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } @@ -443,12 +467,14 @@ void adf_error_notifier(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_FATAL_ERROR)) dev_err(&GET_DEV(accel_dev), "Failed to send error event to %s.\n", service->name); } + mutex_unlock(&service_lock); } int adf_dev_down(struct adf_accel_dev *accel_dev) diff --git a/drivers/crypto/intel/qat/qat_common/adf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_isr.c index 4639d7fd93e6..159e91a50106 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_isr.c +++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c @@ -62,6 +62,23 @@ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + if (!READ_ONCE(accel_dev->pf.vf2pf_disabled)) + GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); + spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); +} + +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs) +{ + void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev); + unsigned long flags; + u32 vf_mask; + + vf_mask = BIT_ULL(num_vfs) - 1; + if (!vf_mask) + return; + + spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, false); GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -72,6 +89,7 @@ void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true); GET_PFVF_OPS(accel_dev)->disable_all_vf2pf_interrupts(pmisc_addr); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -174,6 +192,27 @@ static irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr) return IRQ_NONE; } +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev) +{ + struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + u32 num_entries = pci_dev_info->msix_entries.num_entries; + struct adf_irq *irqs = pci_dev_info->msix_entries.irqs; + u32 irq_idx; + int irq; + + if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !irqs) + return; + + irq_idx = num_entries > 1 ? hw_data->num_banks : 0; + if (irq_idx >= num_entries || !irqs[irq_idx].enabled) + return; + + irq = pci_irq_vector(pci_dev_info->pci_dev, hw_data->num_banks); + if (irq > 0) + synchronize_irq(irq); +} + static void adf_free_irqs(struct adf_accel_dev *accel_dev) { struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; diff --git a/drivers/crypto/intel/qat/qat_common/adf_kpt.c b/drivers/crypto/intel/qat/qat_common/adf_kpt.c new file mode 100644 index 000000000000..a0430141ea0e --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_kpt.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2026 Intel Corporation */ +#include <linux/dma-mapping.h> + +#include "adf_admin.h" +#include "adf_cfg_services.h" +#include "adf_common_drv.h" +#include "adf_kpt.h" + +static bool adf_kpt_supported(struct adf_accel_dev *accel_dev) +{ + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + + return hw_data->accel_capabilities_mask & ICP_ACCEL_CAPABILITIES_KPT; +} + +int adf_enable_kpt(struct adf_accel_dev *accel_dev) +{ + struct adf_kpt_interface_data *user_data = GET_KPT_USER_DATA(accel_dev); + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + dma_addr_t paddr; + void *vaddr; + int ret; + int svc; + + /* Return 0 if KPT is not supported by the hardware */ + if (!adf_kpt_supported(accel_dev)) + return 0; + + if (!user_data->enable) { + /* Disable KPT capability if user has not enabled it */ + hw_data->accel_capabilities_mask &= ~ICP_ACCEL_CAPABILITIES_KPT; + return 0; + } + + svc = adf_get_service_enabled(accel_dev); + if (svc < 0) + return svc; + + if (svc != SVC_ASYM) { + dev_err(&GET_DEV(accel_dev), + "KPT can only be enabled when service is configured as 'asym'\n"); + return -EINVAL; + } + + vaddr = dma_alloc_coherent(&GET_DEV(accel_dev), PAGE_SIZE, &paddr, + GFP_KERNEL); + if (!vaddr) + return -ENOMEM; + + ret = adf_send_admin_kpt_init(accel_dev, vaddr, PAGE_SIZE, paddr); + + dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE, vaddr, paddr); + + return ret; +} diff --git a/drivers/crypto/intel/qat/qat_common/adf_kpt.h b/drivers/crypto/intel/qat/qat_common/adf_kpt.h new file mode 100644 index 000000000000..17d07d24a319 --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_kpt.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2026 Intel Corporation */ +#ifndef ADF_KPT_H_ +#define ADF_KPT_H_ + +#include <linux/types.h> + +#define GET_KPT_CFG_DATA(accel_dev) (&(accel_dev)->hw_device->kpt_data) +#define GET_KPT_USER_DATA(accel_dev) (&(accel_dev)->hw_device->kpt_data.user_input) + +struct adf_accel_dev; + +struct adf_kpt_interface_data { + bool enable; + bool swk_shared; + unsigned int swk_cnt_per_fn; + unsigned int swk_cnt_per_pasid; + unsigned int swk_max_ttl; +}; + +struct adf_kpt_hw_data { + unsigned int max_swk_cnt_per_fn_pasid; + unsigned int max_swk_ttl; + struct adf_kpt_interface_data user_input; +}; + +int adf_enable_kpt(struct adf_accel_dev *accel_dev); + +#endif /* ADF_KPT_H_ */ diff --git a/drivers/crypto/intel/qat/qat_common/adf_module.c b/drivers/crypto/intel/qat/qat_common/adf_module.c new file mode 100644 index 000000000000..30069feec3c1 --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_module.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) +/* Copyright(c) 2014 - 2020 Intel Corporation */ + +#include <crypto/algapi.h> +#include <linux/errno.h> +#include <linux/module.h> + +#include "adf_common_drv.h" + +static int __init adf_register_module(void) +{ + if (adf_init_misc_wq()) + goto err_misc_wq; + + if (adf_init_aer()) + goto err_aer; + + if (adf_init_pf_wq()) + goto err_pf_wq; + + if (adf_init_vf_wq()) + goto err_vf_wq; + + if (qat_crypto_register()) + goto err_crypto_register; + + if (qat_compression_register()) + goto err_compression_register; + + return 0; + +err_compression_register: + qat_crypto_unregister(); +err_crypto_register: + adf_exit_vf_wq(); +err_vf_wq: + adf_exit_pf_wq(); +err_pf_wq: + adf_exit_aer(); +err_aer: + adf_exit_misc_wq(); +err_misc_wq: + return -EFAULT; +} + +static void __exit adf_unregister_module(void) +{ + adf_exit_misc_wq(); + adf_exit_aer(); + adf_exit_vf_wq(); + adf_exit_pf_wq(); + qat_crypto_unregister(); + qat_compression_unregister(); + adf_clean_vf_map(false); +} + +module_init(adf_register_module); +module_exit(adf_unregister_module); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel"); +MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); +MODULE_ALIAS_CRYPTO("intel_qat"); +MODULE_IMPORT_NS("CRYPTO_INTERNAL"); diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c index 8bf0fe1fcb4d..f45ca2eecc00 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c @@ -26,6 +26,9 @@ static void adf_iov_send_resp(struct work_struct *work) u32 vf_nr = vf_info->vf_nr; bool ret; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + goto out; + mutex_lock(&vf_info->pfvf_mig_lock); ret = adf_recv_and_handle_vf2pf_msg(accel_dev, vf_nr); if (ret) @@ -33,13 +36,18 @@ static void adf_iov_send_resp(struct work_struct *work) adf_enable_vf2pf_interrupts(accel_dev, 1 << vf_nr); mutex_unlock(&vf_info->pfvf_mig_lock); +out: kfree(pf2vf_resp); } void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) { + struct adf_accel_dev *accel_dev = vf_info->accel_dev; struct adf_pf2vf_resp *pf2vf_resp; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + return; + pf2vf_resp = kzalloc_obj(*pf2vf_resp, GFP_ATOMIC); if (!pf2vf_resp) return; @@ -49,6 +57,12 @@ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) queue_work(pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work); } +static void adf_flush_pf2vf_resp_wq(void) +{ + if (pf2vf_resp_wq) + flush_workqueue(pf2vf_resp_wq); +} + static int adf_enable_sriov(struct adf_accel_dev *accel_dev) { struct pci_dev *pdev = accel_to_pci_dev(accel_dev); @@ -75,7 +89,11 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) hw_data->configure_iov_threads(accel_dev, true); /* Enable VF to PF interrupts for all VFs */ - adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1); + adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs); + + /* Do not enable SR-IOV if already enabled */ + if (pci_num_vf(pdev)) + return 0; /* * Due to the hardware design, when SR-IOV and the ring arbiter @@ -222,7 +240,7 @@ void adf_reenable_sriov(struct adf_accel_dev *accel_dev) if (adf_add_sriov_configuration(accel_dev)) return; - dev_dbg(&pdev->dev, "Re-enabling SRIOV\n"); + pci_dbg(pdev, "Re-enabling SRIOV\n"); adf_enable_sriov(accel_dev); } @@ -246,10 +264,18 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev) adf_pf2vf_notify_restarting(accel_dev); adf_pf2vf_wait_for_restarting_complete(accel_dev); - pci_disable_sriov(accel_to_pci_dev(accel_dev)); + /* + * When the device is restarting, preserve VF PCI devices across + * the reset by skipping pci_disable_sriov(). VFs are notified to + * quiesce regardless so the PF can safely shut down. + */ + if (!test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) + pci_disable_sriov(accel_to_pci_dev(accel_dev)); - /* Disable VF to PF interrupts */ + /* Block VF2PF work and disable VF to PF interrupts */ adf_disable_all_vf2pf_interrupts(accel_dev); + adf_isr_sync_ae_cluster(accel_dev); + adf_flush_pf2vf_resp_wq(); /* Clear Valid bits in AE Thread to PCIe Function Mapping */ if (hw_data->configure_iov_threads) diff --git a/drivers/crypto/intel/qat/qat_common/adf_sysfs_kpt.c b/drivers/crypto/intel/qat/qat_common/adf_sysfs_kpt.c new file mode 100644 index 000000000000..1f733ae80bdf --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_sysfs_kpt.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2026 Intel Corporation */ +#include <linux/sysfs.h> +#include <linux/types.h> + +#include "adf_cfg.h" +#include "adf_cfg_services.h" +#include "adf_common_drv.h" +#include "adf_kpt.h" +#include "adf_sysfs_kpt.h" + +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adf_kpt_interface_data *user_data; + struct adf_accel_dev *accel_dev; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + user_data = GET_KPT_USER_DATA(accel_dev); + + return sysfs_emit(buf, "%d\n", user_data->enable); +} + +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adf_kpt_interface_data *user_data; + struct adf_hw_device_data *hw_data; + struct adf_accel_dev *accel_dev; + bool enable; + int ret; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + if (adf_dev_started(accel_dev)) { + dev_info(dev, "Device qat_dev%d must be down before enabling KPT\n", + accel_dev->accel_id); + return -EINVAL; + } + + if (adf_get_service_enabled(accel_dev) != SVC_ASYM) { + dev_info(dev, "KPT can only be enabled when the asymmetric service is enabled\n"); + return -EINVAL; + } + + hw_data = GET_HW_DATA(accel_dev); + + /* + * Restore the KPT capability bit in the device's capabilities mask + * before processing user input, as the bit may have been cleared if + * KPT was previously disabled by the user. + */ + hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev); + if (!hw_data->accel_capabilities_mask) + return -EINVAL; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + user_data = GET_KPT_USER_DATA(accel_dev); + user_data->enable = enable; + + return count; +} +static DEVICE_ATTR_RW(enable); + +static ssize_t swk_shared_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adf_kpt_interface_data *user_data; + struct adf_accel_dev *accel_dev; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + user_data = GET_KPT_USER_DATA(accel_dev); + + return sysfs_emit(buf, "%d\n", user_data->swk_shared); +} + +static ssize_t swk_shared_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adf_kpt_interface_data *user_data; + struct adf_accel_dev *accel_dev; + bool swk_shared; + int ret; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + if (adf_dev_started(accel_dev)) { + dev_info(dev, "Device qat_dev%d must be down before setting swk_shared\n", + accel_dev->accel_id); + return -EINVAL; + } + + ret = kstrtobool(buf, &swk_shared); + if (ret) + return ret; + + user_data = GET_KPT_USER_DATA(accel_dev); + user_data->swk_shared = swk_shared; + + return count; +} +static DEVICE_ATTR_RW(swk_shared); + +static ssize_t swk_max_ttl_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adf_kpt_interface_data *user_data; + struct adf_accel_dev *accel_dev; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + user_data = GET_KPT_USER_DATA(accel_dev); + + return sysfs_emit(buf, "%u\n", user_data->swk_max_ttl); +} + +static ssize_t swk_max_ttl_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adf_kpt_hw_data *kpt_data; + struct adf_accel_dev *accel_dev; + unsigned int swk_max_ttl; + int ret; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + if (adf_dev_started(accel_dev)) { + dev_info(dev, "Device qat_dev%d must be down before setting swk_max_ttl\n", + accel_dev->accel_id); + return -EINVAL; + } + + ret = kstrtouint(buf, 10, &swk_max_ttl); + if (ret) + return ret; + + kpt_data = GET_KPT_CFG_DATA(accel_dev); + + if (swk_max_ttl > kpt_data->max_swk_ttl) { + dev_info(dev, "Configuration value is out of range (%u - %u)\n", + 0, kpt_data->max_swk_ttl); + return -EINVAL; + } + + kpt_data->user_input.swk_max_ttl = swk_max_ttl; + + return count; +} +static DEVICE_ATTR_RW(swk_max_ttl); + +static ssize_t swk_cnt_per_fn_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adf_kpt_interface_data *user_data; + struct adf_accel_dev *accel_dev; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + user_data = GET_KPT_USER_DATA(accel_dev); + + return sysfs_emit(buf, "%u\n", user_data->swk_cnt_per_fn); +} + +static ssize_t swk_cnt_per_fn_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adf_kpt_hw_data *kpt_data; + struct adf_accel_dev *accel_dev; + unsigned int swk_cnt_per_fn; + int ret; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + if (adf_dev_started(accel_dev)) { + dev_info(dev, "Device qat_dev%d must be down before setting swk_cnt_per_fn\n", + accel_dev->accel_id); + return -EINVAL; + } + + ret = kstrtouint(buf, 10, &swk_cnt_per_fn); + if (ret) + return ret; + + kpt_data = GET_KPT_CFG_DATA(accel_dev); + + if (swk_cnt_per_fn > kpt_data->max_swk_cnt_per_fn_pasid) { + dev_info(dev, "swk_cnt_per_fn: value out of range (0 - %u)\n", + kpt_data->max_swk_cnt_per_fn_pasid); + return -EINVAL; + } + + kpt_data->user_input.swk_cnt_per_fn = swk_cnt_per_fn; + + return count; +} +static DEVICE_ATTR_RW(swk_cnt_per_fn); + +static ssize_t swk_cnt_per_pasid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adf_kpt_interface_data *user_data; + struct adf_accel_dev *accel_dev; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + user_data = GET_KPT_USER_DATA(accel_dev); + + return sysfs_emit(buf, "%u\n", user_data->swk_cnt_per_pasid); +} + +static ssize_t swk_cnt_per_pasid_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adf_kpt_hw_data *kpt_data; + struct adf_accel_dev *accel_dev; + unsigned int swk_cnt_per_pasid; + int ret; + + accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); + if (!accel_dev) + return -EINVAL; + + if (adf_dev_started(accel_dev)) { + dev_info(dev, "Device qat_dev%d must be down before setting swk_cnt_per_pasid\n", + accel_dev->accel_id); + return -EINVAL; + } + + ret = kstrtouint(buf, 10, &swk_cnt_per_pasid); + if (ret) + return ret; + + kpt_data = GET_KPT_CFG_DATA(accel_dev); + + if (swk_cnt_per_pasid > kpt_data->max_swk_cnt_per_fn_pasid) { + dev_info(dev, "swk_cnt_per_pasid: value out of range (0 - %u)\n", + kpt_data->max_swk_cnt_per_fn_pasid); + return -EINVAL; + } + + kpt_data->user_input.swk_cnt_per_pasid = swk_cnt_per_pasid; + + return count; +} +static DEVICE_ATTR_RW(swk_cnt_per_pasid); + +static struct attribute *qat_kpt_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_swk_shared.attr, + &dev_attr_swk_max_ttl.attr, + &dev_attr_swk_cnt_per_fn.attr, + &dev_attr_swk_cnt_per_pasid.attr, + NULL, +}; + +static const struct attribute_group qat_kpt_group = { + .attrs = qat_kpt_attrs, + .name = "qat_kpt", +}; + +int adf_sysfs_init_kpt(struct adf_accel_dev *accel_dev) +{ + int ret; + + ret = devm_device_add_group(&GET_DEV(accel_dev), &qat_kpt_group); + if (ret) { + dev_err(&GET_DEV(accel_dev), "Failed to create qat_kpt attribute group\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(adf_sysfs_init_kpt); diff --git a/drivers/crypto/intel/qat/qat_common/adf_sysfs_kpt.h b/drivers/crypto/intel/qat/qat_common/adf_sysfs_kpt.h new file mode 100644 index 000000000000..d9d065f75114 --- /dev/null +++ b/drivers/crypto/intel/qat/qat_common/adf_sysfs_kpt.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2026 Intel Corporation */ +#ifndef ADF_SYSFS_KPT_H_ +#define ADF_SYSFS_KPT_H_ + +struct adf_accel_dev; + +int adf_sysfs_init_kpt(struct adf_accel_dev *accel_dev); + +#endif /* ADF_SYSFS_KPT_H_ */ diff --git a/drivers/crypto/intel/qat/qat_common/icp_qat_fw_init_admin.h b/drivers/crypto/intel/qat/qat_common/icp_qat_fw_init_admin.h index 6b0f0d100cb9..95f7f514cb63 100644 --- a/drivers/crypto/intel/qat/qat_common/icp_qat_fw_init_admin.h +++ b/drivers/crypto/intel/qat/qat_common/icp_qat_fw_init_admin.h @@ -31,6 +31,7 @@ enum icp_qat_fw_init_admin_cmd_id { ICP_QAT_FW_RL_REMOVE = 136, ICP_QAT_FW_TL_START = 137, ICP_QAT_FW_TL_STOP = 138, + ICP_QAT_FW_KPT_ENABLE = 144, ICP_QAT_FW_SVN_READ = 146, ICP_QAT_FW_SVN_COMMIT = 147, }; @@ -212,4 +213,11 @@ struct icp_qat_fw_init_admin_pm_info { __u32 resvrd3[6]; }; +struct icp_qat_fw_init_admin_kpt_cfg { + __u32 swk_cnt_per_fn; + __u32 swk_cnt_per_pasid; + __u32 swk_ttl_in_secs; + __u32 swk_shared_disable; +}; + #endif diff --git a/drivers/crypto/intel/qat/qat_common/icp_qat_hw.h b/drivers/crypto/intel/qat/qat_common/icp_qat_hw.h index 16ef6d98fa42..e38115d3cd75 100644 --- a/drivers/crypto/intel/qat/qat_common/icp_qat_hw.h +++ b/drivers/crypto/intel/qat/qat_common/icp_qat_hw.h @@ -114,7 +114,8 @@ enum icp_qat_capabilities_mask { ICP_ACCEL_CAPABILITIES_LZ4_COMPRESSION = BIT(24), ICP_ACCEL_CAPABILITIES_LZ4S_COMPRESSION = BIT(25), ICP_ACCEL_CAPABILITIES_AES_V2 = BIT(26), - /* Bits 27-28 are currently reserved */ + ICP_ACCEL_CAPABILITIES_KPT = BIT(27), + /* Bit 28 is currently reserved */ ICP_ACCEL_CAPABILITIES_ZUC_256 = BIT(29), ICP_ACCEL_CAPABILITIES_WIRELESS_CRYPTO_EXT = BIT(30), }; diff --git a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c index e09b9edfce42..75c15c8e41db 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c @@ -1085,7 +1085,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->p; len = rsa_key->p_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto err; ctx->p = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_p, GFP_KERNEL); if (!ctx->p) @@ -1096,7 +1096,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->q; len = rsa_key->q_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_p; ctx->q = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_q, GFP_KERNEL); if (!ctx->q) @@ -1107,7 +1107,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dp; len = rsa_key->dp_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_q; ctx->dp = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dp, GFP_KERNEL); @@ -1119,7 +1119,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dq; len = rsa_key->dq_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dp; ctx->dq = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dq, GFP_KERNEL); @@ -1131,7 +1131,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->qinv; len = rsa_key->qinv_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dq; ctx->qinv = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_qinv, GFP_KERNEL); diff --git a/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c index b59e0cc49e52..571f302edea3 100644 --- a/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c @@ -162,15 +162,14 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar->size = pci_resource_len(pdev, bar_nr); bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr); + pci_err(pdev, "Failed to map BAR %d\n", bar_nr); ret = -EFAULT; goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { - dev_err(&pdev->dev, "Failed to save pci state\n"); + pci_err(pdev, "Failed to save pci state\n"); ret = -ENOMEM; goto out_err_free_reg; } @@ -256,5 +255,4 @@ MODULE_AUTHOR("Intel"); MODULE_FIRMWARE(ADF_DH895XCC_FW); MODULE_FIRMWARE(ADF_DH895XCC_MMP); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c b/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c index 7cd528ee31e7..481551a08708 100644 --- a/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c @@ -158,12 +158,11 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bar->size = pci_resource_len(pdev, bar_nr); bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0); if (!bar->virt_addr) { - dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr); + pci_err(pdev, "Failed to map BAR %d\n", bar_nr); ret = -EFAULT; goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); @@ -225,5 +224,4 @@ module_exit(adfdrv_release); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel"); MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); -MODULE_VERSION(ADF_DRV_VERSION); MODULE_IMPORT_NS("CRYPTO_QAT"); diff --git a/drivers/crypto/loongson/Kconfig b/drivers/crypto/loongson/Kconfig deleted file mode 100644 index 15475da8fc11..000000000000 --- a/drivers/crypto/loongson/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config CRYPTO_DEV_LOONGSON_RNG - tristate "Support for Loongson RNG Driver" - depends on MFD_LOONGSON_SE - help - Support for Loongson RNG Driver. diff --git a/drivers/crypto/loongson/Makefile b/drivers/crypto/loongson/Makefile deleted file mode 100644 index 1ce5ec32b553..000000000000 --- a/drivers/crypto/loongson/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_CRYPTO_DEV_LOONGSON_RNG) += loongson-rng.o diff --git a/drivers/crypto/loongson/loongson-rng.c b/drivers/crypto/loongson/loongson-rng.c deleted file mode 100644 index 3a4940260f9e..000000000000 --- a/drivers/crypto/loongson/loongson-rng.c +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2019 HiSilicon Limited. */ -/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ - -#include <linux/crypto.h> -#include <linux/err.h> -#include <linux/hw_random.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/mfd/loongson-se.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/platform_device.h> -#include <linux/random.h> -#include <crypto/internal/rng.h> - -#define SE_SEED_SIZE 32 - -struct loongson_rng_list { - struct mutex lock; - struct list_head list; - int registered; -}; - -struct loongson_rng { - u32 used; - struct loongson_se_engine *engine; - struct list_head list; - struct mutex lock; -}; - -struct loongson_rng_ctx { - struct loongson_rng *rng; -}; - -struct loongson_rng_cmd { - u32 cmd_id; - union { - u32 len; - u32 ret; - } u; - u32 seed_off; - u32 out_off; - u32 pad[4]; -}; - -static struct loongson_rng_list rng_devices = { - .lock = __MUTEX_INITIALIZER(rng_devices.lock), - .list = LIST_HEAD_INIT(rng_devices.list), -}; - -static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dstn, unsigned int dlen) -{ - struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct loongson_rng *rng = ctx->rng; - struct loongson_rng_cmd *cmd = rng->engine->command; - int err, len; - - mutex_lock(&rng->lock); - cmd->seed_off = 0; - do { - len = min(dlen, rng->engine->buffer_size); - cmd = rng->engine->command; - cmd->u.len = len; - err = loongson_se_send_engine_cmd(rng->engine); - if (err) - break; - - cmd = rng->engine->command_ret; - if (cmd->u.ret) { - err = -EIO; - break; - } - - memcpy(dstn, rng->engine->data_buffer, len); - dlen -= len; - dstn += len; - } while (dlen > 0); - mutex_unlock(&rng->lock); - - return err; -} - -static int loongson_rng_init(struct crypto_tfm *tfm) -{ - struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); - struct loongson_rng *rng; - u32 min_used = U32_MAX; - - mutex_lock(&rng_devices.lock); - list_for_each_entry(rng, &rng_devices.list, list) { - if (rng->used < min_used) { - ctx->rng = rng; - min_used = rng->used; - } - } - ctx->rng->used++; - mutex_unlock(&rng_devices.lock); - - return 0; -} - -static void loongson_rng_exit(struct crypto_tfm *tfm) -{ - struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); - - mutex_lock(&rng_devices.lock); - ctx->rng->used--; - mutex_unlock(&rng_devices.lock); -} - -static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct loongson_rng *rng = ctx->rng; - struct loongson_rng_cmd *cmd; - int err; - - if (slen < SE_SEED_SIZE) - return -EINVAL; - - slen = min(slen, rng->engine->buffer_size); - - mutex_lock(&rng->lock); - cmd = rng->engine->command; - cmd->u.len = slen; - cmd->seed_off = rng->engine->buffer_off; - memcpy(rng->engine->data_buffer, seed, slen); - err = loongson_se_send_engine_cmd(rng->engine); - if (err) - goto out; - - cmd = rng->engine->command_ret; - if (cmd->u.ret) - err = -EIO; -out: - mutex_unlock(&rng->lock); - - return err; -} - -static struct rng_alg loongson_rng_alg = { - .generate = loongson_rng_generate, - .seed = loongson_rng_seed, - .seedsize = SE_SEED_SIZE, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "loongson_stdrng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct loongson_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = loongson_rng_init, - .cra_exit = loongson_rng_exit, - }, -}; - -static int loongson_rng_probe(struct platform_device *pdev) -{ - struct loongson_rng_cmd *cmd; - struct loongson_rng *rng; - int ret = 0; - - rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); - if (!rng) - return -ENOMEM; - - rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG); - if (!rng->engine) - return -ENODEV; - cmd = rng->engine->command; - cmd->cmd_id = SE_CMD_RNG; - cmd->out_off = rng->engine->buffer_off; - mutex_init(&rng->lock); - - mutex_lock(&rng_devices.lock); - - if (!rng_devices.registered) { - ret = crypto_register_rng(&loongson_rng_alg); - if (ret) { - dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); - goto out; - } - rng_devices.registered = 1; - } - - list_add_tail(&rng->list, &rng_devices.list); -out: - mutex_unlock(&rng_devices.lock); - - return ret; -} - -static struct platform_driver loongson_rng_driver = { - .probe = loongson_rng_probe, - .driver = { - .name = "loongson-rng", - }, -}; -module_platform_driver(loongson_rng_driver); - -MODULE_ALIAS("platform:loongson-rng"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>"); -MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>"); -MODULE_DESCRIPTION("Loongson Random Number Generator driver"); diff --git a/drivers/crypto/marvell/cesa/cesa.c b/drivers/crypto/marvell/cesa/cesa.c index 8afa3a87e38d..75d8ba23d9a2 100644 --- a/drivers/crypto/marvell/cesa/cesa.c +++ b/drivers/crypto/marvell/cesa/cesa.c @@ -18,6 +18,7 @@ #include <linux/io.h> #include <linux/kthread.h> #include <linux/mbus.h> +#include <linux/minmax.h> #include <linux/platform_device.h> #include <linux/scatterlist.h> #include <linux/slab.h> @@ -416,7 +417,7 @@ static int mv_cesa_probe(struct platform_device *pdev) const struct mbus_dram_target_info *dram; struct device *dev = &pdev->dev; struct mv_cesa_dev *cesa; - struct mv_cesa_engine *engines; + struct mv_cesa_engine *engine; int irq, ret, i, cpu; u32 sram_size; @@ -431,7 +432,8 @@ static int mv_cesa_probe(struct platform_device *pdev) return -ENOTSUPP; } - cesa = devm_kzalloc(dev, sizeof(*cesa), GFP_KERNEL); + cesa = devm_kzalloc(dev, struct_size(cesa, engines, caps->nengines), + GFP_KERNEL); if (!cesa) return -ENOMEM; @@ -441,14 +443,8 @@ static int mv_cesa_probe(struct platform_device *pdev) sram_size = CESA_SA_DEFAULT_SRAM_SIZE; of_property_read_u32(cesa->dev->of_node, "marvell,crypto-sram-size", &sram_size); - if (sram_size < CESA_SA_MIN_SRAM_SIZE) - sram_size = CESA_SA_MIN_SRAM_SIZE; - cesa->sram_size = sram_size; - cesa->engines = devm_kcalloc(dev, caps->nengines, sizeof(*engines), - GFP_KERNEL); - if (!cesa->engines) - return -ENOMEM; + cesa->sram_size = max(sram_size, CESA_SA_MIN_SRAM_SIZE); spin_lock_init(&cesa->lock); @@ -465,7 +461,7 @@ static int mv_cesa_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cesa); for (i = 0; i < caps->nengines; i++) { - struct mv_cesa_engine *engine = &cesa->engines[i]; + engine = &cesa->engines[i]; char res_name[16]; engine->id = i; diff --git a/drivers/crypto/marvell/cesa/cesa.h b/drivers/crypto/marvell/cesa/cesa.h index 50ca1039fdaa..18f9f28040a6 100644 --- a/drivers/crypto/marvell/cesa/cesa.h +++ b/drivers/crypto/marvell/cesa/cesa.h @@ -403,27 +403,6 @@ struct mv_cesa_dev_dma { }; /** - * struct mv_cesa_dev - CESA device - * @caps: device capabilities - * @regs: device registers - * @sram_size: usable SRAM size - * @lock: device lock - * @engines: array of engines - * @dma: dma pools - * - * Structure storing CESA device information. - */ -struct mv_cesa_dev { - const struct mv_cesa_caps *caps; - void __iomem *regs; - struct device *dev; - unsigned int sram_size; - spinlock_t lock; - struct mv_cesa_engine *engines; - struct mv_cesa_dev_dma *dma; -}; - -/** * struct mv_cesa_engine - CESA engine * @id: engine id * @regs: engine registers @@ -472,6 +451,27 @@ struct mv_cesa_engine { }; /** + * struct mv_cesa_dev - CESA device + * @caps: device capabilities + * @regs: device registers + * @sram_size: usable SRAM size + * @lock: device lock + * @dma: dma pools + * @engines: array of engines + * + * Structure storing CESA device information. + */ +struct mv_cesa_dev { + const struct mv_cesa_caps *caps; + void __iomem *regs; + struct device *dev; + unsigned int sram_size; + spinlock_t lock; + struct mv_cesa_dev_dma *dma; + struct mv_cesa_engine engines[]; +}; + +/** * struct mv_cesa_req_ops - CESA request operations * @process: process a request chunk result (should return 0 if the * operation, -EINPROGRESS if it needs more steps or an error diff --git a/drivers/crypto/marvell/octeontx/otx_cptpf_main.c b/drivers/crypto/marvell/octeontx/otx_cptpf_main.c index 14a42559f81d..e4c828606a73 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptpf_main.c +++ b/drivers/crypto/marvell/octeontx/otx_cptpf_main.c @@ -283,6 +283,7 @@ static const struct pci_device_id otx_cpt_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OTX_CPT_PCI_PF_DEVICE_ID) }, { 0, } /* end of table */ }; +MODULE_DEVICE_TABLE(pci, otx_cpt_id_table); static struct pci_driver otx_cpt_pci_driver = { .name = DRV_NAME, @@ -298,4 +299,3 @@ MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell OcteonTX CPT Physical Function Driver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); -MODULE_DEVICE_TABLE(pci, otx_cpt_id_table); diff --git a/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c b/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c index e0f38d32bc93..205579a6ba2b 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c +++ b/drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c @@ -1318,7 +1318,7 @@ static ssize_t ucode_load_store(struct device *dev, { struct otx_cpt_engines engs[OTX_CPT_MAX_ETYPES_PER_GRP] = { {0} }; char *ucode_filename[OTX_CPT_MAX_ETYPES_PER_GRP]; - char tmp_buf[OTX_CPT_UCODE_NAME_LENGTH] = { 0 }; + char tmp_buf[OTX_CPT_UCODE_NAME_LENGTH]; char *start, *val, *err_msg, *tmp; struct otx_cpt_eng_grps *eng_grps; int grp_idx = 0, ret = -EINVAL; @@ -1326,12 +1326,11 @@ static ssize_t ucode_load_store(struct device *dev, int del_grp_idx = -1; int ucode_idx = 0; - if (count >= OTX_CPT_UCODE_NAME_LENGTH) + if (strscpy_pad(tmp_buf, buf) < 0) return -EINVAL; eng_grps = container_of(attr, struct otx_cpt_eng_grps, ucode_load_attr); err_msg = "Invalid engine group format"; - strscpy(tmp_buf, buf, OTX_CPT_UCODE_NAME_LENGTH); start = tmp_buf; has_se = has_ie = has_ae = false; diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_main.c b/drivers/crypto/marvell/octeontx/otx_cptvf_main.c index 587609db6c69..159c1d290a36 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_main.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_main.c @@ -957,9 +957,10 @@ static void otx_cptvf_remove(struct pci_dev *pdev) /* Supported devices */ static const struct pci_device_id otx_cptvf_id_table[] = { - {PCI_VDEVICE(CAVIUM, OTX_CPT_PCI_VF_DEVICE_ID), 0}, - { 0, } /* end of table */ + { PCI_VDEVICE(CAVIUM, OTX_CPT_PCI_VF_DEVICE_ID) }, + { } /* end of table */ }; +MODULE_DEVICE_TABLE(pci, otx_cptvf_id_table); static struct pci_driver otx_cptvf_pci_driver = { .name = DRV_NAME, @@ -974,4 +975,3 @@ MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell OcteonTX CPT Virtual Function Driver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); -MODULE_DEVICE_TABLE(pci, otx_cptvf_id_table); diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c index c80baf1ad90b..89030e2711ce 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c @@ -157,8 +157,8 @@ static inline int setup_sgio_components(struct pci_dev *pdev, sg_cleanup: for (j = 0; j < i; j++) { if (list[j].dma_addr) { - dma_unmap_single(&pdev->dev, list[i].dma_addr, - list[i].size, DMA_BIDIRECTIONAL); + dma_unmap_single(&pdev->dev, list[j].dma_addr, + list[j].size, DMA_BIDIRECTIONAL); } list[j].dma_addr = 0; diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c b/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c index 346d1345f11c..f6f47f4e5d83 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c @@ -867,6 +867,7 @@ static const struct pci_device_id otx2_cpt_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, CN10K_CPT_PCI_PF_DEVICE_ID) }, { 0, } /* end of table */ }; +MODULE_DEVICE_TABLE(pci, otx2_cpt_id_table); static struct pci_driver otx2_cpt_pci_driver = { .name = OTX2_CPT_DRV_NAME, @@ -883,4 +884,3 @@ MODULE_IMPORT_NS("CRYPTO_DEV_OCTEONTX2_CPT"); MODULE_AUTHOR("Marvell"); MODULE_DESCRIPTION(OTX2_CPT_DRV_STRING); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, otx2_cpt_id_table); diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c b/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c index 858f851c9c8a..09ab85e6061c 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c @@ -460,10 +460,11 @@ static void otx2_cptvf_remove(struct pci_dev *pdev) /* Supported devices */ static const struct pci_device_id otx2_cptvf_id_table[] = { - {PCI_VDEVICE(CAVIUM, OTX2_CPT_PCI_VF_DEVICE_ID), 0}, - {PCI_VDEVICE(CAVIUM, CN10K_CPT_PCI_VF_DEVICE_ID), 0}, - { 0, } /* end of table */ + { PCI_VDEVICE(CAVIUM, OTX2_CPT_PCI_VF_DEVICE_ID) }, + { PCI_VDEVICE(CAVIUM, CN10K_CPT_PCI_VF_DEVICE_ID) }, + { } /* end of table */ }; +MODULE_DEVICE_TABLE(pci, otx2_cptvf_id_table); static struct pci_driver otx2_cptvf_pci_driver = { .name = OTX2_CPTVF_DRV_NAME, @@ -479,4 +480,3 @@ MODULE_IMPORT_NS("CRYPTO_DEV_OCTEONTX2_CPT"); MODULE_AUTHOR("Marvell"); MODULE_DESCRIPTION("Marvell RVU CPT Virtual Function Driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, otx2_cptvf_id_table); diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c index 78135fb13f5c..1b7509e2ce44 100644 --- a/drivers/crypto/nx/nx.c +++ b/drivers/crypto/nx/nx.c @@ -714,15 +714,13 @@ int nx_crypto_ctx_aes_xcbc_init(struct crypto_shash *tfm) /** * nx_crypto_ctx_exit - destroy a crypto api context * - * @tfm: the crypto transform pointer for the context + * @nx_ctx: the crypto api context * * As crypto API contexts are destroyed, this exit hook is called to free the * memory associated with it. */ -void nx_crypto_ctx_exit(struct crypto_tfm *tfm) +void nx_crypto_ctx_exit(struct nx_crypto_ctx *nx_ctx) { - struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); - kfree_sensitive(nx_ctx->kmem); nx_ctx->csbcpb = NULL; nx_ctx->csbcpb_aead = NULL; diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h index 36974f08490a..6dfabfbf8192 100644 --- a/drivers/crypto/nx/nx.h +++ b/drivers/crypto/nx/nx.h @@ -153,7 +153,7 @@ int nx_crypto_ctx_aes_ctr_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_aes_cbc_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_aes_ecb_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_sha_init(struct crypto_shash *tfm); -void nx_crypto_ctx_exit(struct crypto_tfm *tfm); +void nx_crypto_ctx_exit(struct nx_crypto_ctx *nx_ctx); void nx_crypto_ctx_skcipher_exit(struct crypto_skcipher *tfm); void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm); void nx_crypto_ctx_shash_exit(struct crypto_shash *tfm); diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 3eadaf7a64fa..f31555c0d715 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -1089,6 +1089,20 @@ static struct attribute *omap_aes_attrs[] = { }; ATTRIBUTE_GROUPS(omap_aes); +static void omap_aes_unregister_algs(const struct omap_aes_pdata *pdata) +{ + struct omap_aes_algs_info *alg_info; + int i; + + for (i = pdata->algs_info_size - 1; i >= 0; i--) { + alg_info = &pdata->algs_info[i]; + + crypto_engine_unregister_skciphers(alg_info->algs_list, + alg_info->registered); + alg_info->registered = 0; + } +} + static int omap_aes_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1215,15 +1229,11 @@ static int omap_aes_probe(struct platform_device *pdev) return 0; err_aead_algs: - for (i = dd->pdata->aead_algs_info->registered - 1; i >= 0; i--) { - aalg = &dd->pdata->aead_algs_info->algs_list[i]; - crypto_engine_unregister_aead(aalg); - } + crypto_engine_unregister_aeads(dd->pdata->aead_algs_info->algs_list, + dd->pdata->aead_algs_info->registered); + dd->pdata->aead_algs_info->registered = 0; err_algs: - for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) - for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) - crypto_engine_unregister_skcipher( - &dd->pdata->algs_info[i].algs_list[j]); + omap_aes_unregister_algs(dd->pdata); err_engine: if (dd->engine) @@ -1244,25 +1254,16 @@ err_data: static void omap_aes_remove(struct platform_device *pdev) { struct omap_aes_dev *dd = platform_get_drvdata(pdev); - struct aead_engine_alg *aalg; - int i, j; spin_lock_bh(&list_lock); list_del(&dd->list); spin_unlock_bh(&list_lock); - for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) - for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) { - crypto_engine_unregister_skcipher( - &dd->pdata->algs_info[i].algs_list[j]); - dd->pdata->algs_info[i].registered--; - } + omap_aes_unregister_algs(dd->pdata); - for (i = dd->pdata->aead_algs_info->registered - 1; i >= 0; i--) { - aalg = &dd->pdata->aead_algs_info->algs_list[i]; - crypto_engine_unregister_aead(aalg); - dd->pdata->aead_algs_info->registered--; - } + crypto_engine_unregister_aeads(dd->pdata->aead_algs_info->algs_list, + dd->pdata->aead_algs_info->registered); + dd->pdata->aead_algs_info->registered = 0; crypto_engine_exit(dd->engine); diff --git a/drivers/crypto/omap-des.c b/drivers/crypto/omap-des.c index 149ebd77710b..dfaf831dd8ec 100644 --- a/drivers/crypto/omap-des.c +++ b/drivers/crypto/omap-des.c @@ -800,7 +800,6 @@ static struct omap_des_algs_info omap_des_algs_info_ecb_cbc[] = { }, }; -#ifdef CONFIG_OF static const struct omap_des_pdata omap_des_pdata_omap4 = { .algs_info = omap_des_algs_info_ecb_cbc, .algs_info_size = ARRAY_SIZE(omap_des_algs_info_ecb_cbc), @@ -909,6 +908,7 @@ static const struct of_device_id omap_des_of_match[] = { }; MODULE_DEVICE_TABLE(of, omap_des_of_match); +#ifdef CONFIG_OF static int omap_des_get_of(struct omap_des_dev *dd, struct platform_device *pdev) { @@ -923,7 +923,7 @@ static int omap_des_get_of(struct omap_des_dev *dd, } #else static int omap_des_get_of(struct omap_des_dev *dd, - struct device *dev) + struct platform_device *pdev) { return -EINVAL; } @@ -938,6 +938,20 @@ static int omap_des_get_pdev(struct omap_des_dev *dd, return 0; } +static void omap_des_unregister_algs(const struct omap_des_pdata *pdata) +{ + struct omap_des_algs_info *alg_info; + int i; + + for (i = pdata->algs_info_size - 1; i >= 0; i--) { + alg_info = &pdata->algs_info[i]; + + crypto_engine_unregister_skciphers(alg_info->algs_list, + alg_info->registered); + alg_info->registered = 0; + } +} + static int omap_des_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1043,11 +1057,7 @@ static int omap_des_probe(struct platform_device *pdev) return 0; err_algs: - for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) - for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) - crypto_engine_unregister_skcipher( - &dd->pdata->algs_info[i].algs_list[j]); - + omap_des_unregister_algs(dd->pdata); err_engine: if (dd->engine) crypto_engine_exit(dd->engine); @@ -1067,16 +1077,12 @@ err_data: static void omap_des_remove(struct platform_device *pdev) { struct omap_des_dev *dd = platform_get_drvdata(pdev); - int i, j; spin_lock_bh(&list_lock); list_del(&dd->list); spin_unlock_bh(&list_lock); - for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) - for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) - crypto_engine_unregister_skcipher( - &dd->pdata->algs_info[i].algs_list[j]); + omap_des_unregister_algs(dd->pdata); cancel_work_sync(&dd->done_task); omap_des_dma_cleanup(dd); @@ -1111,7 +1117,7 @@ static struct platform_driver omap_des_driver = { .driver = { .name = "omap-des", .pm = &omap_des_pm_ops, - .of_match_table = of_match_ptr(omap_des_of_match), + .of_match_table = omap_des_of_match, }, }; diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index b8c416c5ee70..be1ac640ee59 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -2042,6 +2042,20 @@ static struct attribute *omap_sham_attrs[] = { }; ATTRIBUTE_GROUPS(omap_sham); +static void omap_sham_unregister_algs(const struct omap_sham_pdata *pdata) +{ + struct omap_sham_algs_info *alg_info; + int i; + + for (i = pdata->algs_info_size - 1; i >= 0; i--) { + alg_info = &pdata->algs_info[i]; + + crypto_engine_unregister_ahashes(alg_info->algs_list, + alg_info->registered); + alg_info->registered = 0; + } +} + static int omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; @@ -2158,10 +2172,7 @@ static int omap_sham_probe(struct platform_device *pdev) return 0; err_algs: - for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) - for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) - crypto_engine_unregister_ahash( - &dd->pdata->algs_info[i].algs_list[j]); + omap_sham_unregister_algs(dd->pdata); err_engine_start: crypto_engine_exit(dd->engine); err_engine: @@ -2182,19 +2193,13 @@ data_err: static void omap_sham_remove(struct platform_device *pdev) { struct omap_sham_dev *dd; - int i, j; dd = platform_get_drvdata(pdev); spin_lock_bh(&sham.lock); list_del(&dd->list); spin_unlock_bh(&sham.lock); - for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) - for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) { - crypto_engine_unregister_ahash( - &dd->pdata->algs_info[i].algs_list[j]); - dd->pdata->algs_info[i].registered--; - } + omap_sham_unregister_algs(dd->pdata); cancel_work_sync(&dd->done_task); pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/crypto/qcom-rng.c b/drivers/crypto/qcom-rng.c index 0685ba122e8a..150e5802e351 100644 --- a/drivers/crypto/qcom-rng.c +++ b/drivers/crypto/qcom-rng.c @@ -265,7 +265,7 @@ static struct platform_driver qcom_rng_driver = { .remove = qcom_rng_remove, .driver = { .name = KBUILD_MODNAME, - .of_match_table = of_match_ptr(qcom_rng_of_match), + .of_match_table = qcom_rng_of_match, .acpi_match_table = ACPI_PTR(qcom_rng_acpi_match), } }; diff --git a/drivers/crypto/starfive/jh7110-cryp.c b/drivers/crypto/starfive/jh7110-cryp.c index 42114e9364f0..e19cd7945968 100644 --- a/drivers/crypto/starfive/jh7110-cryp.c +++ b/drivers/crypto/starfive/jh7110-cryp.c @@ -36,19 +36,14 @@ static struct starfive_dev_list dev_list = { struct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx) { - struct starfive_cryp_dev *cryp = NULL, *tmp; + struct starfive_cryp_dev *cryp; spin_lock_bh(&dev_list.lock); - if (!ctx->cryp) { - list_for_each_entry(tmp, &dev_list.dev_list, list) { - cryp = tmp; - break; - } - ctx->cryp = cryp; - } else { - cryp = ctx->cryp; - } - + if (!ctx->cryp) + ctx->cryp = list_first_entry_or_null(&dev_list.dev_list, + struct starfive_cryp_dev, + list); + cryp = ctx->cryp; spin_unlock_bh(&dev_list.lock); return cryp; diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index bc61d0fe3514..584508963241 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -12,7 +12,6 @@ * All rights reserved. */ -#include <linux/workqueue.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mod_devicetable.h> @@ -255,6 +254,46 @@ static int init_device(struct device *dev) return 0; } +static void dma_map_request(struct device *dev, struct talitos_request *request, + struct talitos_desc *desc, bool is_sec1) +{ + struct talitos_edesc *edesc = + container_of(desc, struct talitos_edesc, desc); + dma_addr_t dma_desc, prev_dma_desc; + struct talitos_edesc *prev_edesc = NULL; + + if (is_sec1) { + while (edesc) { + edesc->desc.hdr1 = edesc->desc.hdr; + + dma_desc = dma_map_single(dev, &edesc->desc.hdr1, + TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + + if (!prev_edesc) { + request->dma_desc = dma_desc; + goto next; + } + + /* Chain in any previous descriptors. */ + + prev_edesc->desc.next_desc = cpu_to_be32(dma_desc); + + dma_sync_single_for_device(dev, prev_dma_desc, + TALITOS_DESC_SIZE, + DMA_TO_DEVICE); + +next: + prev_edesc = edesc; + prev_dma_desc = dma_desc; + edesc = edesc->next_desc; + } + } else { + request->dma_desc = dma_map_single(dev, desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } +} + /** * talitos_submit - submits a descriptor to the device for processing * @dev: the SEC device to be used @@ -291,16 +330,7 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, request = &priv->chan[ch].fifo[head]; /* map descriptor and save caller data */ - if (is_sec1) { - desc->hdr1 = desc->hdr; - request->dma_desc = dma_map_single(dev, &desc->hdr1, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - } else { - request->dma_desc = dma_map_single(dev, desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - } + dma_map_request(dev, request, desc, is_sec1); request->callback = callback; request->context = context; @@ -322,19 +352,51 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, return -EINPROGRESS; } -static __be32 get_request_hdr(struct talitos_request *request, bool is_sec1) +static __be32 get_request_hdr(struct device *dev, + struct talitos_request *request, bool is_sec1) { struct talitos_edesc *edesc; + dma_addr_t dma_desc; - if (!is_sec1) - return request->desc->hdr; + if (!is_sec1) { + dma_sync_single_for_cpu(dev, request->dma_desc, + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - if (!request->desc->next_desc) - return request->desc->hdr1; + return request->desc->hdr; + } edesc = container_of(request->desc, struct talitos_edesc, desc); + dma_desc = request->dma_desc; + while (edesc->next_desc) { + dma_desc = be32_to_cpu(edesc->desc.next_desc); + edesc = edesc->next_desc; + } + + dma_sync_single_for_cpu(dev, dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); - return ((struct talitos_desc *)(edesc->buf + edesc->dma_len))->hdr1; + return edesc->desc.hdr1; +} + +static void dma_unmap_request(struct device *dev, + struct talitos_request *request, bool is_sec1) +{ + struct talitos_edesc *edesc; + + if (is_sec1) { + dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + edesc = container_of(request->desc, struct talitos_edesc, desc); + while (edesc->next_desc) { + dma_unmap_single(dev, + be32_to_cpu(edesc->desc.next_desc), + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = edesc->next_desc; + } + } else { + dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } } /* @@ -358,7 +420,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) /* descriptors with their done bits set don't get the error */ rmb(); - hdr = get_request_hdr(request, is_sec1); + hdr = get_request_hdr(dev, request, is_sec1); if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE) status = 0; @@ -368,9 +430,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) else status = error; - dma_unmap_single(dev, request->dma_desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); + dma_unmap_request(dev, request, is_sec1); /* copy entries so we can call callback outside lock */ saved_req.desc = request->desc; @@ -459,14 +519,35 @@ DEF_TALITOS2_DONE(ch0, TALITOS2_ISR_CH_0_DONE) DEF_TALITOS2_DONE(ch0_2, TALITOS2_ISR_CH_0_2_DONE) DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE) +static __be32 search_desc_hdr_in_request(struct talitos_request *request, + dma_addr_t cur_desc, bool is_sec1) +{ + struct talitos_edesc *edesc; + + if (request->dma_desc == cur_desc) { + return request->desc->hdr; + } else if (is_sec1) { + edesc = container_of(request->desc, struct talitos_edesc, desc); + while (edesc->next_desc) { + if (edesc->desc.next_desc == cpu_to_be32(cur_desc)) + return edesc->next_desc->desc.hdr1; + edesc = edesc->next_desc; + } + } + return 0; +} + /* * locate current (offending) descriptor */ static __be32 current_desc_hdr(struct device *dev, int ch) { struct talitos_private *priv = dev_get_drvdata(dev); + bool is_sec1 = has_ftr_sec1(priv); + struct talitos_request *request; int tail, iter; dma_addr_t cur_desc; + __be32 hdr = 0; cur_desc = ((u64)in_be32(priv->chan[ch].reg + TALITOS_CDPR)) << 32; cur_desc |= in_be32(priv->chan[ch].reg + TALITOS_CDPR_LO); @@ -477,27 +558,21 @@ static __be32 current_desc_hdr(struct device *dev, int ch) } tail = priv->chan[ch].tail; - iter = tail; - while (priv->chan[ch].fifo[iter].dma_desc != cur_desc && - priv->chan[ch].fifo[iter].desc->next_desc != cpu_to_be32(cur_desc)) { - iter = (iter + 1) & (priv->fifo_len - 1); - if (iter == tail) { - dev_err(dev, "couldn't locate current descriptor\n"); - return 0; - } - } + do { + request = &priv->chan[ch].fifo[iter]; - if (priv->chan[ch].fifo[iter].desc->next_desc == cpu_to_be32(cur_desc)) { - struct talitos_edesc *edesc; + hdr = search_desc_hdr_in_request(request, cur_desc, is_sec1); + if (hdr) + break; - edesc = container_of(priv->chan[ch].fifo[iter].desc, - struct talitos_edesc, desc); - return ((struct talitos_desc *) - (edesc->buf + edesc->dma_len))->hdr; - } + iter = (iter + 1) & (priv->fifo_len - 1); + } while (iter != tail); + + if (!hdr) + dev_err(dev, "couldn't locate current descriptor\n"); - return priv->chan[ch].fifo[iter].desc->hdr; + return hdr; } /* @@ -869,28 +944,20 @@ struct talitos_ahash_req_ctx { u8 buf[2][HASH_MAX_BLOCK_SIZE]; int buf_idx; unsigned int swinit; - unsigned int first_desc; - unsigned int last_desc; + unsigned int first_request; unsigned int last_request; unsigned int to_hash_later; unsigned int nbuf; struct scatterlist bufsl[2]; struct scatterlist *psrc; - - struct scatterlist request_bufsl[2]; - struct ahash_request *areq; - struct scatterlist *request_sl; - unsigned int remaining_ahash_request_bytes; - unsigned int current_ahash_request_bytes; - struct work_struct sec1_ahash_process_remaining; }; struct talitos_export_state { u32 hw_context[TALITOS_MDEU_MAX_CONTEXT_SIZE / sizeof(u32)]; u8 buf[HASH_MAX_BLOCK_SIZE]; unsigned int swinit; - unsigned int first_desc; - unsigned int last_desc; + unsigned int first_request; + unsigned int last_request; unsigned int to_hash_later; unsigned int nbuf; }; @@ -1396,10 +1463,6 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev, dma_len = 0; } alloc_len += icv_stashing ? authsize : 0; - - /* if its a ahash, add space for a second desc next to the first one */ - if (is_sec1 && !dst) - alloc_len += sizeof(struct talitos_desc); alloc_len += ivsize; edesc = kmalloc(ALIGN(alloc_len, dma_get_cache_alignment()), flags); @@ -1415,6 +1478,7 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev, edesc->dst_nents = dst_nents; edesc->iv_dma = iv_dma; edesc->dma_len = dma_len; + edesc->next_desc = NULL; if (dma_len) edesc->dma_link_tbl = dma_map_single(dev, &edesc->link_tbl[0], edesc->dma_len, @@ -1715,39 +1779,37 @@ static void common_nonsnoop_hash_unmap(struct device *dev, struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); struct talitos_desc *desc = &edesc->desc; - struct talitos_desc *desc2 = (struct talitos_desc *) - (edesc->buf + edesc->dma_len); unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE); - if (desc->next_desc && - desc->ptr[5].ptr != desc2->ptr[5].ptr) - unmap_single_talitos_ptr(dev, &desc2->ptr[5], DMA_FROM_DEVICE); - if (req_ctx->last_desc) + + if (edesc->last && req_ctx->last_request) memcpy(areq->result, req_ctx->hw_context, crypto_ahash_digestsize(tfm)); - if (req_ctx->psrc) - talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL, 0, 0); + if (edesc->src) + talitos_sg_unmap(dev, edesc, edesc->src, NULL, 0, 0); /* When using hashctx-in, must unmap it. */ if (from_talitos_ptr_len(&desc->ptr[1], is_sec1)) unmap_single_talitos_ptr(dev, &desc->ptr[1], DMA_TO_DEVICE); - else if (desc->next_desc) - unmap_single_talitos_ptr(dev, &desc2->ptr[1], - DMA_TO_DEVICE); - - if (is_sec1 && req_ctx->nbuf) - unmap_single_talitos_ptr(dev, &desc->ptr[3], - DMA_TO_DEVICE); if (edesc->dma_len) dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); +} + +static void free_edesc_list_from(struct ahash_request *areq, struct talitos_edesc *edesc) +{ + struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); + struct talitos_edesc *next; - if (desc->next_desc) - dma_unmap_single(dev, be32_to_cpu(desc->next_desc), - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + while (edesc) { + next = edesc->next_desc; + common_nonsnoop_hash_unmap(ctx->dev, edesc, areq); + kfree(edesc); + edesc = next; + } } static void ahash_done(struct device *dev, @@ -1758,30 +1820,46 @@ static void ahash_done(struct device *dev, struct talitos_edesc *edesc = container_of(desc, struct talitos_edesc, desc); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + bool is_sec1 = has_ftr_sec1(dev_get_drvdata(dev)); + struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); + struct talitos_edesc *next; - if (!req_ctx->last_desc && req_ctx->to_hash_later) { - /* Position any partial block for next update/final/finup */ - req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; - req_ctx->nbuf = req_ctx->to_hash_later; - } - common_nonsnoop_hash_unmap(dev, edesc, areq); - - kfree(edesc); + if (is_sec1) { + if (!req_ctx->last_request && req_ctx->to_hash_later) { + /* Position any partial block for next update/final/finup */ + req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; + req_ctx->nbuf = req_ctx->to_hash_later; + } - if (err) { + free_edesc_list_from(areq, edesc); ahash_request_complete(areq, err); - return; - } + } else { + next = edesc->next_desc; - req_ctx->remaining_ahash_request_bytes -= - req_ctx->current_ahash_request_bytes; + common_nonsnoop_hash_unmap(dev, edesc, areq); + kfree(edesc); - if (!req_ctx->remaining_ahash_request_bytes) { - ahash_request_complete(areq, 0); - return; - } + if (err) + goto out; - schedule_work(&req_ctx->sec1_ahash_process_remaining); + if (next) { + err = talitos_submit(dev, ctx->ch, &next->desc, + ahash_done, areq); + if (err != -EINPROGRESS) + goto out; + return; + } +out: + if (!req_ctx->last_request && req_ctx->to_hash_later) { + /* Position any partial block for next update/final/finup */ + req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; + req_ctx->nbuf = req_ctx->to_hash_later; + } + if (err && next) + free_edesc_list_from(areq, next); + ahash_request_complete(areq, err); + } } /* @@ -1805,18 +1883,15 @@ static void talitos_handle_buggy_hash(struct talitos_ctx *ctx, (char *)padded_hash, DMA_TO_DEVICE); } -static int common_nonsnoop_hash(struct talitos_edesc *edesc, - struct ahash_request *areq, unsigned int length, - void (*callback) (struct device *dev, - struct talitos_desc *desc, - void *context, int error)) +static void common_nonsnoop_hash(struct talitos_edesc *edesc, + struct ahash_request *areq, + unsigned int length) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); struct device *dev = ctx->dev; struct talitos_desc *desc = &edesc->desc; - int ret; bool sync_needed = false; struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); @@ -1825,7 +1900,7 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, /* first DWORD empty */ /* hash context in */ - if (!req_ctx->first_desc || req_ctx->swinit) { + if (!edesc->first || !req_ctx->first_request || req_ctx->swinit) { map_single_talitos_ptr_nosync(dev, &desc->ptr[1], req_ctx->hw_context_size, req_ctx->hw_context, @@ -1833,40 +1908,31 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, req_ctx->swinit = 0; } /* Indicate next op is not the first. */ - req_ctx->first_desc = 0; + req_ctx->first_request = 0; /* HMAC key */ if (ctx->keylen) to_talitos_ptr(&desc->ptr[2], ctx->dma_key, ctx->keylen, is_sec1); - if (is_sec1 && req_ctx->nbuf) - length -= req_ctx->nbuf; - sg_count = edesc->src_nents ?: 1; if (is_sec1 && sg_count > 1) - sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length); + sg_copy_to_buffer(edesc->src, sg_count, edesc->buf, length); else if (length) - sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count, - DMA_TO_DEVICE); + sg_count = dma_map_sg(dev, edesc->src, sg_count, DMA_TO_DEVICE); + /* * data in */ - if (is_sec1 && req_ctx->nbuf) { - map_single_talitos_ptr(dev, &desc->ptr[3], req_ctx->nbuf, - req_ctx->buf[req_ctx->buf_idx], - DMA_TO_DEVICE); - } else { - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc->ptr[3], sg_count, 0, 0); - if (sg_count > 1) - sync_needed = true; - } + sg_count = talitos_sg_map(dev, edesc->src, length, edesc, &desc->ptr[3], + sg_count, 0, 0); + if (sg_count > 1) + sync_needed = true; /* fifth DWORD empty */ /* hash/HMAC out -or- hash context out */ - if (req_ctx->last_desc) + if (edesc->last && req_ctx->last_request) map_single_talitos_ptr(dev, &desc->ptr[5], crypto_ahash_digestsize(tfm), req_ctx->hw_context, DMA_FROM_DEVICE); @@ -1881,73 +1947,94 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, if (is_sec1 && from_talitos_ptr_len(&desc->ptr[3], true) == 0) talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]); - if (is_sec1 && req_ctx->nbuf && length) { - struct talitos_desc *desc2 = (struct talitos_desc *) - (edesc->buf + edesc->dma_len); - dma_addr_t next_desc; - - memset(desc2, 0, sizeof(*desc2)); - desc2->hdr = desc->hdr; - desc2->hdr &= ~DESC_HDR_MODE0_MDEU_INIT; - desc2->hdr1 = desc2->hdr; - desc->hdr &= ~DESC_HDR_MODE0_MDEU_PAD; - desc->hdr |= DESC_HDR_MODE0_MDEU_CONT; - desc->hdr &= ~DESC_HDR_DONE_NOTIFY; - - if (desc->ptr[1].ptr) - copy_talitos_ptr(&desc2->ptr[1], &desc->ptr[1], - is_sec1); - else - map_single_talitos_ptr_nosync(dev, &desc2->ptr[1], - req_ctx->hw_context_size, - req_ctx->hw_context, - DMA_TO_DEVICE); - copy_talitos_ptr(&desc2->ptr[2], &desc->ptr[2], is_sec1); - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc2->ptr[3], sg_count, 0, 0); - if (sg_count > 1) - sync_needed = true; - copy_talitos_ptr(&desc2->ptr[5], &desc->ptr[5], is_sec1); - if (req_ctx->last_desc) - map_single_talitos_ptr_nosync(dev, &desc->ptr[5], - req_ctx->hw_context_size, - req_ctx->hw_context, - DMA_FROM_DEVICE); - - next_desc = dma_map_single(dev, &desc2->hdr1, TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - desc->next_desc = cpu_to_be32(next_desc); - } - if (sync_needed) dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); - - ret = talitos_submit(dev, ctx->ch, desc, callback, areq); - if (ret != -EINPROGRESS) { - common_nonsnoop_hash_unmap(dev, edesc, areq); - kfree(edesc); - } - return ret; } static struct talitos_edesc *ahash_edesc_alloc(struct ahash_request *areq, + struct scatterlist *src, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); + + return talitos_edesc_alloc(ctx->dev, src, NULL, NULL, 0, + nbytes, 0, 0, 0, areq->base.flags, false); +} + +static struct talitos_edesc * +ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, + unsigned int blocksize, bool is_sec1) +{ + struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - struct talitos_private *priv = dev_get_drvdata(ctx->dev); - bool is_sec1 = has_ftr_sec1(priv); + struct talitos_edesc *first = NULL, *prev_edesc = NULL, *edesc; + size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN : + TALITOS2_MAX_DATA_LEN; + struct scatterlist tmp[2]; + size_t to_hash_this_desc; + struct scatterlist *src; + size_t offset = 0; + + do { + src = scatterwalk_ffwd(tmp, req_ctx->psrc, offset); + + to_hash_this_desc = + min(nbytes, ALIGN_DOWN(desc_max, blocksize)); + + /* Allocate extended descriptor */ + edesc = ahash_edesc_alloc(areq, src, to_hash_this_desc); + if (IS_ERR(edesc)) { + if (first) + free_edesc_list_from(areq, first); + return edesc; + } - if (is_sec1) - nbytes -= req_ctx->nbuf; + edesc->src = + scatterwalk_ffwd(edesc->bufsl, req_ctx->psrc, offset); + edesc->desc.hdr = ctx->desc_hdr_template; + edesc->first = offset == 0; + edesc->last = nbytes - to_hash_this_desc == 0; - return talitos_edesc_alloc(ctx->dev, req_ctx->psrc, NULL, NULL, 0, - nbytes, 0, 0, 0, areq->base.flags, false); + /* On last one, request SEC to pad; otherwise continue */ + if (req_ctx->last_request && edesc->last) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD; + else + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; + + /* request SEC to INIT hash. */ + if (req_ctx->first_request && edesc->first && !req_ctx->swinit) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; + + /* + * When the tfm context has a keylen, it's an HMAC. + * A first or last (ie. not middle) descriptor must request HMAC. + */ + if (ctx->keylen && ((req_ctx->first_request && edesc->first) || + (req_ctx->last_request && edesc->last))) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; + + /* clear the DN bit */ + if (is_sec1 && !edesc->last) + edesc->desc.hdr &= ~DESC_HDR_DONE_NOTIFY; + + common_nonsnoop_hash(edesc, areq, to_hash_this_desc); + + offset += to_hash_this_desc; + nbytes -= to_hash_this_desc; + + if (!prev_edesc) + first = edesc; + else + prev_edesc->next_desc = edesc; + prev_edesc = edesc; + } while (nbytes); + + return first; } -static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes) +static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); @@ -1955,23 +2042,23 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes struct talitos_edesc *edesc; unsigned int blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + bool is_sec1 = has_ftr_sec1(dev_get_drvdata(ctx->dev)); unsigned int nbytes_to_hash; unsigned int to_hash_later; unsigned int nsg; int nents; struct device *dev = ctx->dev; - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); u8 *ctx_buf = req_ctx->buf[req_ctx->buf_idx]; + int ret; - if (!req_ctx->last_desc && (nbytes + req_ctx->nbuf <= blocksize)) { + if (!req_ctx->last_request && (nbytes + req_ctx->nbuf <= blocksize)) { /* Buffer up to one whole block */ - nents = sg_nents_for_len(req_ctx->request_sl, nbytes); + nents = sg_nents_for_len(areq->src, nbytes); if (nents < 0) { dev_err(dev, "Invalid number of src SG.\n"); return nents; } - sg_copy_to_buffer(req_ctx->request_sl, nents, + sg_copy_to_buffer(areq->src, nents, ctx_buf + req_ctx->nbuf, nbytes); req_ctx->nbuf += nbytes; return 0; @@ -1981,7 +2068,7 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes nbytes_to_hash = nbytes + req_ctx->nbuf; to_hash_later = nbytes_to_hash & (blocksize - 1); - if (req_ctx->last_desc) + if (req_ctx->last_request) to_hash_later = 0; else if (to_hash_later) /* There is a partial block. Hash the full block(s) now */ @@ -1993,123 +2080,39 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes } /* Chain in any previously buffered data */ - if (!is_sec1 && req_ctx->nbuf) { + if (req_ctx->nbuf) { nsg = (req_ctx->nbuf < nbytes_to_hash) ? 2 : 1; sg_init_table(req_ctx->bufsl, nsg); sg_set_buf(req_ctx->bufsl, ctx_buf, req_ctx->nbuf); if (nsg > 1) - sg_chain(req_ctx->bufsl, 2, req_ctx->request_sl); + sg_chain(req_ctx->bufsl, 2, areq->src); req_ctx->psrc = req_ctx->bufsl; - } else if (is_sec1 && req_ctx->nbuf && req_ctx->nbuf < blocksize) { - int offset; - - if (nbytes_to_hash > blocksize) - offset = blocksize - req_ctx->nbuf; - else - offset = nbytes_to_hash - req_ctx->nbuf; - nents = sg_nents_for_len(req_ctx->request_sl, offset); - if (nents < 0) { - dev_err(dev, "Invalid number of src SG.\n"); - return nents; - } - sg_copy_to_buffer(req_ctx->request_sl, nents, - ctx_buf + req_ctx->nbuf, offset); - req_ctx->nbuf += offset; - req_ctx->psrc = scatterwalk_ffwd(req_ctx->bufsl, req_ctx->request_sl, - offset); } else - req_ctx->psrc = req_ctx->request_sl; + req_ctx->psrc = areq->src; if (to_hash_later) { - nents = sg_nents_for_len(req_ctx->request_sl, nbytes); + nents = sg_nents_for_len(areq->src, nbytes); if (nents < 0) { dev_err(dev, "Invalid number of src SG.\n"); return nents; } - sg_pcopy_to_buffer(req_ctx->request_sl, nents, + sg_pcopy_to_buffer(areq->src, nents, req_ctx->buf[(req_ctx->buf_idx + 1) & 1], to_hash_later, nbytes - to_hash_later); } req_ctx->to_hash_later = to_hash_later; - /* Allocate extended descriptor */ - edesc = ahash_edesc_alloc(req_ctx->areq, nbytes_to_hash); + edesc = ahash_process_req_prepare(areq, nbytes_to_hash, blocksize, + is_sec1); if (IS_ERR(edesc)) return PTR_ERR(edesc); - edesc->desc.hdr = ctx->desc_hdr_template; - - /* On last one, request SEC to pad; otherwise continue */ - if (req_ctx->last_desc) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD; - else - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; - - /* request SEC to INIT hash. */ - if (req_ctx->first_desc && !req_ctx->swinit) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; - - /* When the tfm context has a keylen, it's an HMAC. - * A first or last (ie. not middle) descriptor must request HMAC. - */ - if (ctx->keylen && (req_ctx->first_desc || req_ctx->last_desc)) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; + ret = talitos_submit(dev, ctx->ch, &edesc->desc, ahash_done, areq); + if (ret != -EINPROGRESS) + free_edesc_list_from(areq, edesc); - return common_nonsnoop_hash(edesc, req_ctx->areq, nbytes_to_hash, ahash_done); -} - -static void sec1_ahash_process_remaining(struct work_struct *work) -{ - struct talitos_ahash_req_ctx *req_ctx = - container_of(work, struct talitos_ahash_req_ctx, - sec1_ahash_process_remaining); - int err = 0; - - req_ctx->request_sl = scatterwalk_ffwd(req_ctx->request_bufsl, - req_ctx->request_sl, TALITOS1_MAX_DATA_LEN); - - if (req_ctx->remaining_ahash_request_bytes > TALITOS1_MAX_DATA_LEN) - req_ctx->current_ahash_request_bytes = TALITOS1_MAX_DATA_LEN; - else { - req_ctx->current_ahash_request_bytes = - req_ctx->remaining_ahash_request_bytes; - - if (req_ctx->last_request) - req_ctx->last_desc = 1; - } - - err = ahash_process_req_one(req_ctx->areq, - req_ctx->current_ahash_request_bytes); - - if (err != -EINPROGRESS) - ahash_request_complete(req_ctx->areq, err); -} - -static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) -{ - struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); - struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); - struct device *dev = ctx->dev; - struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); - - req_ctx->areq = areq; - req_ctx->request_sl = areq->src; - req_ctx->remaining_ahash_request_bytes = nbytes; - - if (is_sec1) { - if (nbytes > TALITOS1_MAX_DATA_LEN) - nbytes = TALITOS1_MAX_DATA_LEN; - else if (req_ctx->last_request) - req_ctx->last_desc = 1; - } - - req_ctx->current_ahash_request_bytes = nbytes; - - return ahash_process_req_one(req_ctx->areq, - req_ctx->current_ahash_request_bytes); + return ret; } static int ahash_init(struct ahash_request *areq) @@ -2124,15 +2127,13 @@ static int ahash_init(struct ahash_request *areq) /* Initialize the context */ req_ctx->buf_idx = 0; req_ctx->nbuf = 0; - req_ctx->first_desc = 1; /* first_desc indicates h/w must init its context */ + req_ctx->first_request = 1; req_ctx->swinit = 0; /* assume h/w init of context */ size = (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE) ? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256 : TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512; req_ctx->hw_context_size = size; req_ctx->last_request = 0; - req_ctx->last_desc = 0; - INIT_WORK(&req_ctx->sec1_ahash_process_remaining, sec1_ahash_process_remaining); dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size, DMA_TO_DEVICE); @@ -2224,8 +2225,8 @@ static int ahash_export(struct ahash_request *areq, void *out) req_ctx->hw_context_size); memcpy(export->buf, req_ctx->buf[req_ctx->buf_idx], req_ctx->nbuf); export->swinit = req_ctx->swinit; - export->first_desc = req_ctx->first_desc; - export->last_desc = req_ctx->last_desc; + export->first_request = req_ctx->first_request; + export->last_request = req_ctx->last_request; export->to_hash_later = req_ctx->to_hash_later; export->nbuf = req_ctx->nbuf; @@ -2250,8 +2251,8 @@ static int ahash_import(struct ahash_request *areq, const void *in) memcpy(req_ctx->hw_context, export->hw_context, size); memcpy(req_ctx->buf[0], export->buf, export->nbuf); req_ctx->swinit = export->swinit; - req_ctx->first_desc = export->first_desc; - req_ctx->last_desc = export->last_desc; + req_ctx->first_request = export->first_request; + req_ctx->last_request = export->last_request; req_ctx->to_hash_later = export->to_hash_later; req_ctx->nbuf = export->nbuf; @@ -3409,14 +3410,19 @@ static int talitos_probe(struct platform_device *ofdev) struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; struct talitos_private *priv; + unsigned int num_channels; int i, err; int stride; - struct resource *res; - priv = devm_kzalloc(dev, sizeof(struct talitos_private), GFP_KERNEL); + if (of_property_read_u32(np, "fsl,num-channels", &num_channels)) + return -EINVAL; + + priv = devm_kzalloc(dev, struct_size(priv, chan, num_channels), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->num_channels = num_channels; + INIT_LIST_HEAD(&priv->alg_list); dev_set_drvdata(dev, priv); @@ -3425,18 +3431,14 @@ static int talitos_probe(struct platform_device *ofdev) spin_lock_init(&priv->reg_lock); - res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - priv->reg = devm_ioremap(dev, res->start, resource_size(res)); - if (!priv->reg) { + priv->reg = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(priv->reg)) { dev_err(dev, "failed to of_iomap\n"); - err = -ENOMEM; + err = PTR_ERR(priv->reg); goto err_out; } /* get SEC version capabilities from device tree */ - of_property_read_u32(np, "fsl,num-channels", &priv->num_channels); of_property_read_u32(np, "fsl,channel-fifo-len", &priv->chfifo_len); of_property_read_u32(np, "fsl,exec-units-mask", &priv->exec_units); of_property_read_u32(np, "fsl,descriptor-types-mask", @@ -3511,16 +3513,6 @@ static int talitos_probe(struct platform_device *ofdev) } } - priv->chan = devm_kcalloc(dev, - priv->num_channels, - sizeof(struct talitos_channel), - GFP_KERNEL); - if (!priv->chan) { - dev_err(dev, "failed to allocate channel management space\n"); - err = -ENOMEM; - goto err_out; - } - priv->fifo_len = roundup_pow_of_two(priv->chfifo_len); for (i = 0; i < priv->num_channels; i++) { diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h index 1a93ee355929..d4ff8d589f46 100644 --- a/drivers/crypto/talitos.h +++ b/drivers/crypto/talitos.h @@ -44,11 +44,17 @@ struct talitos_desc { /* * talitos_edesc - s/w-extended descriptor + * @bufsl: scatterlist buffer + * @src: pointer to input scatterlist + * @first: first descriptor of a chain + * @last: last descriptor of a chain + * * @src_nents: number of segments in input scatterlist * @dst_nents: number of segments in output scatterlist * @iv_dma: dma address of iv for checking continuity and link table * @dma_len: length of dma mapped link_tbl space * @dma_link_tbl: bus physical address of link_tbl/buf + * @next_desc: next descriptor * @desc: h/w descriptor * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1) (SEC2) * @buf: input and output buffeur (if {src,dst}_nents > 1) (SEC1) @@ -58,11 +64,17 @@ struct talitos_desc { * of link_tbl data */ struct talitos_edesc { + struct scatterlist bufsl[2]; + struct scatterlist *src; + int first; + int last; + int src_nents; int dst_nents; dma_addr_t iv_dma; int dma_len; dma_addr_t dma_link_tbl; + struct talitos_edesc *next_desc; struct talitos_desc desc; union { DECLARE_FLEX_ARRAY(struct talitos_ptr, link_tbl); @@ -139,8 +151,6 @@ struct talitos_private { */ unsigned int fifo_len; - struct talitos_channel *chan; - /* next channel to be assigned next incoming descriptor */ atomic_t last_chan ____cacheline_aligned; @@ -153,6 +163,9 @@ struct talitos_private { /* hwrng device */ struct hwrng rng; bool rng_registered; + + struct talitos_channel chan[] __counted_by(num_channels); + }; /* .features flag */ diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 30c78afe3dea..9094c03e991f 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -1201,6 +1201,7 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); struct tegra_se *se = ctx->se; + unsigned int bufsize; int ret; ret = tegra_ccm_crypt_init(req, se, rctx); @@ -1210,19 +1211,19 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) rctx->key_id = ctx->key_id; /* Allocate buffers required */ - rctx->inbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; - rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->inbuf.size, + bufsize = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; + rctx->inbuf.size = bufsize; + rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); + ret = -ENOMEM; if (!rctx->inbuf.buf) goto out_finalize; - rctx->outbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; - rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->outbuf.size, + rctx->outbuf.size = bufsize; + rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); - if (!rctx->outbuf.buf) { - ret = -ENOMEM; + if (!rctx->outbuf.buf) goto out_free_inbuf; - } if (!ctx->key_id) { ret = tegra_key_submit_reserved_aes(ctx->se, ctx->key, @@ -1254,11 +1255,11 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) } out: - dma_free_coherent(ctx->se->dev, rctx->inbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->outbuf.buf, rctx->outbuf.addr); out_free_inbuf: - dma_free_coherent(ctx->se->dev, rctx->outbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->inbuf.buf, rctx->inbuf.addr); if (tegra_key_is_reserved(rctx->key_id)) @@ -1278,6 +1279,7 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); struct tegra_aead_reqctx *rctx = aead_request_ctx(req); + unsigned int bufsize; int ret; rctx->src_sg = req->src; @@ -1296,16 +1298,17 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) rctx->key_id = ctx->key_id; /* Allocate buffers required */ - rctx->inbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen; - rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->inbuf.size, + bufsize = rctx->assoclen + rctx->authsize + rctx->cryptlen; + rctx->inbuf.size = bufsize; + rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); if (!rctx->inbuf.buf) { ret = -ENOMEM; goto out_finalize; } - rctx->outbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen; - rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->outbuf.size, + rctx->outbuf.size = bufsize; + rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); if (!rctx->outbuf.buf) { ret = -ENOMEM; @@ -1342,11 +1345,11 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) ret = tegra_gcm_do_verify(ctx->se, rctx); out: - dma_free_coherent(ctx->se->dev, rctx->outbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->outbuf.buf, rctx->outbuf.addr); out_free_inbuf: - dma_free_coherent(ctx->se->dev, rctx->inbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->inbuf.buf, rctx->inbuf.addr); if (tegra_key_is_reserved(rctx->key_id)) diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c index eb71113ed146..d2f518ef9a10 100644 --- a/drivers/crypto/tegra/tegra-se-main.c +++ b/drivers/crypto/tegra/tegra-se-main.c @@ -52,7 +52,7 @@ tegra_se_cmdbuf_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_dire return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -93,7 +93,6 @@ static void tegra_se_cmdbuf_unpin(struct host1x_bo_mapping *map) dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0); sg_free_table(map->sgt); kfree(map->sgt); - host1x_bo_put(map->bo); kfree(map); } @@ -180,7 +179,7 @@ int tegra_se_host1x_submit(struct tegra_se *se, struct tegra_se_cmdbuf *cmdbuf, MAX_SCHEDULE_TIMEOUT, NULL); if (ret) { dev_err(se->dev, "host1x job timed out\n"); - return ret; + goto job_put; } host1x_job_put(job); diff --git a/drivers/crypto/xilinx/Makefile b/drivers/crypto/xilinx/Makefile index 9b51636ef75e..730feff5b5f2 100644 --- a/drivers/crypto/xilinx/Makefile +++ b/drivers/crypto/xilinx/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_CRYPTO_DEV_XILINX_TRNG) += xilinx-trng.o obj-$(CONFIG_CRYPTO_DEV_ZYNQMP_AES) += zynqmp-aes-gcm.o obj-$(CONFIG_CRYPTO_DEV_ZYNQMP_SHA3) += zynqmp-sha.o diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index 179de3cf6d6c..9e183cd8818c 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c @@ -17,7 +17,13 @@ #include <linux/list.h> #include <linux/of.h> -static struct class *devfreq_event_class; +static struct attribute *devfreq_event_attrs[]; +ATTRIBUTE_GROUPS(devfreq_event); + +static const struct class devfreq_event_class = { + .name = "devfreq-event", + .dev_groups = devfreq_event_groups +}; /* The list of all devfreq event list */ static LIST_HEAD(devfreq_event_list); @@ -321,7 +327,7 @@ struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, edev->desc = desc; edev->enable_count = 0; edev->dev.parent = dev; - edev->dev.class = devfreq_event_class; + edev->dev.class = &devfreq_event_class; edev->dev.release = devfreq_event_release_edev; dev_set_name(&edev->dev, "event%d", atomic_inc_return(&event_no)); @@ -461,18 +467,15 @@ static struct attribute *devfreq_event_attrs[] = { &dev_attr_enable_count.attr, NULL, }; -ATTRIBUTE_GROUPS(devfreq_event); static int __init devfreq_event_init(void) { - devfreq_event_class = class_create("devfreq-event"); - if (IS_ERR(devfreq_event_class)) { - pr_err("%s: couldn't create class\n", __FILE__); - return PTR_ERR(devfreq_event_class); - } + int err; - devfreq_event_class->dev_groups = devfreq_event_groups; + err = class_register(&devfreq_event_class); + if (err) + pr_err("%s: couldn't create class\n", __FILE__); - return 0; + return err; } subsys_initcall(devfreq_event_init); diff --git a/drivers/dio/dio.c b/drivers/dio/dio.c index 419b3c13d491..7722d9eae6d0 100644 --- a/drivers/dio/dio.c +++ b/drivers/dio/dio.c @@ -178,7 +178,7 @@ static int __init dio_init(void) if (!MACH_IS_HP300) return 0; - printk(KERN_INFO "Scanning for DIO devices...\n"); + pr_info("Scanning for DIO devices...\n"); /* Initialize the DIO bus */ INIT_LIST_HEAD(&dio_bus.devices); @@ -247,18 +247,19 @@ static int __init dio_init(void) dev->id = prid; dev->ipl = DIO_IPL(va); - strcpy(dev->name, dio_getname(dev->id)); - printk(KERN_INFO "select code %3d: ipl %d: ID %02X", dev->scode, dev->ipl, prid); + strscpy(dev->name, dio_getname(dev->id)); if (DIO_NEEDSSECID(prid)) - printk(":%02X", secid); - printk(": %s\n", dev->name); + pr_info("select code %3d: ipl %u: ID %02X:%02X: %s\n", + dev->scode, dev->ipl, prid, secid, dev->name); + else + pr_info("select code %3d: ipl %u: ID %02X: %s\n", + dev->scode, dev->ipl, prid, dev->name); if (scode >= DIOII_SCBASE) iounmap(va); error = device_register(&dev->dev); if (error) { - pr_err("DIO: Error registering device %s\n", - dev->name); + pr_err("DIO: Error registering device %s\n", dev->name); put_device(&dev->dev); continue; } diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 71f37544a5c6..d504c636dc29 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -792,9 +792,13 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags) if (!dmabuf || !dmabuf->file) return -EINVAL; - fd = FD_ADD(flags, dmabuf->file); + fd = get_unused_fd_flags(flags); + if (fd < 0) + return fd; + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); + fd_install(fd, dmabuf->file); return fd; } EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); diff --git a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c index f87d244cc2d6..686dc140293e 100644 --- a/drivers/dma/ti/k3-udma-glue.c +++ b/drivers/dma/ti/k3-udma-glue.c @@ -312,7 +312,7 @@ k3_udma_glue_request_tx_chn_common(struct device *dev, if (xudma_is_pktdma(tx_chn->common.udmax)) { /* prepare the channel device as coherent */ - tx_chn->common.chan_dev.dma_coherent = true; + dev_set_dma_coherent(&tx_chn->common.chan_dev); dma_coerce_mask_and_coherent(&tx_chn->common.chan_dev, DMA_BIT_MASK(48)); } @@ -1003,7 +1003,7 @@ k3_udma_glue_request_rx_chn_priv(struct device *dev, const char *name, if (xudma_is_pktdma(rx_chn->common.udmax)) { /* prepare the channel device as coherent */ - rx_chn->common.chan_dev.dma_coherent = true; + dev_set_dma_coherent(&rx_chn->common.chan_dev); dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev, DMA_BIT_MASK(48)); } @@ -1104,7 +1104,7 @@ k3_udma_glue_request_remote_rx_chn_common(struct k3_udma_glue_rx_channel *rx_chn if (xudma_is_pktdma(rx_chn->common.udmax)) { /* prepare the channel device as coherent */ - rx_chn->common.chan_dev.dma_coherent = true; + dev_set_dma_coherent(&rx_chn->common.chan_dev); dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev, DMA_BIT_MASK(48)); rx_chn->single_fdq = false; diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index c964ebfcf3b6..1cf158eb7bdb 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -428,18 +428,18 @@ static void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel) /* No special handling for the channel */ chan->dev->chan_dma_dev = false; - chan_dev->dma_coherent = false; + dev_clear_dma_coherent(chan_dev); chan_dev->dma_parms = NULL; } else if (asel == 14 || asel == 15) { chan->dev->chan_dma_dev = true; - chan_dev->dma_coherent = true; + dev_set_dma_coherent(chan_dev); dma_coerce_mask_and_coherent(chan_dev, DMA_BIT_MASK(48)); chan_dev->dma_parms = chan_dev->parent->dma_parms; } else { dev_warn(chan->device->dev, "Invalid ASEL value: %u\n", asel); - chan_dev->dma_coherent = false; + dev_clear_dma_coherent(chan_dev); chan_dev->dma_parms = NULL; } } diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index 0ff1658c2dc1..75e3ae0c16d0 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -829,12 +829,21 @@ int dpll_device_delete_ntf(struct dpll_device *dpll) return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll); } -static int -__dpll_device_change_ntf(struct dpll_device *dpll) +/** + * __dpll_device_change_ntf - notify that the dpll device has been changed + * @dpll: registered dpll pointer + * + * Context: caller must hold dpll_lock. Suitable for use inside device + * callbacks which are already invoked under dpll_lock. + * Return: 0 if succeeds, error code otherwise. + */ +int __dpll_device_change_ntf(struct dpll_device *dpll) { + lockdep_assert_held(&dpll_lock); dpll_device_notify(dpll, DPLL_DEVICE_CHANGED); return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll); } +EXPORT_SYMBOL_GPL(__dpll_device_change_ntf); /** * dpll_device_change_ntf - notify that the dpll device has been changed diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 5f1e70f3e40a..0a133b0f2d97 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -762,18 +762,15 @@ zl3073x_dev_periodic_work(struct kthread_work *work) dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n", ERR_PTR(rc)); - /* Update measured input reference frequencies if any DPLL has - * frequency monitoring enabled. + /* Update measured input reference frequencies if frequency + * monitoring is enabled. */ - list_for_each_entry(zldpll, &zldev->dplls, list) { - if (zldpll->freq_monitor) { - rc = zl3073x_ref_freq_meas_update(zldev); - if (rc) - dev_warn(zldev->dev, - "Failed to update measured frequencies: %pe\n", - ERR_PTR(rc)); - break; - } + if (zldev->freq_monitor) { + rc = zl3073x_ref_freq_meas_update(zldev); + if (rc) + dev_warn(zldev->dev, + "Failed to update measured frequencies: %pe\n", + ERR_PTR(rc)); } /* Update references' fractional frequency offsets */ diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 99440620407d..addba378b0df 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -57,6 +57,7 @@ struct zl3073x_chip_info { * @work: periodic work * @clock_id: clock id of the device * @phase_avg_factor: phase offset measurement averaging factor + * @freq_monitor: is frequency monitor enabled */ struct zl3073x_dev { struct device *dev; @@ -77,9 +78,10 @@ struct zl3073x_dev { struct kthread_worker *kworker; struct kthread_delayed_work work; - /* Devlink parameters */ + /* Per-chip parameters */ u64 clock_id; u8 phase_avg_factor; + bool freq_monitor; }; extern const struct regmap_config zl3073x_regmap_config; diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 64b4e9e3e8fe..0bfcbae2109f 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -1079,15 +1079,6 @@ zl3073x_dpll_phase_offset_avg_factor_get(const struct dpll_device *dpll, return 0; } -static void -zl3073x_dpll_change_work(struct work_struct *work) -{ - struct zl3073x_dpll *zldpll; - - zldpll = container_of(work, struct zl3073x_dpll, change_work); - dpll_device_change_ntf(zldpll->dpll_dev); -} - static int zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll, void *dpll_priv, u32 factor, @@ -1113,8 +1104,10 @@ zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll, * we have to send a notification for other DPLL devices. */ list_for_each_entry(item, &zldpll->dev->dplls, list) { - if (item != zldpll) - schedule_work(&item->change_work); + struct dpll_device *dpll_dev = READ_ONCE(item->dpll_dev); + + if (item != zldpll && dpll_dev) + __dpll_device_change_ntf(dpll_dev); } return 0; @@ -1219,7 +1212,7 @@ zl3073x_dpll_freq_monitor_get(const struct dpll_device *dpll, { struct zl3073x_dpll *zldpll = dpll_priv; - if (zldpll->freq_monitor) + if (zldpll->dev->freq_monitor) *state = DPLL_FEATURE_STATE_ENABLE; else *state = DPLL_FEATURE_STATE_DISABLE; @@ -1233,9 +1226,19 @@ zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll, enum dpll_feature_state state, struct netlink_ext_ack *extack) { - struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dpll *item, *zldpll = dpll_priv; - zldpll->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE); + zldpll->dev->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE); + + /* The frequency monitoring is common for all DPLL channels so after + * change we have to send a notification for other DPLL devices. + */ + list_for_each_entry(item, &zldpll->dev->dplls, list) { + struct dpll_device *dpll_dev = READ_ONCE(item->dpll_dev); + + if (item != zldpll && dpll_dev) + __dpll_device_change_ntf(dpll_dev); + } return 0; } @@ -1627,13 +1630,13 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll) static void zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll) { - WARN(!zldpll->dpll_dev, "DPLL device is not registered\n"); + struct dpll_device *dpll_dev = READ_ONCE(zldpll->dpll_dev); - cancel_work_sync(&zldpll->change_work); + WARN(!dpll_dev, "DPLL device is not registered\n"); - dpll_device_unregister(zldpll->dpll_dev, &zldpll->ops, zldpll); - dpll_device_put(zldpll->dpll_dev, &zldpll->tracker); - zldpll->dpll_dev = NULL; + WRITE_ONCE(zldpll->dpll_dev, NULL); + dpll_device_unregister(dpll_dev, &zldpll->ops, zldpll); + dpll_device_put(dpll_dev, &zldpll->tracker); } /** @@ -1752,7 +1755,7 @@ zl3073x_dpll_pin_measured_freq_check(struct zl3073x_dpll_pin *pin) u8 ref_id; u32 freq; - if (!zldpll->freq_monitor) + if (!zldpll->dev->freq_monitor) return false; ref_id = zl3073x_input_pin_ref_get(pin->id); @@ -1785,10 +1788,8 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) struct zl3073x_dev *zldev = zldpll->dev; enum dpll_lock_status lock_status; struct device *dev = zldev->dev; - const struct zl3073x_chan *chan; struct zl3073x_dpll_pin *pin; int rc; - u8 mode; zldpll->check_count++; @@ -1807,15 +1808,6 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) dpll_device_change_ntf(zldpll->dpll_dev); } - /* Input pin monitoring does make sense only in automatic - * or forced reference modes. - */ - chan = zl3073x_chan_state_get(zldev, zldpll->id); - mode = zl3073x_chan_mode_get(chan); - if (mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO && - mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK) - return; - /* Update phase offset latch registers for this DPLL if the phase * offset monitor feature is enabled. */ @@ -1926,7 +1918,6 @@ zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch) zldpll->dev = zldev; zldpll->id = ch; INIT_LIST_HEAD(&zldpll->pins); - INIT_WORK(&zldpll->change_work, zl3073x_dpll_change_work); return zldpll; } diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h index 434c32a7db12..21adcc18e45e 100644 --- a/drivers/dpll/zl3073x/dpll.h +++ b/drivers/dpll/zl3073x/dpll.h @@ -15,13 +15,11 @@ * @id: DPLL index * @check_count: periodic check counter * @phase_monitor: is phase offset monitor enabled - * @freq_monitor: is frequency monitor enabled * @ops: DPLL device operations for this instance * @dpll_dev: pointer to registered DPLL device * @tracker: tracking object for the acquired reference * @lock_status: last saved DPLL lock status * @pins: list of pins - * @change_work: device change notification work */ struct zl3073x_dpll { struct list_head list; @@ -29,13 +27,11 @@ struct zl3073x_dpll { u8 id; u8 check_count; bool phase_monitor; - bool freq_monitor; struct dpll_device_ops ops; struct dpll_device *dpll_dev; dpll_tracker tracker; enum dpll_lock_status lock_status; struct list_head pins; - struct work_struct change_work; }; struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch); diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 2a49f68a7cf9..7bb11ffdb0c9 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -332,14 +332,14 @@ static void amd76x_remove_one(struct pci_dev *pdev) static const struct pci_device_id amd76x_pci_tbl[] = { { - PCI_VEND_DEV(AMD, FE_GATE_700C), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - AMD762}, - { - PCI_VEND_DEV(AMD, FE_GATE_700E), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - AMD761}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(AMD, FE_GATE_700C), + .driver_data = AMD762 + }, { + PCI_VEND_DEV(AMD, FE_GATE_700E), + .driver_data = AMD761, + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, amd76x_pci_tbl); diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 7221b4bb6df2..77c1fe75451e 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -1414,20 +1414,20 @@ static void e752x_remove_one(struct pci_dev *pdev) static const struct pci_device_id e752x_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, 7520_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - E7520}, - { - PCI_VEND_DEV(INTEL, 7525_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - E7525}, - { - PCI_VEND_DEV(INTEL, 7320_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - E7320}, - { - PCI_VEND_DEV(INTEL, 3100_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - I3100}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, 7520_0), + .driver_data = E7520, + }, { + PCI_VEND_DEV(INTEL, 7525_0), + .driver_data = E7525, + }, { + PCI_VEND_DEV(INTEL, 7320_0), + .driver_data = E7320, + }, { + PCI_VEND_DEV(INTEL, 3100_0), + .driver_data = I3100, + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, e752x_pci_tbl); diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 5852b95fa470..02071180b638 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -554,20 +554,20 @@ static void e7xxx_remove_one(struct pci_dev *pdev) static const struct pci_device_id e7xxx_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, 7205_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - E7205}, - { - PCI_VEND_DEV(INTEL, 7500_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - E7500}, - { - PCI_VEND_DEV(INTEL, 7501_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - E7501}, - { - PCI_VEND_DEV(INTEL, 7505_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - E7505}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, 7205_0), + .driver_data = E7205, + }, { + PCI_VEND_DEV(INTEL, 7500_0), + .driver_data = E7500, + }, { + PCI_VEND_DEV(INTEL, 7501_0), + .driver_data = E7501, + }, { + PCI_VEND_DEV(INTEL, 7505_0), + .driver_data = E7505 + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, e7xxx_pci_tbl); diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h index 881b00eadf7a..9505bbd41784 100644 --- a/drivers/edac/edac_mc.h +++ b/drivers/edac/edac_mc.h @@ -88,8 +88,8 @@ do { \ #endif /* !CONFIG_EDAC_DEBUG */ -#define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \ - PCI_DEVICE_ID_ ## vend ## _ ## dev +#define PCI_VEND_DEV(vend, dev) \ + PCI_DEVICE(PCI_VENDOR_ID_ ## vend, PCI_DEVICE_ID_ ## vend ## _ ## dev) #define edac_dev_name(dev) (dev)->dev_name diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c index 63df35444214..fa1853f252b0 100644 --- a/drivers/edac/i10nm_base.c +++ b/drivers/edac/i10nm_base.c @@ -47,12 +47,6 @@ readl((m)->mbase + ((m)->hbm_mc ? 0xef8 : \ (res_cfg->type == GNR ? 0xaf8 : 0x20ef8)) + \ (i) * (m)->chan_mmio_sz) -#define I10NM_GET_REG32(m, i, offset) \ - readl((m)->mbase + (i) * (m)->chan_mmio_sz + (offset)) -#define I10NM_GET_REG64(m, i, offset) \ - readq((m)->mbase + (i) * (m)->chan_mmio_sz + (offset)) -#define I10NM_SET_REG32(m, i, offset, v) \ - writel(v, (m)->mbase + (i) * (m)->chan_mmio_sz + (offset)) #define I10NM_GET_SCK_MMIO_BASE(reg) (GET_BITFIELD(reg, 0, 28) << 23) #define I10NM_GET_IMC_MMIO_OFFSET(reg) (GET_BITFIELD(reg, 0, 10) << 12) @@ -79,11 +73,12 @@ static struct res_config *res_cfg; static int retry_rd_err_log; static int decoding_via_mca; static bool mem_cfg_2lm; +static bool no_adxl; static struct reg_rrl icx_reg_rrl_ddr = { .set_num = 2, .reg_num = 6, - .modes = {LRE_SCRUB, LRE_DEMAND}, + .sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND}, .offsets = { {0x22c60, 0x22c54, 0x22c5c, 0x22c58, 0x22c28, 0x20ed8}, {0x22e54, 0x22e60, 0x22e64, 0x22e58, 0x22e5c, 0x20ee0}, @@ -104,7 +99,7 @@ static struct reg_rrl icx_reg_rrl_ddr = { static struct reg_rrl spr_reg_rrl_ddr = { .set_num = 3, .reg_num = 6, - .modes = {LRE_SCRUB, LRE_DEMAND, FRE_DEMAND}, + .sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND, RRL_SRC_FRE_DEMAND}, .offsets = { {0x22c60, 0x22c54, 0x22f08, 0x22c58, 0x22c28, 0x20ed8}, {0x22e54, 0x22e60, 0x22f10, 0x22e58, 0x22e5c, 0x20ee0}, @@ -126,7 +121,7 @@ static struct reg_rrl spr_reg_rrl_ddr = { static struct reg_rrl spr_reg_rrl_hbm_pch0 = { .set_num = 2, .reg_num = 6, - .modes = {LRE_SCRUB, LRE_DEMAND}, + .sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND}, .offsets = { {0x2860, 0x2854, 0x2b08, 0x2858, 0x2828, 0x0ed8}, {0x2a54, 0x2a60, 0x2b10, 0x2a58, 0x2a5c, 0x0ee0}, @@ -147,7 +142,7 @@ static struct reg_rrl spr_reg_rrl_hbm_pch0 = { static struct reg_rrl spr_reg_rrl_hbm_pch1 = { .set_num = 2, .reg_num = 6, - .modes = {LRE_SCRUB, LRE_DEMAND}, + .sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND}, .offsets = { {0x2c60, 0x2c54, 0x2f08, 0x2c58, 0x2c28, 0x0fa8}, {0x2e54, 0x2e60, 0x2f10, 0x2e58, 0x2e5c, 0x0fb0}, @@ -168,7 +163,7 @@ static struct reg_rrl spr_reg_rrl_hbm_pch1 = { static struct reg_rrl gnr_reg_rrl_ddr = { .set_num = 4, .reg_num = 6, - .modes = {FRE_SCRUB, FRE_DEMAND, LRE_SCRUB, LRE_DEMAND}, + .sources = {RRL_SRC_FRE_SCRUB, RRL_SRC_FRE_DEMAND, RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND}, .offsets = { {0x2f10, 0x2f20, 0x2f30, 0x2f50, 0x2f60, 0xba0}, {0x2f14, 0x2f24, 0x2f38, 0x2f54, 0x2f64, 0xba8}, @@ -188,214 +183,6 @@ static struct reg_rrl gnr_reg_rrl_ddr = { .cecnt_widths = {4, 4, 4, 4, 4, 4, 4, 4}, }; -static u64 read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width) -{ - switch (width) { - case 4: - return I10NM_GET_REG32(imc, chan, offset); - case 8: - return I10NM_GET_REG64(imc, chan, offset); - default: - i10nm_printk(KERN_ERR, "Invalid read RRL 0x%x width %d\n", offset, width); - return 0; - } -} - -static void write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 val) -{ - switch (width) { - case 4: - return I10NM_SET_REG32(imc, chan, offset, (u32)val); - default: - i10nm_printk(KERN_ERR, "Invalid write RRL 0x%x width %d\n", offset, width); - } -} - -static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl, - int rrl_set, bool enable, u32 *rrl_ctl) -{ - enum rrl_mode mode = rrl->modes[rrl_set]; - u32 offset = rrl->offsets[rrl_set][0], v; - u8 width = rrl->widths[0]; - bool first, scrub; - - /* First or last read error. */ - first = (mode == FRE_SCRUB || mode == FRE_DEMAND); - /* Patrol scrub or on-demand read error. */ - scrub = (mode == FRE_SCRUB || mode == LRE_SCRUB); - - v = read_imc_reg(imc, chan, offset, width); - - if (enable) { - /* Save default configurations. */ - *rrl_ctl = v; - v &= ~rrl->uc_mask; - - if (first) - v |= rrl->noover_mask; - else - v &= ~rrl->noover_mask; - - if (scrub) - v |= rrl->en_patspr_mask; - else - v &= ~rrl->en_patspr_mask; - - v |= rrl->en_mask; - } else { - /* Restore default configurations. */ - if (*rrl_ctl & rrl->uc_mask) - v |= rrl->uc_mask; - - if (first) { - if (!(*rrl_ctl & rrl->noover_mask)) - v &= ~rrl->noover_mask; - } else { - if (*rrl_ctl & rrl->noover_mask) - v |= rrl->noover_mask; - } - - if (scrub) { - if (!(*rrl_ctl & rrl->en_patspr_mask)) - v &= ~rrl->en_patspr_mask; - } else { - if (*rrl_ctl & rrl->en_patspr_mask) - v |= rrl->en_patspr_mask; - } - - if (!(*rrl_ctl & rrl->en_mask)) - v &= ~rrl->en_mask; - } - - write_imc_reg(imc, chan, offset, width, v); -} - -static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl, - bool enable, u32 *rrl_ctl) -{ - for (int i = 0; i < rrl->set_num; i++) - enable_rrl(imc, chan, rrl, i, enable, rrl_ctl + i); -} - -static void enable_rrls_ddr(struct skx_imc *imc, bool enable) -{ - struct reg_rrl *rrl_ddr = res_cfg->reg_rrl_ddr; - int i, chan_num = res_cfg->ddr_chan_num; - struct skx_channel *chan = imc->chan; - - if (!imc->mbase) - return; - - for (i = 0; i < chan_num; i++) - enable_rrls(imc, i, rrl_ddr, enable, chan[i].rrl_ctl[0]); -} - -static void enable_rrls_hbm(struct skx_imc *imc, bool enable) -{ - struct reg_rrl **rrl_hbm = res_cfg->reg_rrl_hbm; - int i, chan_num = res_cfg->hbm_chan_num; - struct skx_channel *chan = imc->chan; - - if (!imc->mbase || !imc->hbm_mc || !rrl_hbm[0] || !rrl_hbm[1]) - return; - - for (i = 0; i < chan_num; i++) { - enable_rrls(imc, i, rrl_hbm[0], enable, chan[i].rrl_ctl[0]); - enable_rrls(imc, i, rrl_hbm[1], enable, chan[i].rrl_ctl[1]); - } -} - -static void enable_retry_rd_err_log(bool enable) -{ - struct skx_dev *d; - int i, imc_num; - - edac_dbg(2, "\n"); - - list_for_each_entry(d, i10nm_edac_list, list) { - imc_num = res_cfg->ddr_imc_num; - for (i = 0; i < imc_num; i++) - enable_rrls_ddr(&d->imc[i], enable); - - imc_num += res_cfg->hbm_imc_num; - for (; i < imc_num; i++) - enable_rrls_hbm(&d->imc[i], enable); - } -} - -static void show_retry_rd_err_log(struct decoded_addr *res, char *msg, - int len, bool scrub_err) -{ - int i, j, n, ch = res->channel, pch = res->cs & 1; - struct skx_imc *imc = &res->dev->imc[res->imc]; - u64 log, corr, status_mask; - struct reg_rrl *rrl; - bool scrub; - u32 offset; - u8 width; - - if (!imc->mbase) - return; - - rrl = imc->hbm_mc ? res_cfg->reg_rrl_hbm[pch] : res_cfg->reg_rrl_ddr; - - if (!rrl) - return; - - status_mask = rrl->over_mask | rrl->uc_mask | rrl->v_mask; - - n = scnprintf(msg, len, " retry_rd_err_log["); - for (i = 0; i < rrl->set_num; i++) { - scrub = (rrl->modes[i] == FRE_SCRUB || rrl->modes[i] == LRE_SCRUB); - if (scrub_err != scrub) - continue; - - for (j = 0; j < rrl->reg_num && len - n > 0; j++) { - offset = rrl->offsets[i][j]; - width = rrl->widths[j]; - log = read_imc_reg(imc, ch, offset, width); - - if (width == 4) - n += scnprintf(msg + n, len - n, "%.8llx ", log); - else - n += scnprintf(msg + n, len - n, "%.16llx ", log); - - /* Clear RRL status if RRL in Linux control mode. */ - if (retry_rd_err_log == 2 && !j && (log & status_mask)) - write_imc_reg(imc, ch, offset, width, log & ~status_mask); - } - } - - /* Move back one space. */ - n--; - n += scnprintf(msg + n, len - n, "]"); - - if (len - n > 0) { - n += scnprintf(msg + n, len - n, " correrrcnt["); - for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) { - offset = rrl->cecnt_offsets[i]; - width = rrl->cecnt_widths[i]; - corr = read_imc_reg(imc, ch, offset, width); - - /* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */ - if (res_cfg->type <= SPR) { - n += scnprintf(msg + n, len - n, "%.4llx %.4llx ", - corr & 0xffff, corr >> 16); - } else { - /* CPUs {GNR} encode one counter per CORRERRCNT register. */ - if (width == 4) - n += scnprintf(msg + n, len - n, "%.8llx ", corr); - else - n += scnprintf(msg + n, len - n, "%.16llx ", corr); - } - } - - /* Move back one space. */ - n--; - n += scnprintf(msg + n, len - n, "]"); - } -} - static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus, unsigned int dev, unsigned int fun) { @@ -980,7 +767,7 @@ static struct res_config i10nm_cfg0 = { .ddr_mdev_bdf = {0, 12, 0}, .hbm_mdev_bdf = {0, 12, 1}, .sad_all_offset = 0x108, - .reg_rrl_ddr = &icx_reg_rrl_ddr, + .reg_rrl_ddr[0] = &icx_reg_rrl_ddr, }; static struct res_config i10nm_cfg1 = { @@ -998,7 +785,7 @@ static struct res_config i10nm_cfg1 = { .ddr_mdev_bdf = {0, 12, 0}, .hbm_mdev_bdf = {0, 12, 1}, .sad_all_offset = 0x108, - .reg_rrl_ddr = &icx_reg_rrl_ddr, + .reg_rrl_ddr[0] = &icx_reg_rrl_ddr, }; static struct res_config spr_cfg = { @@ -1021,7 +808,7 @@ static struct res_config spr_cfg = { .ddr_mdev_bdf = {0, 12, 0}, .hbm_mdev_bdf = {0, 12, 1}, .sad_all_offset = 0x300, - .reg_rrl_ddr = &spr_reg_rrl_ddr, + .reg_rrl_ddr[0] = &spr_reg_rrl_ddr, .reg_rrl_hbm[0] = &spr_reg_rrl_hbm_pch0, .reg_rrl_hbm[1] = &spr_reg_rrl_hbm_pch1, }; @@ -1041,7 +828,7 @@ static struct res_config gnr_cfg = { .uracu_bdf = {0, 0, 1}, .ddr_mdev_bdf = {0, 5, 1}, .sad_all_offset = 0x300, - .reg_rrl_ddr = &gnr_reg_rrl_ddr, + .reg_rrl_ddr[0] = &gnr_reg_rrl_ddr, }; static const struct x86_cpu_id i10nm_cpuids[] = { @@ -1222,21 +1009,28 @@ static int __init i10nm_init(void) } rc = skx_adxl_get(); - if (rc) - goto fail; + if (rc) { + /* Decoding errors via MCA banks for 2LM isn't supported yet */ + if (rc != -ENODEV || mem_cfg_2lm) + goto fail; + i10nm_printk(KERN_INFO, "ADXL not found, falling back to MCA-based decoding.\n"); + no_adxl = true; + decoding_via_mca = true; + } opstate_init(); mce_register_decode_chain(&i10nm_mce_dec); skx_setup_debug("i10nm_test"); - if (retry_rd_err_log && res_cfg->reg_rrl_ddr) { - skx_set_decode(i10nm_mc_decode, show_retry_rd_err_log); - if (retry_rd_err_log == 2) - enable_retry_rd_err_log(true); - } else { - skx_set_decode(i10nm_mc_decode, NULL); + res_cfg->rrl_ctrl_mode = retry_rd_err_log; + if (retry_rd_err_log && res_cfg->reg_rrl_ddr[0]) { + skx_set_show_rrl(skx_show_rrl); + if (retry_rd_err_log == RRL_CTRL_LINUX) + skx_enable_rrl(true); } + skx_set_decode(i10nm_mc_decode); + i10nm_printk(KERN_INFO, "%s\n", I10NM_REVISION); return 0; @@ -1249,15 +1043,18 @@ static void __exit i10nm_exit(void) { edac_dbg(2, "\n"); - if (retry_rd_err_log && res_cfg->reg_rrl_ddr) { - skx_set_decode(NULL, NULL); - if (retry_rd_err_log == 2) - enable_retry_rd_err_log(false); + skx_set_decode(NULL); + + if (retry_rd_err_log && res_cfg->reg_rrl_ddr[0]) { + if (retry_rd_err_log == RRL_CTRL_LINUX) + skx_enable_rrl(false); + skx_set_show_rrl(NULL); } skx_teardown_debug(); mce_unregister_decode_chain(&i10nm_mce_dec); - skx_adxl_put(); + if (!no_adxl) + skx_adxl_put(); skx_remove(); } diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index 9065bc4386ff..04a231660b88 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -485,11 +485,11 @@ static void i3000_remove_one(struct pci_dev *pdev) static const struct pci_device_id i3000_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, 3000_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - I3000}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, 3000_HB), + .driver_data = I3000, + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, i3000_pci_tbl); diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index 6cade6d7ceff..d600b6c05217 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -466,11 +466,11 @@ static void i3200_remove_one(struct pci_dev *pdev) static const struct pci_device_id i3200_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - I3200}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, 3200_HB), + .driver_data = I3200, + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, i3200_pci_tbl); diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 471b8540d18b..c0faf55f7812 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -352,6 +352,9 @@ struct i5000_pvt { /* Actual values for this controller */ int maxch; /* Max channels */ int maxdimmperch; /* Max DIMMs per channel */ + + /* Hardware error reporting status */ + bool enabled_error_reporting; }; /* I5000 MCH error information retrieved from Hardware */ @@ -1302,10 +1305,10 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) } /* - * i5000_enable_error_reporting - * Turn on the memory reporting features of the hardware + * i5000_set_error_reporting + * Turn on/off the memory reporting features of the hardware */ -static void i5000_enable_error_reporting(struct mem_ctl_info *mci) +static void i5000_set_error_reporting(struct mem_ctl_info *mci, bool enable) { struct i5000_pvt *pvt; u32 fbd_error_mask; @@ -1316,8 +1319,11 @@ static void i5000_enable_error_reporting(struct mem_ctl_info *mci) pci_read_config_dword(pvt->branchmap_werrors, EMASK_FBD, &fbd_error_mask); - /* Enable with a '0' */ - fbd_error_mask &= ~(ENABLE_EMASK_ALL); + /* Enable with 0, disable with 1 */ + if (enable) + fbd_error_mask &= ~(ENABLE_EMASK_ALL); + else + fbd_error_mask |= ENABLE_EMASK_ALL; pci_write_config_dword(pvt->branchmap_werrors, EMASK_FBD, fbd_error_mask); @@ -1435,17 +1441,19 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) if (i5000_init_csrows(mci)) { edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5000_init_csrows() returned nonzero value\n"); mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */ + pvt->enabled_error_reporting = false; } else { edac_dbg(1, "MC: Enable error reporting now\n"); - i5000_enable_error_reporting(mci); + i5000_set_error_reporting(mci, true); + pvt->enabled_error_reporting = true; } /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); - /* FIXME: perhaps some code should go here that disables error - * reporting if we just enabled it - */ + /* Disable error reporting if we previously enabled it */ + if (pvt->enabled_error_reporting) + i5000_set_error_reporting(mci, false); goto fail1; } @@ -1503,6 +1511,7 @@ static int i5000_init_one(struct pci_dev *pdev, const struct pci_device_id *id) static void i5000_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; + struct i5000_pvt *pvt; edac_dbg(0, "\n"); @@ -1512,6 +1521,12 @@ static void i5000_remove_one(struct pci_dev *pdev) if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL) return; + pvt = mci->pvt_info; + + /* Disable error reporting on teardown */ + if (pvt->enabled_error_reporting) + i5000_set_error_reporting(mci, false); + /* retrieve references to resources, and free those resources */ i5000_put_devices(mci); edac_mc_free(mci); diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index d470afe65001..d30919ceb22b 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -859,6 +859,21 @@ static void i5100_init_csrows(struct mem_ctl_info *mci) } } +static void i5100_set_error_reporting(struct pci_dev *pdev, bool enable) +{ + u32 dw; + + pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw); + + /* Enable with 0, disable with 1 */ + if (enable) + dw &= ~I5100_FERR_NF_MEM_ANY_MASK; + else + dw |= I5100_FERR_NF_MEM_ANY_MASK; + + pci_write_config_dword(pdev, I5100_EMASK_MEM, dw); +} + /**************************************************************************** * Error injection routines ****************************************************************************/ @@ -1004,11 +1019,6 @@ static int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_read_config_dword(pdev, I5100_MS, &dw); ranksperch = !!(dw & (1 << 8)) * 2 + 4; - /* enable error reporting... */ - pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw); - dw &= ~I5100_FERR_NF_MEM_ANY_MASK; - pci_write_config_dword(pdev, I5100_EMASK_MEM, dw); - /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */ ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_21, 0); @@ -1125,6 +1135,9 @@ static int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id) i5100_setup_debugfs(mci); + /* Enable error reporting on success */ + i5100_set_error_reporting(pdev, true); + return ret; bail_scrub: @@ -1169,6 +1182,9 @@ static void i5100_remove_one(struct pci_dev *pdev) priv = mci->pvt_info; + /* Disable error reporting at teardown */ + i5100_set_error_reporting(pdev, false); + edac_debugfs_remove_recursive(priv->debugfs); priv->scrub_enable = 0; diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index fb49a1d1df11..ae4f9298952d 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -353,6 +353,9 @@ struct i5400_pvt { /* Actual values for this controller */ int maxch; /* Max channels */ int maxdimmperch; /* Max DIMMs per channel */ + + /* Hardware error reporting status */ + bool enabled_error_reporting; }; /* I5400 MCH error information retrieved from Hardware */ @@ -1223,10 +1226,10 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) } /* - * i5400_enable_error_reporting - * Turn on the memory reporting features of the hardware + * i5400_set_error_reporting + * Turn on/off the memory reporting features of the hardware */ -static void i5400_enable_error_reporting(struct mem_ctl_info *mci) +static void i5400_set_error_reporting(struct mem_ctl_info *mci, bool enable) { struct i5400_pvt *pvt; u32 fbd_error_mask; @@ -1237,8 +1240,11 @@ static void i5400_enable_error_reporting(struct mem_ctl_info *mci) pci_read_config_dword(pvt->branchmap_werrors, EMASK_FBD, &fbd_error_mask); - /* Enable with a '0' */ - fbd_error_mask &= ~(ENABLE_EMASK_ALL); + /* Enable with 0, disable with 1 */ + if (enable) + fbd_error_mask &= ~(ENABLE_EMASK_ALL); + else + fbd_error_mask |= ENABLE_EMASK_ALL; pci_write_config_dword(pvt->branchmap_werrors, EMASK_FBD, fbd_error_mask); @@ -1319,17 +1325,19 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx) if (i5400_init_dimms(mci)) { edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5400_init_dimms() returned nonzero value\n"); mci->edac_cap = EDAC_FLAG_NONE; /* no dimms found */ + pvt->enabled_error_reporting = false; } else { edac_dbg(1, "MC: Enable error reporting now\n"); - i5400_enable_error_reporting(mci); + i5400_set_error_reporting(mci, true); + pvt->enabled_error_reporting = true; } /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); - /* FIXME: perhaps some code should go here that disables error - * reporting if we just enabled it - */ + /* Disable error reporting if we just enabled it */ + if (pvt->enabled_error_reporting) + i5400_set_error_reporting(mci, false); goto fail1; } @@ -1387,6 +1395,7 @@ static int i5400_init_one(struct pci_dev *pdev, const struct pci_device_id *id) static void i5400_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; + struct i5400_pvt *pvt; edac_dbg(0, "\n"); @@ -1397,6 +1406,12 @@ static void i5400_remove_one(struct pci_dev *pdev) if (!mci) return; + pvt = mci->pvt_info; + + /* Disable error reporting on teardown */ + if (pvt->enabled_error_reporting) + i5400_set_error_reporting(mci, false); + /* retrieve references to resources, and free those resources */ i5400_put_devices(mci); diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index 69068f8d0cad..64bc2d805a62 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -111,6 +111,9 @@ struct i7300_pvt { /* Temporary buffer for use when preparing error messages */ char *tmp_prt_buffer; + + /* Hardware error reporting status */ + bool enabled_error_reporting; }; /* FIXME: Why do we need to have this static? */ @@ -550,11 +553,12 @@ static void i7300_clear_error(struct mem_ctl_info *mci) } /** - * i7300_enable_error_reporting() - Enable the memory reporting logic at the + * i7300_set_error_reporting() - Enable or disable the memory reporting logic at the * hardware * @mci: struct mem_ctl_info pointer + * @enable: enables if 'true', disables if 'false' */ -static void i7300_enable_error_reporting(struct mem_ctl_info *mci) +static void i7300_set_error_reporting(struct mem_ctl_info *mci, bool enable) { struct i7300_pvt *pvt = mci->pvt_info; u32 fbd_error_mask; @@ -563,8 +567,11 @@ static void i7300_enable_error_reporting(struct mem_ctl_info *mci) pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, EMASK_FBD, &fbd_error_mask); - /* Enable with a '0' */ - fbd_error_mask &= ~(EMASK_FBD_ERR_MASK); + /* Enable with 0, disable with 1 */ + if (enable) + fbd_error_mask &= ~(EMASK_FBD_ERR_MASK); + else + fbd_error_mask |= EMASK_FBD_ERR_MASK; pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map, EMASK_FBD, fbd_error_mask); @@ -1087,17 +1094,19 @@ static int i7300_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (i7300_get_mc_regs(mci)) { edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i7300_init_csrows() returned nonzero value\n"); mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */ + pvt->enabled_error_reporting = false; } else { edac_dbg(1, "MC: Enable error reporting now\n"); - i7300_enable_error_reporting(mci); + i7300_set_error_reporting(mci, true); + pvt->enabled_error_reporting = true; } /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); - /* FIXME: perhaps some code should go here that disables error - * reporting if we just enabled it - */ + /* Disable error reporting if we just enabled it */ + if (pvt->enabled_error_reporting) + i7300_set_error_reporting(mci, false); goto fail1; } @@ -1134,6 +1143,7 @@ fail0: static void i7300_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; + struct i7300_pvt *pvt; char *tmp; edac_dbg(0, "\n"); @@ -1145,7 +1155,12 @@ static void i7300_remove_one(struct pci_dev *pdev) if (!mci) return; - tmp = ((struct i7300_pvt *)mci->pvt_info)->tmp_prt_buffer; + pvt = (struct i7300_pvt *)mci->pvt_info; + tmp = pvt->tmp_prt_buffer; + + /* Disable error reporting before unregistering device */ + if (pvt->enabled_error_reporting) + i7300_set_error_reporting(mci, false); /* retrieve references to resources, and free those resources */ i7300_put_devices(mci); diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index b8a497f0de28..e8c1ee80bba8 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -287,11 +287,11 @@ static void i82860_remove_one(struct pci_dev *pdev) static const struct pci_device_id i82860_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - I82860}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, 82860_0), + .driver_data = I82860, + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, i82860_pci_tbl); diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 553880b9fc12..869de8e372b3 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -276,7 +276,7 @@ static int i82875p_setup_overfl_dev(struct pci_dev *pdev, *ovrfl_pdev = NULL; *ovrfl_window = NULL; - dev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL); + dev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82875_6, NULL); if (dev == NULL) { /* Intel tells BIOS developers to hide device 6 which @@ -518,11 +518,11 @@ static void i82875p_remove_one(struct pci_dev *pdev) static const struct pci_device_id i82875p_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, 82875_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - I82875P}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, 82875_0), + .driver_data = I82875P, + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, i82875p_pci_tbl); diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index d99f005832cf..09a79eaaa486 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -624,12 +624,11 @@ static void i82975x_remove_one(struct pci_dev *pdev) static const struct pci_device_id i82975x_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, 82975_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - I82975X - }, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, 82975_0), + .driver_data = I82975X + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, i82975x_pci_tbl); diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c index a5dc4b88097f..e3bd6436669b 100644 --- a/drivers/edac/ie31200_edac.c +++ b/drivers/edac/ie31200_edac.c @@ -733,49 +733,49 @@ static struct res_config rpl_s_cfg = { }; static const struct pci_device_id ie31200_pci_tbl[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_1), (kernel_ulong_t)&snb_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_2), (kernel_ulong_t)&snb_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_3), (kernel_ulong_t)&snb_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_4), (kernel_ulong_t)&snb_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_5), (kernel_ulong_t)&snb_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_6), (kernel_ulong_t)&snb_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_7), (kernel_ulong_t)&snb_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_8), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_9), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_10), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_11), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_12), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_1), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_2), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_3), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_4), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_5), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_6), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_7), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_8), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_9), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_10), (kernel_ulong_t)&skl_cfg }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_1), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_2), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_3), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_4), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_5), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_6), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_HX_1), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_1), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_2), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_3), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_1), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_2), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_3), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_4), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_5), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_6), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_7), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_8), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_9), (kernel_ulong_t)&rpl_s_cfg}, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_10), (kernel_ulong_t)&rpl_s_cfg}, - { 0, } /* 0 terminated list. */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_1), .driver_data = (kernel_ulong_t)&snb_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_2), .driver_data = (kernel_ulong_t)&snb_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_3), .driver_data = (kernel_ulong_t)&snb_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_4), .driver_data = (kernel_ulong_t)&snb_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_5), .driver_data = (kernel_ulong_t)&snb_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_6), .driver_data = (kernel_ulong_t)&snb_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_7), .driver_data = (kernel_ulong_t)&snb_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_8), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_9), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_10), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_11), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_12), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_1), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_2), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_3), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_4), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_5), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_6), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_7), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_8), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_9), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_10), .driver_data = (kernel_ulong_t)&skl_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_1), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_2), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_3), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_4), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_5), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_6), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_HX_1), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_1), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_2), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_3), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_1), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_2), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_3), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_4), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_5), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_6), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_7), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_8), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_9), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_BTL_S_10), .driver_data = (kernel_ulong_t)&rpl_s_cfg }, + { } /* 0 terminated list. */ }; MODULE_DEVICE_TABLE(pci, ie31200_pci_tbl); diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index fcb8ab44cba5..f1fc20d4ebf6 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -122,6 +122,20 @@ #define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) #define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) +struct igen6_imc { + int mc; + struct mem_ctl_info *mci; + struct pci_dev *pdev; + struct device dev; + void __iomem *window; + u64 size; + u64 ch_s_size; + int ch_l_map; + u64 dimm_s_size[NUM_CHANNELS]; + u64 dimm_l_size[NUM_CHANNELS]; + int dimm_l_map[NUM_CHANNELS]; +}; + static struct res_config { bool machine_check; /* The number of present memory controllers. */ @@ -134,12 +148,32 @@ static struct res_config { u64 reg_touud_mask; /* IBECC error log */ u64 reg_eccerrlog_addr_mask; + /* MEMSS_PMA_CR registers. */ + u32 reg_mem_config_offset; + u32 reg_mem_config_ddr_type_mask; + u32 reg_mem_config_ibecc_en_mask; + u32 reg_capabilities_misc_offset; + u32 reg_capabilities_misc_ibecc_dis; + /* Memory controller registers. */ + u32 reg_mad_inter_size_mask[NUM_CHANNELS]; + u64 reg_mad_inter_size_granularity; + u32 reg_mad_intra_rank_mask[NUM_DIMMS]; + u32 reg_mad_intra_width_mask[NUM_DIMMS]; + u32 reg_mad_intra_density_mask[NUM_DIMMS]; u32 imc_base; u32 cmf_base; u32 cmf_size; u32 ms_hash_offset; u32 ibecc_base; u32 ibecc_error_log_offset; + /* Get memory type. */ + enum mem_type (*get_mem_type)(struct igen6_imc *imc); + /* Get DRAM chip type. */ + enum dev_type (*get_dev_type)(struct igen6_imc *imc, int chan, int dimm_l); + /* Set imc->ch_{s_size,l_map}. */ + void (*set_chan_params)(struct igen6_imc *imc); + /* Set imc->dimm_{l_size,s_size,l_map}[chan]. */ + void (*set_dimm_params)(struct igen6_imc *imc, int chan); bool (*ibecc_available)(struct pci_dev *pdev); /* Extract error address logged in IBECC */ u64 (*err_addr)(u64 ecclog); @@ -149,22 +183,9 @@ static struct res_config { u64 (*err_addr_to_imc_addr)(u64 eaddr, int mc); } *res_cfg; -struct igen6_imc { - int mc; - struct mem_ctl_info *mci; - struct pci_dev *pdev; - struct device dev; - void __iomem *window; - u64 size; - u64 ch_s_size; - int ch_l_map; - u64 dimm_s_size[NUM_CHANNELS]; - u64 dimm_l_size[NUM_CHANNELS]; - int dimm_l_map[NUM_CHANNELS]; -}; - static struct igen6_pvt { struct igen6_imc imc[NUM_IMC]; + void __iomem *memss_pma_cr; u64 ms_hash; u64 ms_s_size; int ms_l_map; @@ -291,10 +312,17 @@ static struct work_struct ecclog_work; #define DID_PTL_H_SKU11 0xb028 #define DID_PTL_H_SKU12 0xb029 #define DID_PTL_H_SKU13 0xb02a +#define DID_PTL_H_SKU14 0xb00a /* Compute die IDs for Wildcat Lake with IBECC */ #define DID_WCL_SKU1 0xfd00 +/* Compute die IDs for Nova Lake-H/HX with IBECC */ +#define DID_NVL_H_SKU1 0xd701 +#define DID_NVL_H_SKU2 0xd702 +#define DID_NVL_H_SKU3 0xd704 +#define DID_NVL_H_SKU4 0xd705 + static int get_mchbar(struct pci_dev *pdev, u64 *mchbar) { union { @@ -386,27 +414,26 @@ static bool mtl_p_ibecc_available(struct pci_dev *pdev) return !(CAPID_E_IBECC_BIT18 & v); } -static bool mtl_ps_ibecc_available(struct pci_dev *pdev) +static bool generic_ibecc_available(struct pci_dev *pdev) { -#define MCHBAR_MEMSS_IBECCDIS 0x13c00 - void __iomem *window; - u64 mchbar; + void __iomem *base = igen6_pvt->memss_pma_cr; + bool present; u32 val; - if (get_mchbar(pdev, &mchbar)) - return false; - - window = ioremap(mchbar, MCHBAR_SIZE * 2); - if (!window) { - igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", mchbar); - return false; + if (res_cfg->reg_capabilities_misc_offset) { + val = readl(base + res_cfg->reg_capabilities_misc_offset); + present = !(val & res_cfg->reg_capabilities_misc_ibecc_dis); + edac_dbg(2, "capabilities misc reg 0x%x\n", val); + } else if (res_cfg->reg_mem_config_offset) { + val = readl(base + res_cfg->reg_mem_config_offset); + present = !!(val & res_cfg->reg_mem_config_ibecc_en_mask); + edac_dbg(2, "mem config reg 0x%x\n", val); + } else { + igen6_printk(KERN_ERR, "No register for detecting IBECC presence.\n"); + present = false; } - val = readl(window + MCHBAR_MEMSS_IBECCDIS); - iounmap(window); - - /* Bit6: 1 - IBECC is disabled, 0 - IBECC isn't disabled */ - return !GET_BITFIELD(val, 6, 6); + return present; } static u64 mem_addr_to_sys_addr(u64 maddr) @@ -500,6 +527,119 @@ static u64 rpl_p_err_addr(u64 ecclog) return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog); } +static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc) +{ + u32 mtype, val; + + val = readl(igen6_pvt->memss_pma_cr + res_cfg->reg_mem_config_offset); + mtype = field_get(res_cfg->reg_mem_config_ddr_type_mask, val); + + edac_dbg(2, "mtype %u (reg 0x%x)\n", mtype, val); + + switch (mtype) { + case 1: + return MEM_DDR5; + case 2: + return MEM_LPDDR5; + case 3: + return MEM_LPDDR4; + default: + return MEM_UNKNOWN; + } +} + +static enum dev_type ptl_h_get_dev_type(struct igen6_imc *imc, int chan, int dimm) +{ + u32 width, val; + + val = readl(imc->window + MAD_INTRA_CH0_OFFSET + chan * 4); + width = field_get(res_cfg->reg_mad_intra_width_mask[dimm], val); + + switch (width) { + case 1: + return DEV_X8; + default: + return DEV_X16; + } +} + +static u64 ptl_h_get_chan_size(struct igen6_imc *imc, int chan) +{ + u32 val = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); + + return field_get(res_cfg->reg_mad_inter_size_mask[chan], val) * + res_cfg->reg_mad_inter_size_granularity; +} + +static u64 ptl_h_get_dimm_size(struct igen6_imc *imc, int chan, int dimm) +{ + u32 val = readl(imc->window + MAD_INTRA_CH0_OFFSET + chan * 4); + u32 ranks = 1 << field_get(res_cfg->reg_mad_intra_rank_mask[dimm], val); + /* DRAM device density in Gb */ + u64 density = field_get(res_cfg->reg_mad_intra_density_mask[dimm], val) * 4; + + enum mem_type mtype = ptl_h_get_mem_type(imc); + enum dev_type dtype = ptl_h_get_dev_type(imc, chan, dimm); + u64 sub_ch_width, dev_num; + + switch (mtype) { + case MEM_DDR5: + sub_ch_width = 32; + break; + case MEM_LPDDR5: + case MEM_LPDDR4: + sub_ch_width = 16; + break; + default: + sub_ch_width = 0; + } + + switch (dtype) { + case DEV_X8: + dev_num = sub_ch_width / 8; + break; + case DEV_X16: + dev_num = sub_ch_width / 16; + break; + default: + dev_num = 0; + } + + edac_dbg(2, "ranks %d, density %lluGb, sub_ch_width %llu, dev_num %llu (reg 0x%x)\n", ranks, density, sub_ch_width, dev_num, val); + + return ((dev_num * density / 8) * ranks) << 30; +} + +static void ptl_h_set_chan_params(struct igen6_imc *imc) +{ + u64 ch0_size = ptl_h_get_chan_size(imc, 0); + u64 ch1_size = ptl_h_get_chan_size(imc, 1); + + if (ch0_size <= ch1_size) { + imc->ch_s_size = ch0_size; + imc->ch_l_map = 1; + } else { + imc->ch_s_size = ch1_size; + imc->ch_l_map = 0; + } +} + +static void ptl_h_set_dimm_params(struct igen6_imc *imc, int chan) +{ + u64 dimm0_size = ptl_h_get_dimm_size(imc, chan, 0); + u64 dimm1_size = ptl_h_get_dimm_size(imc, chan, 1); + + if (dimm0_size <= dimm1_size) { + imc->dimm_s_size[chan] = dimm0_size; + imc->dimm_l_size[chan] = dimm1_size; + imc->dimm_l_map[chan] = 1; + } else { + imc->dimm_s_size[chan] = dimm1_size; + imc->dimm_l_size[chan] = dimm0_size; + imc->dimm_l_map[chan] = 0; + } +} + static struct res_config ehl_cfg = { .num_imc = 1, .reg_mchbar_mask = GENMASK_ULL(38, 16), @@ -593,18 +733,20 @@ static struct res_config rpl_p_cfg = { }; static struct res_config mtl_ps_cfg = { - .machine_check = true, - .num_imc = 2, - .reg_mchbar_mask = GENMASK_ULL(41, 17), - .reg_tom_mask = GENMASK_ULL(41, 20), - .reg_touud_mask = GENMASK_ULL(41, 20), - .reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5), - .imc_base = 0xd800, - .ibecc_base = 0xd400, - .ibecc_error_log_offset = 0x170, - .ibecc_available = mtl_ps_ibecc_available, - .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, - .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, + .machine_check = true, + .num_imc = 2, + .reg_mchbar_mask = GENMASK_ULL(41, 17), + .reg_tom_mask = GENMASK_ULL(41, 20), + .reg_touud_mask = GENMASK_ULL(41, 20), + .reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5), + .reg_capabilities_misc_offset = 0x13c00, + .reg_capabilities_misc_ibecc_dis = BIT(6), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, + .ibecc_available = generic_ibecc_available, + .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, + .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; static struct res_config mtl_p_cfg = { @@ -622,6 +764,36 @@ static struct res_config mtl_p_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; +static struct res_config ptl_h_cfg = { + .machine_check = true, + .num_imc = 2, + .reg_mchbar_mask = GENMASK_ULL(41, 17), + .reg_tom_mask = GENMASK_ULL(41, 20), + .reg_touud_mask = GENMASK_ULL(41, 20), + .reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5), + .reg_mem_config_offset = 0x13d04, + .reg_mem_config_ddr_type_mask = GENMASK(8, 6), + .reg_mad_inter_size_mask[0] = GENMASK(15, 8), + .reg_mad_inter_size_mask[1] = GENMASK(23, 16), + .reg_mad_inter_size_granularity = BIT_ULL(29), + .reg_mad_intra_rank_mask[0] = BIT(7), + .reg_mad_intra_rank_mask[1] = BIT(15), + .reg_mad_intra_width_mask[0] = BIT(6), + .reg_mad_intra_width_mask[1] = BIT(14), + .reg_mad_intra_density_mask[0] = GENMASK(3, 0), + .reg_mad_intra_density_mask[1] = GENMASK(11, 8), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, + .get_mem_type = ptl_h_get_mem_type, + .get_dev_type = ptl_h_get_dev_type, + .set_chan_params = ptl_h_set_chan_params, + .set_dimm_params = ptl_h_set_dimm_params, + .ibecc_available = mtl_p_ibecc_available, + .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, + .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, +}; + static struct res_config wcl_cfg = { .machine_check = true, .num_imc = 1, @@ -637,98 +809,122 @@ static struct res_config wcl_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; +static struct res_config nvl_h_cfg = { + .machine_check = true, + .num_imc = 2, + .reg_mchbar_mask = GENMASK_ULL(41, 17), + .reg_tom_mask = GENMASK_ULL(41, 20), + .reg_touud_mask = GENMASK_ULL(41, 20), + .reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5), + .reg_mem_config_offset = 0x12904, + .reg_mem_config_ddr_type_mask = GENMASK(8, 6), + .reg_mem_config_ibecc_en_mask = GENMASK(3, 2), + .reg_mad_inter_size_mask[0] = GENMASK(15, 8), + .reg_mad_inter_size_mask[1] = GENMASK(23, 16), + .reg_mad_inter_size_granularity = BIT_ULL(29), + .reg_mad_intra_rank_mask[0] = BIT(7), + .reg_mad_intra_rank_mask[1] = BIT(15), + .reg_mad_intra_width_mask[0] = BIT(6), + .reg_mad_intra_width_mask[1] = BIT(14), + .reg_mad_intra_density_mask[0] = GENMASK(3, 0), + .reg_mad_intra_density_mask[1] = GENMASK(11, 8), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, + .get_mem_type = ptl_h_get_mem_type, + .get_dev_type = ptl_h_get_dev_type, + .set_chan_params = ptl_h_set_chan_params, + .set_dimm_params = ptl_h_set_dimm_params, + .ibecc_available = generic_ibecc_available, + .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, + .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, +}; + static struct pci_device_id igen6_pci_tbl[] = { - { PCI_VDEVICE(INTEL, DID_EHL_SKU5), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU6), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU7), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU8), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU9), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU10), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU11), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU12), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU13), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU14), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_EHL_SKU15), (kernel_ulong_t)&ehl_cfg }, - { PCI_VDEVICE(INTEL, DID_ICL_SKU8), (kernel_ulong_t)&icl_cfg }, - { PCI_VDEVICE(INTEL, DID_ICL_SKU10), (kernel_ulong_t)&icl_cfg }, - { PCI_VDEVICE(INTEL, DID_ICL_SKU11), (kernel_ulong_t)&icl_cfg }, - { PCI_VDEVICE(INTEL, DID_ICL_SKU12), (kernel_ulong_t)&icl_cfg }, - { PCI_VDEVICE(INTEL, DID_TGL_SKU), (kernel_ulong_t)&tgl_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_SKU1), (kernel_ulong_t)&adl_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_SKU2), (kernel_ulong_t)&adl_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_SKU3), (kernel_ulong_t)&adl_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_SKU4), (kernel_ulong_t)&adl_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_AZB_SKU1), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ASL_SKU1), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ASL_SKU2), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ASL_SKU3), (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), (kernel_ulong_t)&mtl_ps_cfg }, - { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), (kernel_ulong_t)&mtl_ps_cfg }, - { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), (kernel_ulong_t)&mtl_ps_cfg }, - { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU4), (kernel_ulong_t)&mtl_ps_cfg }, - { PCI_VDEVICE(INTEL, DID_MTL_P_SKU1), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_MTL_P_SKU2), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_MTL_P_SKU3), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_WCL_SKU1), (kernel_ulong_t)&wcl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU5), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU6), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU7), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU8), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU9), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU10), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU11), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU12), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU13), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU14), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU15), .driver_data = (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_ICL_SKU8), .driver_data = (kernel_ulong_t)&icl_cfg }, + { PCI_VDEVICE(INTEL, DID_ICL_SKU10), .driver_data = (kernel_ulong_t)&icl_cfg }, + { PCI_VDEVICE(INTEL, DID_ICL_SKU11), .driver_data = (kernel_ulong_t)&icl_cfg }, + { PCI_VDEVICE(INTEL, DID_ICL_SKU12), .driver_data = (kernel_ulong_t)&icl_cfg }, + { PCI_VDEVICE(INTEL, DID_TGL_SKU), .driver_data = (kernel_ulong_t)&tgl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), .driver_data = (kernel_ulong_t)&mtl_ps_cfg }, + { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), .driver_data = (kernel_ulong_t)&mtl_ps_cfg }, + { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), .driver_data = (kernel_ulong_t)&mtl_ps_cfg }, + { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU4), .driver_data = (kernel_ulong_t)&mtl_ps_cfg }, + { PCI_VDEVICE(INTEL, DID_MTL_P_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_MTL_P_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_MTL_P_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU14), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&wcl_cfg }, + { PCI_VDEVICE(INTEL, DID_NVL_H_SKU1), .driver_data = (kernel_ulong_t)&nvl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_NVL_H_SKU2), .driver_data = (kernel_ulong_t)&nvl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_NVL_H_SKU3), .driver_data = (kernel_ulong_t)&nvl_h_cfg }, + { PCI_VDEVICE(INTEL, DID_NVL_H_SKU4), .driver_data = (kernel_ulong_t)&nvl_h_cfg }, { }, }; MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); -static enum dev_type get_width(int dimm_l, u32 mad_dimm) +static enum mem_type get_mem_type(struct igen6_imc *imc) { - u32 w = dimm_l ? MAD_DIMM_CH_DLW(mad_dimm) : - MAD_DIMM_CH_DSW(mad_dimm); + u32 val; - switch (w) { - case 0: - return DEV_X8; - case 1: - return DEV_X16; - case 2: - return DEV_X32; - default: - return DEV_UNKNOWN; - } -} + if (res_cfg->get_mem_type) + return res_cfg->get_mem_type(imc); -static enum mem_type get_memory_type(u32 mad_inter) -{ - u32 t = MAD_INTER_CHANNEL_DDR_TYPE(mad_inter); + val = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); - switch (t) { + switch (MAD_INTER_CHANNEL_DDR_TYPE(val)) { case 0: return MEM_DDR4; case 1: @@ -744,6 +940,73 @@ static enum mem_type get_memory_type(u32 mad_inter) } } +static bool large_dimm(struct igen6_imc *imc, int chan, int dimm) +{ + return dimm == imc->dimm_l_map[chan]; +} + +static enum dev_type get_dev_type(struct igen6_imc *imc, int chan, int dimm) +{ + u32 width, val; + + if (res_cfg->get_dev_type) + return res_cfg->get_dev_type(imc, chan, dimm); + + val = readl(imc->window + MAD_DIMM_CH0_OFFSET + chan * 4); + width = large_dimm(imc, chan, dimm) ? MAD_DIMM_CH_DLW(val) : + MAD_DIMM_CH_DSW(val); + + switch (width) { + case 0: + return DEV_X8; + case 1: + return DEV_X16; + case 2: + return DEV_X32; + default: + return DEV_UNKNOWN; + } +} + +static u64 get_dimm_size(struct igen6_imc *imc, int chan, int dimm) +{ + if (large_dimm(imc, chan, dimm)) + return imc->dimm_l_size[chan]; + + return imc->dimm_s_size[chan]; +} + +static void set_chan_params(struct igen6_imc *imc) +{ + u32 val; + + if (res_cfg->set_chan_params) { + res_cfg->set_chan_params(imc); + return; + } + + val = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); + imc->ch_s_size = MAD_INTER_CHANNEL_CH_S_SIZE(val); + imc->ch_l_map = MAD_INTER_CHANNEL_CH_L_MAP(val); +} + +static void set_dimm_params(struct igen6_imc *imc, int chan) +{ + u32 val; + + if (res_cfg->set_dimm_params) { + res_cfg->set_dimm_params(imc, chan); + return; + } + + val = readl(imc->window + MAD_INTRA_CH0_OFFSET + chan * 4); + imc->dimm_l_map[chan] = MAD_INTRA_CH_DIMM_L_MAP(val); + + val = readl(imc->window + MAD_DIMM_CH0_OFFSET + chan * 4); + imc->dimm_l_size[chan] = MAD_DIMM_CH_DIMM_L_SIZE(val); + imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val); +} + static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit) { u64 hash_addr = addr & mask, hash = 0; @@ -1084,7 +1347,6 @@ static bool igen6_check_ecc(struct igen6_imc *imc) static int igen6_get_dimm_config(struct mem_ctl_info *mci) { struct igen6_imc *imc = mci->pvt_info; - u32 mad_inter, mad_intra, mad_dimm; int i, j, ndimms, mc = imc->mc; struct dimm_info *dimm; enum mem_type mtype; @@ -1094,33 +1356,20 @@ static int igen6_get_dimm_config(struct mem_ctl_info *mci) edac_dbg(2, "\n"); - mad_inter = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); - mtype = get_memory_type(mad_inter); + mtype = get_mem_type(imc); ecc = igen6_check_ecc(imc); - imc->ch_s_size = MAD_INTER_CHANNEL_CH_S_SIZE(mad_inter); - imc->ch_l_map = MAD_INTER_CHANNEL_CH_L_MAP(mad_inter); + set_chan_params(imc); for (i = 0; i < NUM_CHANNELS; i++) { - mad_intra = readl(imc->window + MAD_INTRA_CH0_OFFSET + i * 4); - mad_dimm = readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4); - - imc->dimm_l_size[i] = MAD_DIMM_CH_DIMM_L_SIZE(mad_dimm); - imc->dimm_s_size[i] = MAD_DIMM_CH_DIMM_S_SIZE(mad_dimm); - imc->dimm_l_map[i] = MAD_INTRA_CH_DIMM_L_MAP(mad_intra); + set_dimm_params(imc, i); imc->size += imc->dimm_s_size[i]; imc->size += imc->dimm_l_size[i]; ndimms = 0; for (j = 0; j < NUM_DIMMS; j++) { dimm = edac_get_dimm(mci, i, j, 0); - - if (j ^ imc->dimm_l_map[i]) { - dtype = get_width(0, mad_dimm); - dsize = imc->dimm_s_size[i]; - } else { - dtype = get_width(1, mad_dimm); - dsize = imc->dimm_l_size[i]; - } + dtype = get_dev_type(imc, i, j); + dsize = get_dimm_size(imc, i, j); if (!dsize) continue; @@ -1223,6 +1472,39 @@ static void igen6_debug_setup(void) {} static void igen6_debug_teardown(void) {} #endif +static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev) +{ + void __iomem *memss_pma_cr; + struct igen6_pvt *pvt; + u64 mchbar; + int rc; + + pvt = kzalloc_obj(*igen6_pvt); + if (!pvt) + return NULL; + + rc = get_mchbar(pdev, &mchbar); + if (rc) { + kfree(pvt); + return NULL; + } + + memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2); + if (!memss_pma_cr) { + kfree(pvt); + return NULL; + } + pvt->memss_pma_cr = memss_pma_cr; + + return pvt; +} + +static void igen6_pvt_release(struct igen6_pvt *pvt) +{ + iounmap(pvt->memss_pma_cr); + kfree(pvt); +} + static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) { union { @@ -1296,6 +1578,11 @@ static bool igen6_imc_absent(void __iomem *window) return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0; } +static void imc_release(struct device *dev) +{ + /* Nothing to do, the 'imc' owns the 'dev' and will also release it. */ +} + static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev) { struct edac_mc_layer layers[2]; @@ -1334,6 +1621,7 @@ static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev mci->pvt_info = &igen6_pvt->imc[mc]; imc = mci->pvt_info; + imc->dev.release = imc_release; device_initialize(&imc->dev); /* * EDAC core uses mci->pdev(pointer of structure device) as @@ -1549,12 +1837,12 @@ static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent) edac_dbg(2, "\n"); - igen6_pvt = kzalloc_obj(*igen6_pvt); + res_cfg = (struct res_config *)ent->driver_data; + + igen6_pvt = igen6_pvt_setup(pdev); if (!igen6_pvt) return -ENOMEM; - res_cfg = (struct res_config *)ent->driver_data; - rc = igen6_pci_setup(pdev, &mchbar); if (rc) goto fail; @@ -1603,7 +1891,7 @@ fail3: fail2: igen6_unregister_mcis(); fail: - kfree(igen6_pvt); + igen6_pvt_release(igen6_pvt); return rc; } @@ -1618,7 +1906,7 @@ static void igen6_remove(struct pci_dev *pdev) flush_work(&ecclog_work); gen_pool_destroy(ecclog_pool); igen6_unregister_mcis(); - kfree(igen6_pvt); + igen6_pvt_release(igen6_pvt); } static struct pci_driver igen6_driver = { diff --git a/drivers/edac/imh_base.c b/drivers/edac/imh_base.c index 40082ba45e62..6ca0df031bf5 100644 --- a/drivers/edac/imh_base.c +++ b/drivers/edac/imh_base.c @@ -71,28 +71,44 @@ struct local_reg { .width = (cfg)->ip_name##_reg_##reg_name##_width, \ } -static u64 readx(void __iomem *addr, u8 width) -{ - switch (width) { - case 1: - return readb(addr); - case 2: - return readw(addr); - case 4: - return readl(addr); - case 8: - return readq(addr); - default: - imh_printk(KERN_ERR, "Invalid reg 0x%p width %d\n", addr, width); - return 0; - } +static struct res_config *res_cfg; +static int retry_rd_err_log; + +#define REG_RRL_DEFINE(a0, a1, a2, a3, a4, a5, a6, b0, b1, b2, b3) \ + { \ + .set_num = 4, \ + .reg_num = 7, \ + .sources = {RRL_SRC_FRE_SCRUB, RRL_SRC_FRE_DEMAND, RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND}, \ + .offsets = { \ + {a0, a1, a2, a3, a4, a5, a6}, \ + {a0 + 4, a1 + 4, a2 + 8, a3 + 4, a4 + 4, a5 + 8, a6 + 8}, \ + {a0 + 8, a1 + 8, a2 + 16, a3 + 8, a4 + 8, a5 + 16, a6 + 16}, \ + {a0 + 12, a1 + 12, a2 + 24, a3 + 12, a4 + 12, a5 + 24, a6 + 24}, \ + }, \ + .widths = {4, 4, 8, 4, 4, 8, 8}, \ + .v_mask = BIT(0), \ + .uc_mask = BIT(1), \ + .over_mask = BIT(2), \ + .en_mask = BIT(12), \ + .en_patspr_mask = BIT(14), \ + .noover_mask = BIT(15), \ + .cecnt_num = 4, \ + .cecnt_offsets = {b0, b1, b2, b3}, \ + .cecnt_widths = {8, 8, 8, 8}, \ } +static struct reg_rrl dmr_reg_rrl_ddr_subch0 = REG_RRL_DEFINE( + 0x2dc0, 0x2dd0, 0x2de0, 0x2e00, 0x2e10, 0x2f70, 0x0200, + 0x2c10, 0x2c18, 0x2c20, 0x2c28); +static struct reg_rrl dmr_reg_rrl_ddr_subch1 = REG_RRL_DEFINE( + 0x6dc0, 0x6dd0, 0x6de0, 0x6e00, 0x6e10, 0x6f70, 0x4200, + 0x6c10, 0x6c18, 0x6c20, 0x6c28); + static void __read_local_reg(void *reg) { struct local_reg *r = (struct local_reg *)reg; - r->val = readx(r->vbase + r->offset, r->width); + r->val = skx_readx(r->vbase + r->offset, r->width); } /* Read a local-view register. */ @@ -378,22 +394,16 @@ static bool imh_2lm_enabled(struct res_config *cfg, struct list_head *head) return false; } -/* Helpers to read memory controller registers */ -static u64 read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width) -{ - return readx(imc->mbase + imc->chan_mmio_sz * chan + offset, width); -} - static u32 read_imc_mcmtr(struct res_config *cfg, struct skx_imc *imc, int chan) { - return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_mcmtr_offset, cfg->ddr_reg_mcmtr_width); + return (u32)skx_read_imc_reg(imc, chan, cfg->ddr_reg_mcmtr_offset, cfg->ddr_reg_mcmtr_width); } static u32 read_imc_dimmmtr(struct res_config *cfg, struct skx_imc *imc, int chan, int dimm) { - return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_dimmmtr_offset + - cfg->ddr_reg_dimmmtr_width * dimm, - cfg->ddr_reg_dimmmtr_width); + return (u32)skx_read_imc_reg(imc, chan, cfg->ddr_reg_dimmmtr_offset + + cfg->ddr_reg_dimmmtr_width * dimm, + cfg->ddr_reg_dimmmtr_width); } static bool ecc_enabled(u32 mcmtr) @@ -503,6 +513,8 @@ static struct res_config dmr_cfg = { .ha_size = 0x1000, .ha_reg_mode_offset = 0x4a0, .ha_reg_mode_width = 4, + .reg_rrl_ddr[0] = &dmr_reg_rrl_ddr_subch0, + .reg_rrl_ddr[1] = &dmr_reg_rrl_ddr_subch1, }; static const struct x86_cpu_id imh_cpuids[] = { @@ -542,6 +554,7 @@ static int __init imh_init(void) return -ENODEV; cfg = (struct res_config *)id->driver_data; skx_set_res_cfg(cfg); + res_cfg = cfg; if (!imh_get_tolm_tohm(cfg, &tolm, &tohm)) return -ENODEV; @@ -576,6 +589,13 @@ static int __init imh_init(void) mce_register_decode_chain(&imh_mce_dec); skx_setup_debug("imh_test"); + cfg->rrl_ctrl_mode = retry_rd_err_log; + if (retry_rd_err_log && cfg->reg_rrl_ddr[0]) { + skx_set_show_rrl(skx_show_rrl); + if (retry_rd_err_log == RRL_CTRL_LINUX) + skx_enable_rrl(true); + } + imh_printk(KERN_INFO, "%s\n", IMH_REVISION); return 0; @@ -588,6 +608,12 @@ static void __exit imh_exit(void) { edac_dbg(2, "\n"); + if (retry_rd_err_log && res_cfg->reg_rrl_ddr[0]) { + if (retry_rd_err_log == RRL_CTRL_LINUX) + skx_enable_rrl(false); + skx_set_show_rrl(NULL); + } + skx_teardown_debug(); mce_unregister_decode_chain(&imh_mce_dec); skx_adxl_put(); @@ -597,6 +623,9 @@ static void __exit imh_exit(void) module_init(imh_init); module_exit(imh_exit); +module_param(retry_rd_err_log, int, 0444); +MODULE_PARM_DESC(retry_rd_err_log, "retry_rd_err_log: 0=off(default), 1=bios(Linux doesn't reset any control bits, but just reports values.), 2=linux(Linux tries to take control and resets mode bits, clear valid/UC bits after reading.)"); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Qiuxu Zhuo"); MODULE_DESCRIPTION("MC Driver for Intel servers using IMH-based memory controller"); diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 7b282dfd093f..35eb7a2038ab 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -2016,7 +2016,7 @@ static bool sb_decode_ddr4(struct mem_ctl_info *mci, int ch, u8 rank, static bool sb_decode_ddr3(struct mem_ctl_info *mci, int ch, u8 rank, u64 rank_addr, char *msg) { - pr_warn_once("DDR3 row/column decode not support yet!\n"); + pr_warn_once("DDR3 row/column decode is not supported yet!\n"); msg[0] = '\0'; return false; } diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c index aa6593ccda2d..de749413ff9a 100644 --- a/drivers/edac/skx_base.c +++ b/drivers/edac/skx_base.c @@ -671,14 +671,14 @@ static int __init skx_init(void) } } - skx_set_decode(skx_decode, skx_show_retry_rd_err_log); + skx_set_show_rrl(skx_show_retry_rd_err_log); if (nvdimm_count && skx_adxl_get() != -ENODEV) { - skx_set_decode(NULL, skx_show_retry_rd_err_log); + skx_set_decode(NULL); } else { if (nvdimm_count) skx_printk(KERN_NOTICE, "Only decoding DDR4 address!\n"); - skx_set_decode(skx_decode, skx_show_retry_rd_err_log); + skx_set_decode(skx_decode); } /* Ensure that the OPSTATE is set correctly for POLL or NMI */ diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c index a9557c8344bc..bfd0cd1689ed 100644 --- a/drivers/edac/skx_common.c +++ b/drivers/edac/skx_common.c @@ -31,10 +31,12 @@ static const char * const component_names[] = { [INDEX_CHANNEL] = "ChannelId", [INDEX_DIMM] = "DimmSlotId", [INDEX_CS] = "ChipSelect", + [INDEX_SUBCH] = "SubChId", [INDEX_NM_MEMCTRL] = "NmMemoryControllerId", [INDEX_NM_CHANNEL] = "NmChannelId", [INDEX_NM_DIMM] = "NmDimmSlotId", [INDEX_NM_CS] = "NmChipSelect", + [INDEX_NM_SUBCH] = "NmSubChId", }; static int component_indices[ARRAY_SIZE(component_names)]; @@ -43,15 +45,284 @@ static const char * const *adxl_component_names; static u64 *adxl_values; static char *adxl_msg; static unsigned long adxl_nm_bitmap; +static unsigned long adxl_bitmap; static char skx_msg[MSG_SIZE]; static skx_decode_f driver_decode; -static skx_show_retry_log_f skx_show_retry_rd_err_log; +static skx_show_rrl_f show_rrl; static u64 skx_tolm, skx_tohm; static LIST_HEAD(dev_edac_list); static bool skx_mem_cfg_2lm; static struct res_config *skx_res_cfg; +u64 skx_readx(void __iomem *addr, u8 width) +{ + switch (width) { + case 1: + return readb(addr); + case 2: + return readw(addr); + case 4: + return readl(addr); + case 8: + return readq(addr); + default: + skx_printk(KERN_ERR, "Invalid reg 0x%p width %u to read.\n", addr, width); + return 0; + } +} +EXPORT_SYMBOL_GPL(skx_readx); + +static void skx_writex(void __iomem *addr, u8 width, u64 val) +{ + switch (width) { + case 1: + writeb((u8)val, addr); + return; + case 2: + writew((u16)val, addr); + return; + case 4: + writel((u32)val, addr); + return; + case 8: + writeq(val, addr); + return; + default: + skx_printk(KERN_ERR, "Invalid reg 0x%p width %u to write 0x%llx.\n", addr, width, val); + } +} + +u64 skx_read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width) +{ + return skx_readx(imc->mbase + imc->chan_mmio_sz * chan + offset, width); +} +EXPORT_SYMBOL_GPL(skx_read_imc_reg); + +void skx_write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 val) +{ + skx_writex(imc->mbase + imc->chan_mmio_sz * chan + offset, width, val); +} +EXPORT_SYMBOL_GPL(skx_write_imc_reg); + +static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl, + int rrl_set, bool enable, u32 *rrl_ctl) +{ + enum rrl_source_type source = rrl->sources[rrl_set]; + u32 offset = rrl->offsets[rrl_set][0], v; + u8 width = rrl->widths[0]; + bool first, scrub; + + /* First or last read error. */ + first = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_FRE_DEMAND); + /* Patrol scrub or on-demand read error. */ + scrub = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_LRE_SCRUB); + + v = skx_read_imc_reg(imc, chan, offset, width); + + if (enable) { + /* Save default configurations. */ + *rrl_ctl = v; + v &= ~rrl->uc_mask; + + if (first) + v |= rrl->noover_mask; + else + v &= ~rrl->noover_mask; + + if (scrub) + v |= rrl->en_patspr_mask; + else + v &= ~rrl->en_patspr_mask; + + v |= rrl->en_mask; + } else { + /* Restore default configurations. */ + if (*rrl_ctl & rrl->uc_mask) + v |= rrl->uc_mask; + + if (first) { + if (!(*rrl_ctl & rrl->noover_mask)) + v &= ~rrl->noover_mask; + } else { + if (*rrl_ctl & rrl->noover_mask) + v |= rrl->noover_mask; + } + + if (scrub) { + if (!(*rrl_ctl & rrl->en_patspr_mask)) + v &= ~rrl->en_patspr_mask; + } else { + if (*rrl_ctl & rrl->en_patspr_mask) + v |= rrl->en_patspr_mask; + } + + if (!(*rrl_ctl & rrl->en_mask)) + v &= ~rrl->en_mask; + } + + skx_write_imc_reg(imc, chan, offset, width, v); +} + +static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl, + bool enable, u32 *rrl_ctl) +{ + for (int i = 0; i < rrl->set_num; i++) + enable_rrl(imc, chan, rrl, i, enable, rrl_ctl + i); +} + +static void enable_rrls_ddr(struct skx_imc *imc, bool enable) +{ + struct reg_rrl **rrl_ddr = skx_res_cfg->reg_rrl_ddr; + int i, chan_num = skx_res_cfg->ddr_chan_num; + struct skx_channel *chan = imc->chan; + + if (!imc->mbase) + return; + + for (i = 0; i < chan_num; i++) { + enable_rrls(imc, i, rrl_ddr[0], enable, chan[i].rrl_ctl[0]); + if (rrl_ddr[1]) + enable_rrls(imc, i, rrl_ddr[1], enable, chan[i].rrl_ctl[1]); + } +} + +static void enable_rrls_hbm(struct skx_imc *imc, bool enable) +{ + struct reg_rrl **rrl_hbm = skx_res_cfg->reg_rrl_hbm; + int i, chan_num = skx_res_cfg->hbm_chan_num; + struct skx_channel *chan = imc->chan; + + if (!imc->mbase || !imc->hbm_mc || !rrl_hbm[0] || !rrl_hbm[1]) + return; + + for (i = 0; i < chan_num; i++) { + enable_rrls(imc, i, rrl_hbm[0], enable, chan[i].rrl_ctl[0]); + enable_rrls(imc, i, rrl_hbm[1], enable, chan[i].rrl_ctl[1]); + } +} + +void skx_enable_rrl(bool enable) +{ + struct skx_dev *d; + int i, imc_num; + + edac_dbg(2, "\n"); + + list_for_each_entry(d, &dev_edac_list, list) { + imc_num = skx_res_cfg->ddr_imc_num; + for (i = 0; i < imc_num; i++) + enable_rrls_ddr(&d->imc[i], enable); + + imc_num += skx_res_cfg->hbm_imc_num; + for (; i < imc_num; i++) + enable_rrls_hbm(&d->imc[i], enable); + } +} +EXPORT_SYMBOL_GPL(skx_enable_rrl); + +static struct reg_rrl *get_rrl_reg(struct decoded_addr *res, struct res_config *cfg) +{ + struct skx_imc *imc = &res->dev->imc[res->imc]; + + /* HBM has two groups of RRL sets, one per pseudo-channel. */ + if (imc->hbm_mc) + return cfg->reg_rrl_hbm[res->cs & 1]; + + /* One group of RRL sets per DDR channel. */ + if (!cfg->reg_rrl_ddr[1]) + return cfg->reg_rrl_ddr[0]; + + if (res->subch == -1) { + skx_printk(KERN_ERR, "Invalid sub-channel id (-1), possibly missing %s ADXL component.\n", component_names[INDEX_SUBCH]); + return NULL; + } + + /* Two groups of RRL sets per DDR channel (e.g., DMR: one group per sub-channel). */ + return cfg->reg_rrl_ddr[res->subch & 1]; +} + +void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err) +{ + struct skx_imc *imc = &res->dev->imc[res->imc]; + int i, j, n, ch = res->channel; + u64 log, corr, status_mask; + struct reg_rrl *rrl; + bool scrub; + u32 offset; + u8 width; + + if (!imc->mbase) + return; + + rrl = get_rrl_reg(res, skx_res_cfg); + if (!rrl) + return; + + status_mask = rrl->over_mask | rrl->uc_mask | rrl->v_mask; + + n = scnprintf(msg, len, " retry_rd_err_log["); + for (i = 0; i < rrl->set_num; i++) { + scrub = (rrl->sources[i] == RRL_SRC_FRE_SCRUB || rrl->sources[i] == RRL_SRC_LRE_SCRUB); + if (scrub_err != scrub) + continue; + + for (j = 0; j < rrl->reg_num && len - n > 0; j++) { + offset = rrl->offsets[i][j]; + width = rrl->widths[j]; + log = skx_read_imc_reg(imc, ch, offset, width); + + if (width == 4) + n += scnprintf(msg + n, len - n, "%.8llx ", log); + else + n += scnprintf(msg + n, len - n, "%.16llx ", log); + + /* Clear RRL status if RRL in Linux control mode. */ + if (skx_res_cfg->rrl_ctrl_mode == RRL_CTRL_LINUX && !j && (log & status_mask)) + skx_write_imc_reg(imc, ch, offset, width, log & ~status_mask); + } + } + + /* Move back one space. */ + n--; + n += scnprintf(msg + n, len - n, "]"); + + if (len - n > 0) { + n += scnprintf(msg + n, len - n, " correrrcnt["); + for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) { + offset = rrl->cecnt_offsets[i]; + width = rrl->cecnt_widths[i]; + corr = skx_read_imc_reg(imc, ch, offset, width); + + /* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */ + if (skx_res_cfg->type <= SPR) { + n += scnprintf(msg + n, len - n, "%.4llx %.4llx ", + corr & 0xffff, corr >> 16); + } else { + /* CPUs {GNR} encode one counter per CORRERRCNT register. */ + if (width == 4) + n += scnprintf(msg + n, len - n, "%.8llx ", corr); + else + n += scnprintf(msg + n, len - n, "%.16llx ", corr); + } + } + + /* Move back one space. */ + n--; + n += scnprintf(msg + n, len - n, "]"); + } +} +EXPORT_SYMBOL_GPL(skx_show_rrl); + +static bool adxl_component_required(int idx) +{ + return idx == INDEX_SOCKET || + idx == INDEX_MEMCTRL || + idx == INDEX_CHANNEL || + idx == INDEX_DIMM || + idx == INDEX_CS; +} + int skx_adxl_get(void) { const char * const *names; @@ -70,12 +341,14 @@ int skx_adxl_get(void) if (i >= INDEX_NM_FIRST) adxl_nm_bitmap |= 1 << i; + else + adxl_bitmap |= 1 << i; break; } } - if (!names[j] && i < INDEX_NM_FIRST) + if (!names[j] && adxl_component_required(i)) goto err; } @@ -202,11 +475,15 @@ static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) (int)adxl_values[component_indices[INDEX_NM_DIMM]] : -1; res->cs = (adxl_nm_bitmap & BIT_NM_CS) ? (int)adxl_values[component_indices[INDEX_NM_CS]] : -1; + res->subch = (adxl_nm_bitmap & BIT_NM_SUBCH) ? + (int)adxl_values[component_indices[INDEX_NM_SUBCH]] : -1; } else { res->imc = (int)adxl_values[component_indices[INDEX_MEMCTRL]]; res->channel = (int)adxl_values[component_indices[INDEX_CHANNEL]]; res->dimm = (int)adxl_values[component_indices[INDEX_DIMM]]; res->cs = (int)adxl_values[component_indices[INDEX_CS]]; + res->subch = (adxl_bitmap & BIT_SUBCH) ? + (int)adxl_values[component_indices[INDEX_SUBCH]] : -1; } if (res->imc < 0) { @@ -262,13 +539,18 @@ void skx_set_res_cfg(struct res_config *cfg) } EXPORT_SYMBOL_GPL(skx_set_res_cfg); -void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log) +void skx_set_decode(skx_decode_f decode) { driver_decode = decode; - skx_show_retry_rd_err_log = show_retry_log; } EXPORT_SYMBOL_GPL(skx_set_decode); +void skx_set_show_rrl(skx_show_rrl_f rrl) +{ + show_rrl = rrl; +} +EXPORT_SYMBOL_GPL(skx_set_show_rrl); + static int skx_get_pkg_id(struct skx_dev *d, u8 *id) { int node; @@ -466,6 +748,9 @@ int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm, rows = numrow(mtr); cols = imc->hbm_mc ? 6 : numcol(mtr); + if (ranks < 0 || rows < 0 || cols < 0) + return 0; + if (imc->hbm_mc) { banks = 32; mtype = MEM_HBM2; @@ -714,8 +999,8 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, res->row, res->column, res->bank_address, res->bank_group); } - if (skx_show_retry_rd_err_log) - skx_show_retry_rd_err_log(res, skx_msg + len, MSG_SIZE - len, scrub_err); + if (show_rrl) + show_rrl(res, skx_msg + len, MSG_SIZE - len, scrub_err); edac_dbg(0, "%s\n", skx_msg); diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h index f88038e5b18c..777252cca809 100644 --- a/drivers/edac/skx_common.h +++ b/drivers/edac/skx_common.h @@ -77,27 +77,36 @@ /* Max RRL register sets per {,sub-,pseudo-}channel. */ #define NUM_RRL_SET 4 /* Max RRL registers per set. */ -#define NUM_RRL_REG 6 +#define NUM_RRL_REG 7 /* Max correctable error count registers. */ #define NUM_CECNT_REG 8 -/* Modes of RRL register set. */ -enum rrl_mode { +/* Error source from which the RRL registers log errors. */ +enum rrl_source_type { /* Last read error from patrol scrub. */ - LRE_SCRUB, + RRL_SRC_LRE_SCRUB, /* Last read error from demand. */ - LRE_DEMAND, + RRL_SRC_LRE_DEMAND, /* First read error from patrol scrub. */ - FRE_SCRUB, + RRL_SRC_FRE_SCRUB, /* First read error from demand. */ - FRE_DEMAND, + RRL_SRC_FRE_DEMAND, +}; + +enum rrl_ctrl_mode { + /* Linux does not control RRL or reports values. */ + RRL_CTRL_NONE, + /* Firmware retains control. Linux only reports values. */ + RRL_CTRL_BIOS, + /* Linux takes control, resets mode bits, and clears valid/UC bits; reports values. */ + RRL_CTRL_LINUX, }; /* RRL registers per {,sub-,pseudo-}channel. */ struct reg_rrl { /* RRL register parts. */ int set_num, reg_num; - enum rrl_mode modes[NUM_RRL_SET]; + enum rrl_source_type sources[NUM_RRL_SET]; u32 offsets[NUM_RRL_SET][NUM_RRL_REG]; /* RRL register widths in byte per set. */ u8 widths[NUM_RRL_REG]; @@ -201,11 +210,13 @@ enum { INDEX_CHANNEL, INDEX_DIMM, INDEX_CS, + INDEX_SUBCH, INDEX_NM_FIRST, INDEX_NM_MEMCTRL = INDEX_NM_FIRST, INDEX_NM_CHANNEL, INDEX_NM_DIMM, INDEX_NM_CS, + INDEX_NM_SUBCH, INDEX_MAX }; @@ -216,10 +227,12 @@ enum error_source { ERR_SRC_NOT_MEMORY, }; +#define BIT_SUBCH BIT_ULL(INDEX_SUBCH) #define BIT_NM_MEMCTRL BIT_ULL(INDEX_NM_MEMCTRL) #define BIT_NM_CHANNEL BIT_ULL(INDEX_NM_CHANNEL) #define BIT_NM_DIMM BIT_ULL(INDEX_NM_DIMM) #define BIT_NM_CS BIT_ULL(INDEX_NM_CS) +#define BIT_NM_SUBCH BIT_ULL(INDEX_NM_SUBCH) struct decoded_addr { struct mce *mce; @@ -233,6 +246,7 @@ struct decoded_addr { int chanways; int dimm; int cs; + int subch; int rank; int channel_rank; u64 rank_address; @@ -269,9 +283,11 @@ struct res_config { int hbm_chan_mmio_sz; bool support_ddr5; /* RRL register sets per DDR channel */ - struct reg_rrl *reg_rrl_ddr; + struct reg_rrl *reg_rrl_ddr[2]; /* RRL register sets per HBM channel */ struct reg_rrl *reg_rrl_hbm[2]; + /* RRL control mode */ + enum rrl_ctrl_mode rrl_ctrl_mode; union { /* {skx,i10nm}_edac */ struct { @@ -324,11 +340,17 @@ struct res_config { typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci, struct res_config *cfg); typedef bool (*skx_decode_f)(struct decoded_addr *res); -typedef void (*skx_show_retry_log_f)(struct decoded_addr *res, char *msg, int len, bool scrub_err); +typedef void (*skx_show_rrl_f)(struct decoded_addr *res, char *msg, int len, bool scrub_err); +u64 skx_readx(void __iomem *addr, u8 width); +u64 skx_read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width); +void skx_write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 val); int skx_adxl_get(void); void skx_adxl_put(void); -void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log); +void skx_set_decode(skx_decode_f decode); +void skx_set_show_rrl(skx_show_rrl_f rrl); +void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err); +void skx_enable_rrl(bool enable); void skx_set_mem_cfg(bool mem_cfg_2lm); void skx_set_res_cfg(struct res_config *cfg); void skx_init_mc_mapping(struct skx_dev *d); diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index 292dda754c23..2b55daca33b0 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -446,11 +446,11 @@ static void x38_remove_one(struct pci_dev *pdev) static const struct pci_device_id x38_pci_tbl[] = { { - PCI_VEND_DEV(INTEL, X38_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, - X38}, - { - 0, - } /* 0 terminated list. */ + PCI_VEND_DEV(INTEL, X38_HB), + .driver_data = X38, + }, { + /* 0 terminated list. */ + } }; MODULE_DEVICE_TABLE(pci, x38_pci_tbl); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 318d1cc9a066..0327a39d31fa 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -73,7 +73,6 @@ struct mm_struct efi_mm = { MMAP_LOCK_INITIALIZER(efi_mm) .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), - .user_ns = &init_user_ns, #ifdef CONFIG_SCHED_MM_CID .mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(efi_mm.mm_cid.lock), #endif diff --git a/drivers/firmware/efi/libstub/x86-5lvl.c b/drivers/firmware/efi/libstub/x86-5lvl.c index c00d0ae7ed5d..c3da05c0df8b 100644 --- a/drivers/firmware/efi/libstub/x86-5lvl.c +++ b/drivers/firmware/efi/libstub/x86-5lvl.c @@ -2,6 +2,7 @@ #include <linux/efi.h> #include <asm/boot.h> +#include <asm/cpuid/api.h> #include <asm/desc.h> #include <asm/efi.h> diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index c769631ea15d..83f7eedf0b3f 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -112,16 +112,20 @@ void coreboot_driver_unregister(struct coreboot_driver *driver) } EXPORT_SYMBOL(coreboot_driver_unregister); -static int coreboot_table_populate(struct device *dev, void *ptr) +static int coreboot_table_populate(struct device *dev, void *ptr, resource_size_t len) { int i, ret; void *ptr_entry; struct coreboot_device *device; struct coreboot_table_entry *entry; struct coreboot_table_header *header = ptr; + void *ptr_end; + ptr_end = ptr + len; ptr_entry = ptr + header->header_bytes; for (i = 0; i < header->table_entries; i++) { + if (ptr_entry + sizeof(*entry) > ptr_end) + return -EINVAL; entry = ptr_entry; if (entry->size < sizeof(*entry)) { @@ -129,6 +133,9 @@ static int coreboot_table_populate(struct device *dev, void *ptr) return -EINVAL; } + if (ptr_entry + entry->size > ptr_end) + return -EINVAL; + device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL); if (!device) return -ENOMEM; @@ -148,13 +155,13 @@ static int coreboot_table_populate(struct device *dev, void *ptr) break; } + ptr_entry += entry->size; + ret = device_register(&device->dev); if (ret) { + dev_warn(dev, "failed to register coreboot device: %d\n", ret); put_device(&device->dev); - return ret; } - - ptr_entry += entry->size; } return 0; @@ -194,7 +201,7 @@ static int coreboot_table_probe(struct platform_device *pdev) if (!ptr) return -ENOMEM; - ret = coreboot_table_populate(dev, ptr); + ret = coreboot_table_populate(dev, ptr, len); memunmap(ptr); diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 3ab67aaa9e5d..ab9751a59b55 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -41,12 +41,13 @@ static const struct meson_sm_chip gxbb_chip = { .cmd_shmem_in_base = 0x82000020, .cmd_shmem_out_base = 0x82000021, .cmd = { - CMD(SM_EFUSE_READ, 0x82000030), - CMD(SM_EFUSE_WRITE, 0x82000031), + CMD(SM_EFUSE_READ, 0x82000030), + CMD(SM_EFUSE_WRITE, 0x82000031), CMD(SM_EFUSE_USER_MAX, 0x82000033), - CMD(SM_GET_CHIP_ID, 0x82000044), - CMD(SM_A1_PWRC_SET, 0x82000093), - CMD(SM_A1_PWRC_GET, 0x82000095), + CMD(SM_GET_CHIP_ID, 0x82000044), + CMD(SM_THERMAL_CALIB_READ, 0x82000047), + CMD(SM_A1_PWRC_SET, 0x82000093), + CMD(SM_A1_PWRC_GET, 0x82000095), { /* sentinel */ }, }, }; @@ -245,6 +246,23 @@ struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node) } EXPORT_SYMBOL_GPL(meson_sm_get); +/** + * meson_sm_get_thermal_calib - Read thermal sensor calibration data. + * @fw: Pointer to secure-monitor firmware. + * @trim_info: Pointer to store the returned calibration data. + * @tsensor_id: Sensor index to identify which sensor's calibration data + * to retrieve + * + * Return: 0 on success, negative error code on failure. + */ +int meson_sm_get_thermal_calib(struct meson_sm_firmware *fw, u32 *trim_info, + u32 tsensor_id) +{ + return meson_sm_call(fw, SM_THERMAL_CALIB_READ, trim_info, tsensor_id, + 0, 0, 0, 0); +} +EXPORT_SYMBOL_GPL(meson_sm_get_thermal_calib); + #define SM_CHIP_ID_LENGTH 119 #define SM_CHIP_ID_OFFSET 4 #define SM_CHIP_ID_SIZE 12 diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c index 06bdf62dea1f..fdea7aa24ca0 100644 --- a/drivers/firmware/samsung/exynos-acpm-dvfs.c +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c @@ -31,6 +31,9 @@ static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, if (response) { xfer->rxcnt = cmdlen; xfer->rxd = cmd; + } else { + xfer->rxcnt = 0; + xfer->rxd = NULL; } } diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 16c46ed60837..19db3674a28f 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -7,11 +7,12 @@ #include <linux/bitfield.h> #include <linux/bitmap.h> -#include <linux/bits.h> +#include <linux/bitops.h> #include <linux/cleanup.h> #include <linux/container_of.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/find.h> #include <linux/firmware/samsung/exynos-acpm-protocol.h> #include <linux/io.h> #include <linux/iopoll.h> @@ -104,12 +105,15 @@ struct acpm_queue { * * @cmd: pointer to where the data shall be saved. * @n_cmd: number of 32-bit commands. - * @response: true if the client expects the RX data. + * @rxcnt: expected length of the response in 32-bit words. + * @completed: flag indicating if the firmware response has been fully + * processed. */ struct acpm_rx_data { u32 *cmd; size_t n_cmd; - bool response; + size_t rxcnt; + bool completed; }; #define ACPM_SEQNUM_MAX 64 @@ -199,31 +203,33 @@ static void acpm_get_saved_rx(struct acpm_chan *achan, const struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum - 1]; u32 rx_seqnum; - if (!rx_data->response) + if (!rx_data->rxcnt) return; rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]); - if (rx_seqnum == tx_seqnum) { + if (rx_seqnum == tx_seqnum) memcpy(xfer->rxd, rx_data->cmd, xfer->rxcnt * sizeof(*xfer->rxd)); - clear_bit(rx_seqnum - 1, achan->bitmap_seqnum); - } } /** * acpm_get_rx() - get response from RX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer to get response for. + * @native_match: pointer to a boolean set to true if the thread natively + * processed its own sequence number during this call. * * Return: 0 on success, -errno otherwise. */ -static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) +static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer, + bool *native_match) { u32 rx_front, rx_seqnum, tx_seqnum, seqnum; const void __iomem *base, *addr; struct acpm_rx_data *rx_data; u32 i, val, mlen; - bool rx_set = false; + + *native_match = false; guard(mutex)(&achan->rx_lock); @@ -232,10 +238,8 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]); - if (i == rx_front) { - acpm_get_saved_rx(achan, xfer, tx_seqnum); + if (i == rx_front) return 0; - } base = achan->rx.base; mlen = achan->mlen; @@ -256,11 +260,16 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) seqnum = rx_seqnum - 1; rx_data = &achan->rx_data[seqnum]; - if (rx_data->response) { + if (rx_data->rxcnt) { if (rx_seqnum == tx_seqnum) { __ioread32_copy(xfer->rxd, addr, xfer->rxcnt); - rx_set = true; - clear_bit(seqnum, achan->bitmap_seqnum); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling + * loop. + */ + smp_store_release(&rx_data->completed, true); + *native_match = true; } else { /* * The RX data corresponds to another request. @@ -268,10 +277,23 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) * clear yet the bitmap. It will be cleared * after the response is copied to the request. */ - __ioread32_copy(rx_data->cmd, addr, xfer->rxcnt); + __ioread32_copy(rx_data->cmd, addr, + rx_data->rxcnt); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling + * loop. + */ + smp_store_release(&rx_data->completed, true); } } else { - clear_bit(seqnum, achan->bitmap_seqnum); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling loop. + */ + smp_store_release(&rx_data->completed, true); + if (rx_seqnum == tx_seqnum) + *native_match = true; } i = (i + 1) % achan->qlen; @@ -280,13 +302,6 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) /* We saved all responses, mark RX empty. */ writel(rx_front, achan->rx.rear); - /* - * If the response was not in this iteration of the queue, check if the - * RX data was previously saved. - */ - if (!rx_set) - acpm_get_saved_rx(achan, xfer, tx_seqnum); - return 0; } @@ -301,6 +316,7 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, const struct acpm_xfer *xfer) { struct device *dev = achan->acpm->dev; + bool native_match; ktime_t timeout; u32 seqnum; int ret; @@ -309,12 +325,25 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US); do { - ret = acpm_get_rx(achan, xfer); + ret = acpm_get_rx(achan, xfer, &native_match); if (ret) return ret; - if (!test_bit(seqnum - 1, achan->bitmap_seqnum)) + /* + * Safely check if our specific transaction has been processed. + * smp_load_acquire prevents the CPU from speculatively + * executing subsequent instructions before the transaction is + * synchronized. + */ + if (smp_load_acquire(&achan->rx_data[seqnum - 1].completed)) { + /* Retrieve payload if another thread cached it for us */ + if (!native_match) + acpm_get_saved_rx(achan, xfer, seqnum); + + /* Relinquish ownership of the sequence slot */ + clear_bit_unlock(seqnum - 1, achan->bitmap_seqnum); return 0; + } /* Determined experimentally. */ udelay(20); @@ -362,29 +391,48 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front) * TX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer being prepared. + * + * Return: 0 on success, -errno otherwise. */ -static void acpm_prepare_xfer(struct acpm_chan *achan, - const struct acpm_xfer *xfer) +static int acpm_prepare_xfer(struct acpm_chan *achan, + const struct acpm_xfer *xfer) { struct acpm_rx_data *rx_data; u32 *txd = (u32 *)xfer->txd; + unsigned long size = ACPM_SEQNUM_MAX - 1; + unsigned long bit = achan->seqnum; + + bit = find_next_zero_bit(achan->bitmap_seqnum, size, bit); + if (bit >= size) { + bit = find_first_zero_bit(achan->bitmap_seqnum, size); + if (bit >= size) { + dev_err_ratelimited(achan->acpm->dev, + "ACPM sequence number pool exhausted\n"); + return -EBUSY; + } + } - /* Prevent chan->seqnum from being re-used */ - do { - if (++achan->seqnum == ACPM_SEQNUM_MAX) - achan->seqnum = 1; - } while (test_bit(achan->seqnum - 1, achan->bitmap_seqnum)); + /* + * Execute the atomic set to formally claim the bit and establish + * LKMM Acquire semantics against the RX thread's clear_bit_unlock(). + * A loop is unnecessary because allocations are strictly serialized + * by tx_lock. + */ + if (WARN_ON_ONCE(test_and_set_bit_lock(bit, achan->bitmap_seqnum))) + return -EIO; + /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ + achan->seqnum = bit + 1; txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum); /* Clear data for upcoming responses */ - rx_data = &achan->rx_data[achan->seqnum - 1]; + rx_data = &achan->rx_data[bit]; + rx_data->completed = false; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); - if (xfer->rxd) - rx_data->response = true; + /* zero means no response expected */ + rx_data->rxcnt = xfer->rxcnt; - /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ - set_bit(achan->seqnum - 1, achan->bitmap_seqnum); + return 0; } /** @@ -444,7 +492,9 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer) if (ret) return ret; - acpm_prepare_xfer(achan, xfer); + ret = acpm_prepare_xfer(achan, xfer); + if (ret) + return ret; /* Write TX command. */ __iowrite32_copy(achan->tx.base + achan->mlen * tx_front, @@ -526,10 +576,11 @@ static int acpm_achan_alloc_cmds(struct acpm_chan *achan) /** * acpm_free_mbox_chans() - free mailbox channels. - * @acpm: pointer to driver data. + * @data: pointer to driver data. */ -static void acpm_free_mbox_chans(struct acpm_info *acpm) +static void acpm_free_mbox_chans(void *data) { + struct acpm_info *acpm = data; int i; for (i = 0; i < acpm->num_chans; i++) @@ -557,6 +608,10 @@ static int acpm_channels_init(struct acpm_info *acpm) if (!acpm->chans) return -ENOMEM; + ret = devm_add_action_or_reset(dev, acpm_free_mbox_chans, acpm); + if (ret) + return dev_err_probe(dev, ret, "Failed to add mbox free action.\n"); + chans_shmem = acpm->sram_base + readl(&shmem->chans); for (i = 0; i < acpm->num_chans; i++) { @@ -578,10 +633,8 @@ static int acpm_channels_init(struct acpm_info *acpm) cl->dev = dev; achan->chan = mbox_request_channel(cl, 0); - if (IS_ERR(achan->chan)) { - acpm_free_mbox_chans(acpm); + if (IS_ERR(achan->chan)) return PTR_ERR(achan->chan); - } } return 0; diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index e1912108a0fe..2a7a0f774389 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -723,15 +723,9 @@ static int stratix10_rsu_probe(struct platform_device *pdev) return -ENOMEM; priv->client.dev = dev; - priv->client.receive_cb = NULL; priv->client.priv = priv; - priv->status.current_image = 0; - priv->status.fail_image = 0; - priv->status.error_location = 0; - priv->status.error_details = 0; - priv->status.version = 0; - priv->status.state = 0; priv->retry_counter = INVALID_RETRY_COUNTER; + priv->max_retry = INVALID_RETRY_COUNTER; priv->dcmf_version.dcmf0 = INVALID_DCMF_VERSION; priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION; priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION; @@ -740,11 +734,11 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; - priv->max_retry = INVALID_RETRY_COUNTER; - priv->spt0_address = INVALID_SPT_ADDRESS; - priv->spt1_address = INVALID_SPT_ADDRESS; + /* spt0/1_address and status fields default to 0 from kzalloc */ mutex_init(&priv->lock); + init_completion(&priv->completion); + priv->chan = stratix10_svc_request_channel_byname(&priv->client, SVC_CLIENT_RSU); if (IS_ERR(priv->chan)) { @@ -756,11 +750,9 @@ static int stratix10_rsu_probe(struct platform_device *pdev) ret = stratix10_svc_add_async_client(priv->chan, false); if (ret) { dev_err(dev, "failed to add async client\n"); - stratix10_svc_free_channel(priv->chan); - return ret; + goto free_channel; } - init_completion(&priv->completion); platform_set_drvdata(pdev, priv); /* get the initial state from firmware */ @@ -768,41 +760,44 @@ static int stratix10_rsu_probe(struct platform_device *pdev) rsu_async_status_callback); if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); - stratix10_svc_remove_async_client(priv->chan); - stratix10_svc_free_channel(priv->chan); - return ret; + goto remove_async_client; } /* get DCMF version from firmware */ - ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION, - 0, rsu_dcmf_version_callback); + ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION, 0, + rsu_dcmf_version_callback); if (ret) { dev_err(dev, "Error, getting DCMF version %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } - ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, - 0, rsu_dcmf_status_callback); + ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0, + rsu_dcmf_status_callback); if (ret) { dev_err(dev, "Error, getting DCMF status %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0, rsu_max_retry_callback); if (ret) { dev_err(dev, "Error, getting RSU max retry %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0, rsu_async_get_spt_table_callback); if (ret) { dev_err(dev, "Error, getting SPT table %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } + return 0; + +remove_async_client: + stratix10_svc_remove_async_client(priv->chan); +free_channel: + stratix10_svc_free_channel(priv->chan); return ret; } diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index e9e35d67ef96..39eb78f5905b 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -212,6 +212,7 @@ struct stratix10_async_chan { /** * struct stratix10_async_ctrl - Control structure for Stratix10 * asynchronous operations + * @supported: Flag indicating whether the system supports async operations * @initialized: Flag indicating whether the control structure has * been initialized * @invoke_fn: Function pointer for invoking Stratix10 service calls @@ -228,6 +229,7 @@ struct stratix10_async_chan { */ struct stratix10_async_ctrl { + bool supported; bool initialized; void (*invoke_fn)(struct stratix10_async_ctrl *actrl, const struct arm_smccc_1_2_regs *args, @@ -1103,6 +1105,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_request_channel_byname); * Return: 0 on success, or a negative error code on failure: * -EINVAL if the channel is NULL or the async controller is * not initialized. + * -EOPNOTSUPP if async operations are not supported. * -EALREADY if the async channel is already allocated. * -ENOMEM if memory allocation fails. * Other negative values if ID allocation fails. @@ -1121,6 +1124,9 @@ int stratix10_svc_add_async_client(struct stratix10_svc_chan *chan, ctrl = chan->ctrl; actrl = &ctrl->actrl; + if (!actrl->supported) + return -EOPNOTSUPP; + if (!actrl->initialized) { dev_err(ctrl->dev, "Async controller not initialized\n"); return -EINVAL; @@ -1562,6 +1568,7 @@ static inline void stratix10_smc_1_2(struct stratix10_async_ctrl *actrl, * initialized, -ENOMEM if memory allocation fails, * -EADDRINUSE if the client ID is already reserved, or other * negative error codes on failure. + * -EOPNOTSUPP if system doesn't support async operations. */ static int stratix10_svc_async_init(struct stratix10_svc_controller *controller) { @@ -1585,10 +1592,12 @@ static int stratix10_svc_async_init(struct stratix10_svc_controller *controller) !(res.a1 > ASYNC_ATF_MINIMUM_MAJOR_VERSION || (res.a1 == ASYNC_ATF_MINIMUM_MAJOR_VERSION && res.a2 >= ASYNC_ATF_MINIMUM_MINOR_VERSION))) { - dev_err(dev, - "Intel Service Layer Driver: ATF version is not compatible for async operation\n"); - return -EINVAL; + dev_info(dev, + "Intel Service Layer Driver: ATF version is not compatible for async operation\n"); + actrl->supported = false; + return -EOPNOTSUPP; } + actrl->supported = true; actrl->invoke_fn = stratix10_smc_1_2; @@ -1952,10 +1961,14 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) init_completion(&controller->complete_status); ret = stratix10_svc_async_init(controller); - if (ret) { + if (ret == -EOPNOTSUPP) { + dev_info(dev, "Intel Service Layer Driver Initialized (sync-only mode)\n"); + } else if (ret) { dev_dbg(dev, "Intel Service Layer Driver: Error on stratix10_svc_async_init %d\n", ret); goto err_destroy_pool; + } else { + dev_info(dev, "Intel Service Layer Driver Initialized\n"); } fifo_size = sizeof(struct stratix10_svc_data) * SVC_NUM_DATA_IN_FIFO; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 020e51e30317..28cf6d2e83c2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -102,6 +102,14 @@ config GPIO_CDEV_V1 This ABI version is deprecated. Please use the latest ABI for new developments. +config GPIO_KUNIT + tristate "Build GPIO Kunit test cases" + depends on KUNIT + default KUNIT_ALL_TESTS + help + Say Y here to build the module containing Kunit test cases verifying + the functionality of the GPIO subsystem. + config GPIO_GENERIC depends on HAS_IOMEM # Only for IOMEM drivers tristate @@ -156,6 +164,7 @@ config GPIO_74XX_MMIO config GPIO_ALTERA tristate "Altera GPIO" + select GPIO_GENERIC select GPIOLIB_IRQCHIP help Say Y or M here to build support for the Altera PIO device. @@ -301,7 +310,7 @@ config GPIO_EM config GPIO_EN7523 tristate "Airoha GPIO support" - depends on ARCH_AIROHA + depends on ARCH_AIROHA || COMPILE_TEST default ARCH_AIROHA select GPIO_GENERIC select GPIOLIB_IRQCHIP @@ -698,7 +707,7 @@ config GPIO_SPACEMIT_K1 config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" - depends on PLAT_SPEAR + depends on PLAT_SPEAR || COMPILE_TEST select GENERIC_IRQ_CHIP help Say yes here to support ST SPEAr SPI Chip Select as GPIO device. @@ -805,8 +814,18 @@ config GPIO_VISCONTI help Say yes here to support GPIO on Tohisba Visconti. +config GPIO_WAVESHARE_DSI_TOUCH + tristate "Waveshare GPIO controller for DSI panels" + depends on BACKLIGHT_CLASS_DEVICE + depends on I2C + select REGMAP_I2C + help + Enable support for the GPIO and PWM controller found on Waveshare DSI + TOUCH panel kits. It provides GPIOs (used for regulator control and + resets) and backlight support. + config GPIO_WCD934X - tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" + tristate "Qualcomm WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X help This driver is to support GPIO block found on the Qualcomm Technologies @@ -814,7 +833,7 @@ config GPIO_WCD934X config GPIO_XGENE bool "APM X-Gene GPIO controller support" - depends on ARM64 + depends on ARM64 || COMPILE_TEST help This driver is to support the GPIO block within the APM X-Gene SoC platform's generic flash controller. The GPIO pins are muxed with @@ -858,7 +877,7 @@ config GPIO_XTENSA config GPIO_ZEVIO bool "LSI ZEVIO SoC memory mapped GPIOs" - depends on ARM + depends on ARM || COMPILE_TEST help Say yes here to support the GPIO controller in LSI ZEVIO SoCs. @@ -1093,15 +1112,6 @@ config GPIO_SCH311X To compile this driver as a module, choose M here: the module will be called gpio-sch311x. -config GPIO_TS5500 - tristate "TS-5500 DIO blocks and compatibles" - depends on TS5500 || COMPILE_TEST - help - This driver supports Digital I/O exposed by pin blocks found on some - Technologic Systems platforms. It includes, but is not limited to, 3 - blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 - LCD port. - config GPIO_WINBOND tristate "Winbond Super I/O GPIO support" select ISA_BUS_API @@ -1783,6 +1793,21 @@ config GPIO_WM8994 endmenu +menu "Auxiliary Bus GPIO drivers" + depends on AUXILIARY_BUS + +config GPIO_LTC4283 + tristate "Analog Devices LTC4283 GPIO support" + depends on SENSORS_LTC4283 + help + If you say yes here you want the GPIO function available in Analog + Devices LTC4283 Negative Voltage Hot Swap Controller. + + This driver can also be built as a module. If so, the module will + be called gpio-ltc4283. + +endmenu + menu "PCI GPIO expanders" depends on PCI @@ -2060,6 +2085,7 @@ config GPIO_VIRTIO config GPIO_SIM tristate "GPIO Simulator Module" + depends on SYSFS select IRQ_SIM select CONFIGFS_FS help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b267598b517d..4d0e900402fc 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o +obj-$(CONFIG_GPIO_KUNIT) += gpiolib-kunit.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o @@ -100,6 +101,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_LTC4283) += gpio-ltc4283.o obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o @@ -194,7 +196,6 @@ obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o -obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o @@ -205,6 +206,7 @@ obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_WAVESHARE_DSI_TOUCH) += gpio-waveshare-dsi.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index c226524efeba..5ca61cf5206a 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -112,7 +112,7 @@ static int gen_74x164_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct gen_74x164_chip *chip; - u32 nregs; + u32 nregs, init_state; int ret; /* @@ -134,6 +134,21 @@ static int gen_74x164_probe(struct spi_device *spi) chip->registers = nregs; + /* + * Optionally seed the chain with a board-specified pattern so the + * outputs come up in a known state on the first SPI write. The + * property follows the nxp,pcf8575 convention where bit N maps to + * GPIO line N. On this output-only device, bit=0 drives the line + * low and bit=1 drives it high. The bitmask covers up to 32 lines; + * any further outputs come up zeroed by devm_kzalloc(). + */ + if (!device_property_read_u32(dev, "lines-initial-states", &init_state)) { + unsigned int i; + + for (i = 0; i < min(nregs, 4U); i++) + chip->buffer[nregs - 1 - i] = (init_state >> (i * 8)) & 0xff; + } + chip->gpiod_oe = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(chip->gpiod_oe)) return PTR_ERR(chip->gpiod_oe); diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index e5ac2d211013..350feea2afa3 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -237,7 +237,9 @@ static irqreturn_t adnp_irq(int irq, void *data) unsigned long pending; int err; - scoped_guard(mutex, &adnp->i2c_lock) { + { + guard(mutex)(&adnp->i2c_lock); + err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level); if (err < 0) continue; @@ -499,8 +501,8 @@ static int adnp_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adnp_i2c_id[] = { - { "gpio-adnp" }, - { }, + { .name = "gpio-adnp" }, + { } }; MODULE_DEVICE_TABLE(i2c, adnp_i2c_id); diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index 0fd3cc26d017..6f10fc646008 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -507,8 +507,8 @@ static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { }; static const struct platform_device_id adp5585_gpio_id_table[] = { - { "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info }, - { "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info }, + { .name = "adp5585-gpio", .driver_data = (kernel_ulong_t)&adp5585_gpio_chip_info }, + { .name = "adp5589-gpio", .driver_data = (kernel_ulong_t)&adp5589_gpio_chip_info }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table); diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 9508d764cce4..fe144360a88d 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -17,6 +17,7 @@ #include <linux/types.h> #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h> #define ALTERA_GPIO_MAX_NGPIO 32 #define ALTERA_GPIO_DATA 0x0 @@ -26,7 +27,7 @@ /** * struct altera_gpio_chip -* @gc : GPIO chip structure. +* @chip : Generic GPIO chip structure. * @regs : memory mapped IO address for the controller registers. * @gpio_lock : synchronization lock so that new irq/set/get requests * will be blocked until the current one completes. @@ -34,7 +35,7 @@ * (rising, falling, both, high) */ struct altera_gpio_chip { - struct gpio_chip gc; + struct gpio_generic_chip chip; void __iomem *regs; raw_spinlock_t gpio_lock; int interrupt_trigger; @@ -106,72 +107,6 @@ static unsigned int altera_gpio_irq_startup(struct irq_data *d) return 0; } -static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - - return !!(readl(altera_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); -} - -static int altera_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int data_reg; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); - if (value) - data_reg |= BIT(offset); - else - data_reg &= ~BIT(offset); - writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - -static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int gpio_ddr; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - /* Set pin as input, assumes software controlled IP */ - gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr &= ~BIT(offset); - writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - -static int altera_gpio_direction_output(struct gpio_chip *gc, - unsigned offset, int value) -{ - struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc); - unsigned long flags; - unsigned int data_reg, gpio_ddr; - - raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); - /* Sets the GPIO value */ - data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA); - if (value) - data_reg |= BIT(offset); - else - data_reg &= ~BIT(offset); - writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA); - - /* Set pin as output, assumes software controlled IP */ - gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr |= BIT(offset); - writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR); - raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); - - return 0; -} - static void altera_gpio_irq_edge_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -231,9 +166,12 @@ static const struct irq_chip altera_gpio_irq_chip = { static int altera_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config; struct device *dev = &pdev->dev; int reg, ret; struct altera_gpio_chip *altera_gc; + struct gpio_generic_chip *chip; + struct gpio_chip *gc; struct gpio_irq_chip *girq; int mapped_irq; @@ -243,35 +181,45 @@ static int altera_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&altera_gc->gpio_lock); + altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(altera_gc->regs)) + return dev_err_probe(dev, PTR_ERR(altera_gc->regs), + "failed to ioremap memory resource\n"); + + chip = &altera_gc->chip; + + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = altera_gc->regs + ALTERA_GPIO_DATA, + .set = altera_gc->regs + ALTERA_GPIO_DATA, + .dirout = altera_gc->regs + ALTERA_GPIO_DIR, + }; + + ret = gpio_generic_chip_init(chip, &config); + if (ret) + return dev_err_probe(dev, ret, "unable to init generic GPIO\n"); + + gc = &chip->gc; + if (device_property_read_u32(dev, "altr,ngpio", ®)) /* By default assume maximum ngpio */ - altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + gc->ngpio = ALTERA_GPIO_MAX_NGPIO; else - altera_gc->gc.ngpio = reg; + gc->ngpio = reg; - if (altera_gc->gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { + if (gc->ngpio > ALTERA_GPIO_MAX_NGPIO) { dev_warn(&pdev->dev, "ngpio is greater than %d, defaulting to %d\n", ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); - altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO; + gc->ngpio = ALTERA_GPIO_MAX_NGPIO; } - altera_gc->gc.direction_input = altera_gpio_direction_input; - altera_gc->gc.direction_output = altera_gpio_direction_output; - altera_gc->gc.get = altera_gpio_get; - altera_gc->gc.set = altera_gpio_set; - altera_gc->gc.owner = THIS_MODULE; - altera_gc->gc.parent = &pdev->dev; - altera_gc->gc.base = -1; - - altera_gc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); - if (!altera_gc->gc.label) + gc->base = -1; + gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!gc->label) return -ENOMEM; - altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(altera_gc->regs)) - return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n"); - mapped_irq = platform_get_irq_optional(pdev, 0); if (mapped_irq < 0) goto skip_irq; @@ -283,7 +231,7 @@ static int altera_gpio_probe(struct platform_device *pdev) } altera_gc->interrupt_trigger = reg; - girq = &altera_gc->gc.irq; + girq = &gc->irq; gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip); if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) @@ -300,7 +248,7 @@ static int altera_gpio_probe(struct platform_device *pdev) girq->parents[0] = mapped_irq; skip_irq: - ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc); + ret = devm_gpiochip_add_data(dev, gc, altera_gc); if (ret) { dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); return ret; diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 15fd5e210d74..8078b5d7b80c 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -59,8 +59,8 @@ * want to register another driver on the same PCI id. */ static const struct pci_device_id pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 }, - { 0, }, /* terminate list */ + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS) }, + { }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c index d0f936ed80af..306e23411209 100644 --- a/drivers/gpio/gpio-bd72720.c +++ b/drivers/gpio/gpio-bd72720.c @@ -263,8 +263,8 @@ static int gpo_bd72720_probe(struct platform_device *pdev) } static const struct platform_device_id bd72720_gpio_id[] = { - { "bd72720-gpio" }, - { }, + { .name = "bd72720-gpio" }, + { } }; MODULE_DEVICE_TABLE(platform, bd72720_gpio_id); diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index cc5b1746f2fe..f829fc40c02b 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -110,8 +110,8 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id bd9571mwv_gpio_id_table[] = { - { "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 }, - { "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 }, + { .name = "bd9571mwv-gpio", .driver_data = ROHM_CHIP_TYPE_BD9571 }, + { .name = "bd9574mwf-gpio", .driver_data = ROHM_CHIP_TYPE_BD9574 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table); diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c index 435483826c6e..9deda8a9d11a 100644 --- a/drivers/gpio/gpio-cros-ec.c +++ b/drivers/gpio/gpio-cros-ec.c @@ -196,8 +196,8 @@ static int cros_ec_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id cros_ec_gpio_id[] = { - { "cros-ec-gpio", 0 }, - {} + { .name = "cros-ec-gpio" }, + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id); diff --git a/drivers/gpio/gpio-ds4520.c b/drivers/gpio/gpio-ds4520.c index f52ecae382a4..5add662a7ef5 100644 --- a/drivers/gpio/gpio-ds4520.c +++ b/drivers/gpio/gpio-ds4520.c @@ -54,7 +54,7 @@ static const struct of_device_id ds4520_gpio_of_match_table[] = { MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table); static const struct i2c_device_id ds4520_gpio_id_table[] = { - { "ds4520-gpio" }, + { .name = "ds4520-gpio" }, { } }; MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table); diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 15cebc8b5d66..c1f3d83a67c1 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -694,6 +694,7 @@ static const struct acpi_device_id dwapb_acpi_match[] = { {"APMC0D07", GPIO_REG_OFFSET_V1}, {"APMC0D81", GPIO_REG_OFFSET_V2}, {"FUJI200A", GPIO_REG_OFFSET_V1}, + {"LECA0001", GPIO_REG_OFFSET_V1}, { } }; MODULE_DEVICE_TABLE(acpi, dwapb_acpi_match); diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 1f56e44ffc9a..8784e433e1ff 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -323,8 +323,7 @@ static int ep93xx_setup_irqs(struct platform_device *pdev, } girq->default_type = IRQ_TYPE_NONE; - /* TODO: replace with handle_bad_irq() once we are fully hierarchical */ - girq->handler = handle_simple_irq; + girq->handler = handle_bad_irq; return 0; } diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c index afc1b8461dab..45b02d36e66f 100644 --- a/drivers/gpio/gpio-fxl6408.c +++ b/drivers/gpio/gpio-fxl6408.c @@ -150,7 +150,7 @@ static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = { MODULE_DEVICE_TABLE(of, fxl6408_dt_ids); static const struct i2c_device_id fxl6408_id[] = { - { "fxl6408" }, + { .name = "fxl6408" }, { } }; MODULE_DEVICE_TABLE(i2c, fxl6408_id); diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 2e5d97b7363f..bf1f91c3c4a8 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -109,7 +109,7 @@ static int gw_pld_probe(struct i2c_client *client) } static const struct i2c_device_id gw_pld_id[] = { - { "gw-pld", }, + { .name = "gw-pld" }, { } }; MODULE_DEVICE_TABLE(i2c, gw_pld_id); diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index f34d87869c8b..669b139cd499 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -311,12 +311,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) } g->chip.gc.ngpio = 16; g->chip.gc.label = "IXP4XX_GPIO_CHIP"; - /* - * TODO: when we have migrated to device tree and all GPIOs - * are fetched using phandles, set this to -1 to get rid of - * the fixed gpiochip base. - */ - g->chip.gc.base = 0; + g->chip.gc.base = -1; g->chip.gc.parent = &pdev->dev; g->chip.gc.owner = THIS_MODULE; diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index f4413fa5a811..0d1bd9df265a 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -156,7 +156,7 @@ static int lp873x_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id lp873x_gpio_id_table[] = { - { "lp873x-gpio", }, + { .name = "lp873x-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp873x_gpio_id_table); diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index 0f337c1283b2..3ac78f2b0fa7 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -171,7 +171,7 @@ static int lp87565_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id lp87565_gpio_id_table[] = { - { "lp87565-q1-gpio", }, + { .name = "lp87565-q1-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table); diff --git a/drivers/gpio/gpio-ltc4283.c b/drivers/gpio/gpio-ltc4283.c new file mode 100644 index 000000000000..6609443c5d62 --- /dev/null +++ b/drivers/gpio/gpio-ltc4283.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices LTC4283 GPIO driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/gpio/driver.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#define LTC4283_PINS_MAX 8 +#define LTC4283_PGIOX_START_NR 4 +#define LTC4283_INPUT_STATUS 0x02 +#define LTC4283_PGIO_CONFIG 0x10 +#define LTC4283_PGIO_CFG_MASK(pin) \ + GENMASK(((pin) - LTC4283_PGIOX_START_NR) * 2 + 1, (((pin) - LTC4283_PGIOX_START_NR) * 2)) +#define LTC4283_PGIO_CONFIG_2 0x11 + +#define LTC4283_ADIO_CONFIG 0x12 +/* starts at bit 4 */ +#define LTC4283_ADIOX_CONFIG_MASK(pin) BIT((pin) + 4) +#define LTC4283_PGIO_DIR_IN 3 +#define LTC4283_PGIO_DIR_OUT 2 + +struct ltc4283_gpio { + struct gpio_chip gpio_chip; + struct regmap *regmap; +}; + +static int ltc4283_pgio_get_direction(const struct ltc4283_gpio *st, unsigned int off) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, LTC4283_PGIO_CONFIG, &val); + if (ret) + return ret; + + val = field_get(LTC4283_PGIO_CFG_MASK(off), val); + if (val == LTC4283_PGIO_DIR_IN) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int ltc4283_gpio_get_direction(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + unsigned int val; + int ret; + + if (off >= LTC4283_PGIOX_START_NR) + return ltc4283_pgio_get_direction(st, off); + + ret = regmap_read(st->regmap, LTC4283_ADIO_CONFIG, &val); + if (ret) + return ret; + + if (val & LTC4283_ADIOX_CONFIG_MASK(off)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int ltc4283_gpio_direction_set(const struct ltc4283_gpio *st, + unsigned int off, bool input) +{ + if (off >= LTC4283_PGIOX_START_NR) { + unsigned int val = LTC4283_PGIO_DIR_OUT; + + if (input) + val = LTC4283_PGIO_DIR_IN; + + val = field_prep(LTC4283_PGIO_CFG_MASK(off), val); + return regmap_update_bits(st->regmap, LTC4283_PGIO_CONFIG, + LTC4283_PGIO_CFG_MASK(off), val); + } + + return regmap_update_bits(st->regmap, LTC4283_ADIO_CONFIG, + LTC4283_ADIOX_CONFIG_MASK(off), + field_prep(LTC4283_ADIOX_CONFIG_MASK(off), input)); +} + +static int __ltc4283_gpio_set_value(const struct ltc4283_gpio *st, + unsigned int off, int val) +{ + u32 reg = off < LTC4283_PGIOX_START_NR ? LTC4283_ADIO_CONFIG : LTC4283_PGIO_CONFIG_2; + + return regmap_update_bits(st->regmap, reg, BIT(off), + field_prep(BIT(off), !!val)); +} + +static int ltc4283_gpio_direction_input(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + + return ltc4283_gpio_direction_set(st, off, true); +} + +static int ltc4283_gpio_direction_output(struct gpio_chip *gc, unsigned int off, int val) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + int ret; + + ret = ltc4283_gpio_direction_set(st, off, false); + if (ret) + return ret; + + return __ltc4283_gpio_set_value(st, off, val); +} + +static int ltc4283_gpio_get_value(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + unsigned int val, reg; + int ret, dir; + + dir = ltc4283_gpio_get_direction(gc, off); + if (dir < 0) + return dir; + + if (dir == GPIO_LINE_DIRECTION_IN) { + ret = regmap_read(st->regmap, LTC4283_INPUT_STATUS, &val); + if (ret) + return ret; + + /* ADIO1 is at bit 3. */ + if (off < LTC4283_PGIOX_START_NR) + return !!(val & BIT(3 - off)); + + /* PGIO1 is at bit 7. */ + return !!(val & BIT(7 - (off - LTC4283_PGIOX_START_NR))); + } + + if (off < LTC4283_PGIOX_START_NR) + reg = LTC4283_ADIO_CONFIG; + else + reg = LTC4283_PGIO_CONFIG_2; + + ret = regmap_read(st->regmap, reg, &val); + if (ret) + return ret; + + return !!(val & BIT(off)); +} + +static int ltc4283_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + + return __ltc4283_gpio_set_value(st, off, val); +} + +static int ltc4283_init_valid_mask(struct gpio_chip *gc, unsigned long *valid_mask, + unsigned int ngpios) +{ + unsigned long *mask = dev_get_platdata(gc->parent); + + bitmap_copy(valid_mask, mask, ngpios); + return 0; +} + +static int ltc4283_gpio_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct ltc4283_gpio *st; + struct gpio_chip *gc; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->regmap = dev_get_regmap(dev->parent, NULL); + if (!st->regmap) + return dev_err_probe(dev, -ENODEV, + "Failed to get regmap\n"); + + gc = &st->gpio_chip; + gc->parent = dev; + gc->get_direction = ltc4283_gpio_get_direction; + gc->direction_input = ltc4283_gpio_direction_input; + gc->direction_output = ltc4283_gpio_direction_output; + gc->get = ltc4283_gpio_get_value; + gc->set = ltc4283_gpio_set_value; + gc->init_valid_mask = ltc4283_init_valid_mask; + gc->can_sleep = true; + + gc->base = -1; + gc->ngpio = LTC4283_PINS_MAX; + gc->label = adev->name; + gc->owner = THIS_MODULE; + + return devm_gpiochip_add_data(dev, &st->gpio_chip, st); +} + +static const struct auxiliary_device_id ltc4283_aux_id_table[] = { + { "ltc4283.gpio" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, ltc4283_aux_id_table); + +static struct auxiliary_driver ltc4283_gpio_driver = { + .probe = ltc4283_gpio_probe, + .id_table = ltc4283_aux_id_table, +}; +module_auxiliary_driver(ltc4283_gpio_driver); + +MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("GPIO LTC4283 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c index 621d609ece90..62f2434c0d79 100644 --- a/drivers/gpio/gpio-max7300.c +++ b/drivers/gpio/gpio-max7300.c @@ -53,7 +53,7 @@ static void max7300_remove(struct i2c_client *client) } static const struct i2c_device_id max7300_id[] = { - { "max7300" }, + { .name = "max7300" }, { } }; MODULE_DEVICE_TABLE(i2c, max7300_id); diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 281ba1740a6a..24c67c912954 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -103,16 +103,16 @@ static uint64_t max732x_features[] = { }; static const struct i2c_device_id max732x_id[] = { - { "max7319", MAX7319 }, - { "max7320", MAX7320 }, - { "max7321", MAX7321 }, - { "max7322", MAX7322 }, - { "max7323", MAX7323 }, - { "max7324", MAX7324 }, - { "max7325", MAX7325 }, - { "max7326", MAX7326 }, - { "max7327", MAX7327 }, - { }, + { .name = "max7319", .driver_data = MAX7319 }, + { .name = "max7320", .driver_data = MAX7320 }, + { .name = "max7321", .driver_data = MAX7321 }, + { .name = "max7322", .driver_data = MAX7322 }, + { .name = "max7323", .driver_data = MAX7323 }, + { .name = "max7324", .driver_data = MAX7324 }, + { .name = "max7325", .driver_data = MAX7325 }, + { .name = "max7326", .driver_data = MAX7326 }, + { .name = "max7327", .driver_data = MAX7327 }, + { } }; MODULE_DEVICE_TABLE(i2c, max732x_id); diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index e6c85411c695..2bf3b55a61b5 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -367,7 +367,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) static const struct platform_device_id max77620_gpio_devtype[] = { { .name = "max77620-gpio", }, { .name = "max20024-gpio", }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype); diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c index 3bf9f23d1532..c6bdac7fb44a 100644 --- a/drivers/gpio/gpio-max77759.c +++ b/drivers/gpio/gpio-max77759.c @@ -502,7 +502,7 @@ static const struct of_device_id max77759_gpio_of_id[] = { MODULE_DEVICE_TABLE(of, max77759_gpio_of_id); static const struct platform_device_id max77759_gpio_platform_id[] = { - { "max77759-gpio", }, + { .name = "max77759-gpio" }, { } }; MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id); diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index a7d69f3835c1..91ff789c4fa6 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -17,6 +17,7 @@ #include <linux/irq.h> #include <linux/irq_sim.h> #include <linux/irqdomain.h> +#include <linux/limits.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -578,7 +579,7 @@ static int __init gpio_mockup_register_chip(int idx) static int __init gpio_mockup_init(void) { - int i, num_chips, err; + int i, num_chips, err, base, ngpio; if ((gpio_mockup_num_ranges % 2) || (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES)) @@ -592,8 +593,19 @@ static int __init gpio_mockup_init(void) * always be greater than 0. */ for (i = 0; i < num_chips; i++) { - if (gpio_mockup_range_ngpio(i) < 0) + base = gpio_mockup_range_base(i); + ngpio = gpio_mockup_range_ngpio(i); + + if (ngpio <= 0) return -EINVAL; + + if (base < 0) { + if (ngpio > U16_MAX) + return -EINVAL; + } else { + if (ngpio <= base || ngpio - base > U16_MAX) + return -EINVAL; + } } gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup", NULL); diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 91230be51587..a814885ccd5d 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -29,8 +29,8 @@ #define GPIO_REG_EDGE 0xA0 struct mtk_gc { - struct irq_chip irq_chip; struct gpio_generic_chip chip; + struct mtk *parent_priv; int bank; u32 rising; u32 falling; @@ -41,20 +41,32 @@ struct mtk_gc { /** * struct mtk - state container for * data of the platform driver. It is 3 - * separate gpio-chip each one with its - * own irq_chip. - * @dev: device instance + * separate gpio-chip having an IRQ + * linear domain shared for all of them + * @pdev: platform device instance * @base: memory base address + * @irq_domain: IRQ linear domain shared across the three gpio chips * @gpio_irq: irq number from the device tree + * @num_gpios: total number of gpio pins on the three gpio chips * @gc_map: array of the gpio chips */ struct mtk { - struct device *dev; + struct platform_device *pdev; void __iomem *base; + struct irq_domain *irq_domain; int gpio_irq; + int num_gpios; struct mtk_gc gc_map[MTK_BANK_CNT]; }; +static inline struct mtk * +mt7621_gpio_gc_to_priv(struct gpio_chip *gc) +{ + struct mtk_gc *bank = gpiochip_get_data(gc); + + return bank->parent_priv; +} + static inline struct mtk_gc * to_mediatek_gpio(struct gpio_chip *chip) { @@ -67,7 +79,7 @@ static inline void mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; gpio_generic_write_reg(&rg->chip, mtk->base + offset, val); @@ -77,41 +89,62 @@ static inline u32 mtk_gpio_r32(struct mtk_gc *rg, u32 offset) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; return gpio_generic_read_reg(&rg->chip, mtk->base + offset); } -static irqreturn_t -mediatek_gpio_irq_handler(int irq, void *data) +static void +mt7621_gpio_irq_bank_handler(struct mtk_gc *bank) { - struct gpio_chip *gc = data; - struct mtk_gc *rg = to_mediatek_gpio(gc); - irqreturn_t ret = IRQ_NONE; + struct mtk *priv = bank->parent_priv; + struct irq_domain *domain = priv->irq_domain; + int hwbase = bank->chip.gc.offset; unsigned long pending; - int bit; + unsigned int offset; + + pending = mtk_gpio_r32(bank, GPIO_REG_STAT); + if (!pending) + return; + + mtk_gpio_w32(bank, GPIO_REG_STAT, pending); + + for_each_set_bit(offset, &pending, MTK_BANK_WIDTH) + generic_handle_domain_irq(domain, hwbase + offset); +} + +static void +mt7621_gpio_irq_handler(struct irq_desc *desc) +{ + struct mtk *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int i; - pending = mtk_gpio_r32(rg, GPIO_REG_STAT); + chained_irq_enter(chip, desc); + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; - for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) { - generic_handle_domain_irq(gc->irq.domain, bit); - mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit)); - ret |= IRQ_HANDLED; + mt7621_gpio_irq_bank_handler(bank); } + chained_irq_exit(chip, desc); +} - return ret; +static int +mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank) +{ + return hwirq - bank->chip.gc.offset; } static void mediatek_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; - gpiochip_enable_irq(gc, d->hwirq); + gpiochip_enable_irq(gc, mask); guard(gpio_generic_lock_irqsave)(&rg->chip); @@ -119,18 +152,18 @@ mediatek_gpio_irq_unmask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising)); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(mask) & rg->rising)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(mask) & rg->falling)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(mask) & rg->hlevel)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(mask) & rg->llevel)); } static void mediatek_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; scoped_guard(gpio_generic_lock_irqsave, &rg->chip) { @@ -138,22 +171,21 @@ mediatek_gpio_irq_mask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(mask)); } - gpiochip_disable_irq(gc, d->hwirq); + gpiochip_disable_irq(gc, mask); } static int mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; - u32 mask = BIT(pin); + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg)); if (type == IRQ_TYPE_PROBE) { if ((rg->rising | rg->falling | @@ -191,6 +223,26 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) } static int +mt7621_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + return gpiochip_reqres_irq(gc, irq); +} + +static void +mt7621_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + gpiochip_relres_irq(gc, irq); +} + +static int mediatek_gpio_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) { @@ -208,15 +260,124 @@ mediatek_gpio_xlate(struct gpio_chip *chip, static const struct irq_chip mt7621_irq_chip = { .name = "mt7621-gpio", + .irq_request_resources = mt7621_gpio_irq_reqres, + .irq_release_resources = mt7621_gpio_irq_relres, .irq_mask_ack = mediatek_gpio_irq_mask, .irq_mask = mediatek_gpio_irq_mask, .irq_unmask = mediatek_gpio_irq_unmask, .irq_set_type = mediatek_gpio_irq_type, .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void +mt7621_gpio_remove(struct platform_device *pdev) +{ + struct mtk *priv = platform_get_drvdata(pdev); + int offset, virq; + + if (priv->gpio_irq > 0) + irq_set_chained_handler_and_data(priv->gpio_irq, NULL, NULL); + + /* Remove all IRQ mappings and delete the domain */ + if (priv->irq_domain) { + for (offset = 0; offset < priv->num_gpios; offset++) { + virq = irq_find_mapping(priv->irq_domain, offset); + irq_dispose_mapping(virq); + } + irq_domain_remove(priv->irq_domain); + } +} + +static struct mtk_gc * +mt7621_gpio_hwirq_to_bank(struct mtk *priv, irq_hw_number_t hwirq) +{ + int i; + + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; + + if (hwirq >= bank->chip.gc.offset && + hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio)) + return bank; + } + + return NULL; +} + +static int +mt7621_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mtk *priv = d->host_data; + struct mtk_gc *bank = mt7621_gpio_hwirq_to_bank(priv, hwirq); + struct platform_device *pdev = priv->pdev; + int ret; + + if (!bank) + return -EINVAL; + + dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n", + irq, (int)hwirq, bank->bank); + + ret = irq_set_chip_data(irq, &bank->chip.gc); + if (ret < 0) + return ret; + + irq_set_chip_and_handler(irq, &mt7621_irq_chip, handle_simple_irq); + irq_set_noprobe(irq); + + return 0; +} + +static void +mt7621_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops mt7621_gpio_irq_domain_ops = { + .map = mt7621_gpio_irq_map, + .unmap = mt7621_gpio_irq_unmap, + .xlate = irq_domain_xlate_twocell, }; static int +mt7621_gpio_irq_setup(struct platform_device *pdev, + struct mtk *priv) +{ + struct device *dev = &pdev->dev; + + priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev), + priv->num_gpios, + &mt7621_gpio_irq_domain_ops, + priv); + if (!priv->irq_domain) { + dev_err(dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } + + irq_set_chained_handler_and_data(priv->gpio_irq, + mt7621_gpio_irq_handler, priv); + irq_set_status_flags(priv->gpio_irq, IRQ_DISABLE_UNLAZY); + + return 0; +} + +static int +mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct mtk *priv = mt7621_gpio_gc_to_priv(gc); + /* gc_offset is relative to this gpio_chip; want real offset */ + int hwirq = offset + gc->offset; + + if (hwirq >= priv->num_gpios) + return -ENXIO; + + return irq_create_mapping(priv->irq_domain, hwirq); +} + +static int mediatek_gpio_bank_probe(struct device *dev, int bank) { struct gpio_generic_chip_config config; @@ -228,6 +389,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg = &mtk->gc_map[bank]; memset(rg, 0, sizeof(*rg)); + rg->parent_priv = mtk; rg->bank = bank; dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE); @@ -253,41 +415,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg->chip.gc.of_gpio_n_cells = 2; rg->chip.gc.of_xlate = mediatek_gpio_xlate; + rg->chip.gc.ngpio = MTK_BANK_WIDTH; rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", dev_name(dev), bank); if (!rg->chip.gc.label) return -ENOMEM; rg->chip.gc.offset = bank * MTK_BANK_WIDTH; + if (mtk->gpio_irq > 0) + rg->chip.gc.to_irq = mt7621_gpio_to_irq; - if (mtk->gpio_irq) { - struct gpio_irq_chip *girq; - - /* - * Directly request the irq here instead of passing - * a flow-handler because the irq is shared. - */ - ret = devm_request_irq(dev, mtk->gpio_irq, - mediatek_gpio_irq_handler, IRQF_SHARED, - rg->chip.gc.label, &rg->chip.gc); - - if (ret) { - dev_err(dev, "Error requesting IRQ %d: %d\n", - mtk->gpio_irq, ret); - return ret; - } - - girq = &rg->chip.gc.irq; - gpio_irq_chip_set_chip(girq, &mt7621_irq_chip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_simple_irq; - } - - ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk); + ret = devm_gpiochip_add_data(dev, &rg->chip.gc, rg); if (ret < 0) { dev_err(dev, "Could not register gpio %d, ret=%d\n", rg->chip.gc.ngpio, ret); @@ -322,7 +460,8 @@ mediatek_gpio_probe(struct platform_device *pdev) if (mtk->gpio_irq < 0) return mtk->gpio_irq; - mtk->dev = dev; + mtk->pdev = pdev; + mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT; platform_set_drvdata(pdev, mtk); for (i = 0; i < MTK_BANK_CNT; i++) { @@ -331,7 +470,17 @@ mediatek_gpio_probe(struct platform_device *pdev) return ret; } + if (mtk->gpio_irq > 0) { + ret = mt7621_gpio_irq_setup(pdev, mtk); + if (ret) + goto fail; + } + return 0; + +fail: + mt7621_gpio_remove(pdev); + return ret; } static const struct of_device_id mediatek_gpio_match[] = { @@ -342,6 +491,7 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match); static struct platform_driver mediatek_gpio_driver = { .probe = mediatek_gpio_probe, + .remove = mt7621_gpio_remove, .driver = { .name = "mt7621_gpio", .of_match_table = mediatek_gpio_match, diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 22c36b79e249..c030d1f00abc 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -996,7 +996,7 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) BUG(); } - if (IS_REACHABLE(CONFIG_PWM)) + if (IS_REACHABLE(CONFIG_PWM) && mvchip->mvpwm) mvebu_pwm_suspend(mvchip); return 0; @@ -1048,7 +1048,7 @@ static int mvebu_gpio_resume(struct platform_device *pdev) BUG(); } - if (IS_REACHABLE(CONFIG_PWM)) + if (IS_REACHABLE(CONFIG_PWM) && mvchip->mvpwm) mvebu_pwm_resume(mvchip); return 0; diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 647b6f4861b7..7e2690d92df6 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -330,13 +330,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) ret = enable_irq_wake(port->irq_high); else ret = enable_irq_wake(port->irq); - port->wakeup_pads |= (1 << gpio_idx); + port->wakeup_pads |= BIT(gpio_idx); } else { if (port->irq_high && (gpio_idx >= 16)) ret = disable_irq_wake(port->irq_high); else ret = disable_irq_wake(port->irq); - port->wakeup_pads &= ~(1 << gpio_idx); + port->wakeup_pads &= ~BIT(gpio_idx); } return ret; @@ -469,7 +469,7 @@ static int mxc_gpio_probe(struct platform_device *pdev) * the handler is needed only once, but doing it for every port * is more robust and easier. */ - port->irq_high = -1; + port->irq_high = 0; port->mx_irq_handler = mx2_gpio_irq_handler; } else port->mx_irq_handler = mx3_gpio_irq_handler; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b9c905a0ffa9..2ee35e855e4d 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -86,49 +86,49 @@ #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) static const struct i2c_device_id pca953x_id[] = { - { "pca6408", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca6416", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9505", 40 | PCA953X_TYPE | PCA_INT, }, - { "pca9506", 40 | PCA953X_TYPE | PCA_INT, }, - { "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9536", 4 | PCA953X_TYPE, }, - { "pca9537", 4 | PCA953X_TYPE | PCA_INT, }, - { "pca9538", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9539", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9554", 8 | PCA953X_TYPE | PCA_INT, }, - { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca9556", 8 | PCA953X_TYPE, }, - { "pca9557", 8 | PCA953X_TYPE, }, - { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, - { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, - { "pca9698", 40 | PCA953X_TYPE, }, - - { "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal6534", 34 | PCAL653X_TYPE | PCA_LATCH_INT, }, - { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal9554b", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, - - { "max7310", 8 | PCA953X_TYPE, }, - { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, - { "max7315", 8 | PCA953X_TYPE | PCA_INT, }, - { "max7318", 16 | PCA953X_TYPE | PCA_INT, }, - { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, - { "tca6418", 18 | TCA6418_TYPE | PCA_INT, }, - { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, - { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, - { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, - { "tca9554", 8 | PCA953X_TYPE | PCA_INT, }, - { "xra1202", 8 | PCA953X_TYPE }, - - { "tcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, - { "tcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, + { .name = "pca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9505", .driver_data = 40 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9506", .driver_data = 40 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9534", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9535", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9536", .driver_data = 4 | PCA953X_TYPE }, + { .name = "pca9537", .driver_data = 4 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9555", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca9556", .driver_data = 8 | PCA953X_TYPE }, + { .name = "pca9557", .driver_data = 8 | PCA953X_TYPE }, + { .name = "pca9574", .driver_data = 8 | PCA957X_TYPE | PCA_INT }, + { .name = "pca9575", .driver_data = 16 | PCA957X_TYPE | PCA_INT }, + { .name = "pca9698", .driver_data = 40 | PCA953X_TYPE }, + + { .name = "pcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6524", .driver_data = 24 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal6534", .driver_data = 34 | PCAL653X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9535", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9554b", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "pcal9555a", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, + + { .name = "max7310", .driver_data = 8 | PCA953X_TYPE }, + { .name = "max7312", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "max7313", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "max7315", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "max7318", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "pca6107", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "tca6418", .driver_data = 18 | TCA6418_TYPE | PCA_INT }, + { .name = "tca6424", .driver_data = 24 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT }, + { .name = "tca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT }, + { .name = "xra1202", .driver_data = 8 | PCA953X_TYPE }, + + { .name = "tcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT }, + { .name = "tcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); @@ -877,11 +877,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio); bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio); - bitmap_complement(reg_direction, reg_direction, gc->ngpio); - bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); /* Look for any newly setup interrupt */ - for_each_set_bit(level, irq_mask, gc->ngpio) + for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio) pca953x_gpio_direction_input(&chip->gpio_chip, level); mutex_unlock(&chip->irq_lock); @@ -1005,8 +1003,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); bitmap_or(pending, pending, cur_stat, gc->ngpio); - bitmap_complement(cur_stat, new_stat, gc->ngpio); - bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio); + bitmap_andnot(cur_stat, reg_direction, new_stat, gc->ngpio); bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio); bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio); bitmap_or(pending, pending, old_stat, gc->ngpio); diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index 4a368803fb03..7a47a9aa0414 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -163,9 +163,9 @@ static const struct pca9570_chip_data slg7xl45106_gpio = { }; static const struct i2c_device_id pca9570_id_table[] = { - { "pca9570", (kernel_ulong_t)&pca9570_gpio}, - { "pca9571", (kernel_ulong_t)&pca9571_gpio }, - { "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio }, + { .name = "pca9570", .driver_data = (kernel_ulong_t)&pca9570_gpio }, + { .name = "pca9571", .driver_data = (kernel_ulong_t)&pca9571_gpio }, + { .name = "slg7xl45106", .driver_data = (kernel_ulong_t)&slg7xl45106_gpio }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9570_id_table); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 3b9de8c3d924..c942b959571b 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -20,19 +20,19 @@ #include <linux/spinlock.h> static const struct i2c_device_id pcf857x_id[] = { - { "pcf8574", 8 }, - { "pcf8574a", 8 }, - { "pca8574", 8 }, - { "pca9670", 8 }, - { "pca9672", 8 }, - { "pca9674", 8 }, - { "pcf8575", 16 }, - { "pca8575", 16 }, - { "pca9671", 16 }, - { "pca9673", 16 }, - { "pca9675", 16 }, - { "max7328", 8 }, - { "max7329", 8 }, + { .name = "pcf8574", .driver_data = 8 }, + { .name = "pcf8574a", .driver_data = 8 }, + { .name = "pca8574", .driver_data = 8 }, + { .name = "pca9670", .driver_data = 8 }, + { .name = "pca9672", .driver_data = 8 }, + { .name = "pca9674", .driver_data = 8 }, + { .name = "pcf8575", .driver_data = 16 }, + { .name = "pca8575", .driver_data = 16 }, + { .name = "pca9671", .driver_data = 16 }, + { .name = "pca9673", .driver_data = 16 }, + { .name = "pca9675", .driver_data = 16 }, + { .name = "max7328", .driver_data = 8 }, + { .name = "max7329", .driver_data = 8 }, { } }; MODULE_DEVICE_TABLE(i2c, pcf857x_id); diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 664cf1eef494..5d61053e0596 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -708,15 +708,15 @@ static int pxa_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id gpio_id_table[] = { - { "pxa25x-gpio", (unsigned long)&pxa25x_id }, - { "pxa26x-gpio", (unsigned long)&pxa26x_id }, - { "pxa27x-gpio", (unsigned long)&pxa27x_id }, - { "pxa3xx-gpio", (unsigned long)&pxa3xx_id }, - { "pxa93x-gpio", (unsigned long)&pxa93x_id }, - { "mmp-gpio", (unsigned long)&mmp_id }, - { "mmp2-gpio", (unsigned long)&mmp2_id }, - { "pxa1928-gpio", (unsigned long)&pxa1928_id }, - { }, + { .name = "pxa25x-gpio", .driver_data = (unsigned long)&pxa25x_id }, + { .name = "pxa26x-gpio", .driver_data = (unsigned long)&pxa26x_id }, + { .name = "pxa27x-gpio", .driver_data = (unsigned long)&pxa27x_id }, + { .name = "pxa3xx-gpio", .driver_data = (unsigned long)&pxa3xx_id }, + { .name = "pxa93x-gpio", .driver_data = (unsigned long)&pxa93x_id }, + { .name = "mmp-gpio", .driver_data = (unsigned long)&mmp_id }, + { .name = "mmp2-gpio", .driver_data = (unsigned long)&mmp2_id }, + { .name = "pxa1928-gpio", .driver_data = (unsigned long)&pxa1928_id }, + { } }; static struct platform_driver pxa_gpio_driver = { diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index 5e3152c2e51a..37ef56f45318 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -40,16 +40,18 @@ #define REALTEK_GPIO_PORTS_PER_BANK 4 /** - * realtek_gpio_ctrl - Realtek Otto GPIO driver data + * struct realtek_gpio_ctrl - Realtek Otto GPIO driver data * * @chip: Associated gpio_generic_chip instance * @base: Base address of the register block for a GPIO bank + * @cpumask_base: Base address of the per-CPU interrupt mask registers + * @cpu_irq_maskable: CPUs that can receive GPIO interrupts * @lock: Lock for accessing the IRQ registers and values * @intr_mask: Mask for interrupts lines * @intr_type: Interrupt type selection * @bank_read: Read a bank setting as a single 32-bit value * @bank_write: Write a bank setting as a single 32-bit value - * @imr_line_pos: Bit shift of an IRQ line's IMR value. + * @line_imr_pos: Bit shift of an IRQ line's IMR value. * * The DIR, DATA, and ISR registers consist of four 8-bit port values, packed * into a single 32-bit register. Use @bank_read (@bank_write) to get (assign) diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 9ae4a41a2427..51b4d69b8740 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -31,6 +31,7 @@ struct gpio_regmap { unsigned int reg_clr_base; unsigned int reg_dir_in_base; unsigned int reg_dir_out_base; + unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output; #ifdef CONFIG_REGMAP_IRQ @@ -138,6 +139,20 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip, return regmap_write(gpio->regmap, reg, mask); } +static bool gpio_regmap_fixed_direction(struct gpio_regmap *gpio, + unsigned int offset) +{ + if (!gpio->fixed_direction_output) + return false; + + /* In this case only some GPIOs are fixed as input/output */ + if (gpio->fixed_direction_mask && + !test_bit(offset, gpio->fixed_direction_mask)) + return false; + + return true; +} + static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -145,7 +160,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; - if (gpio->fixed_direction_output) { + if (gpio_regmap_fixed_direction(gpio, offset)) { if (test_bit(offset, gpio->fixed_direction_output)) return GPIO_LINE_DIRECTION_OUT; else @@ -181,6 +196,22 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_IN; } +static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio, + unsigned int offset, bool output) +{ + if (test_bit(offset, gpio->fixed_direction_output)) { + if (output) + return 0; + else + return -EINVAL; + } else { + if (output) + return -EINVAL; + else + return 0; + } +} + static int gpio_regmap_set_direction(struct gpio_chip *chip, unsigned int offset, bool output) { @@ -188,6 +219,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; + /* + * If the direction is fixed, only accept the fixed + * direction in this call. + */ + if (gpio_regmap_fixed_direction(gpio, offset)) + return gpio_regmap_try_direction_fixed(gpio, offset, output); + if (gpio->reg_dir_out_base) { base = gpio_regmap_addr(gpio->reg_dir_out_base); invert = 0; @@ -219,6 +257,20 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip, static int gpio_regmap_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { + struct gpio_regmap *gpio = gpiochip_get_data(chip); + int ret; + + /* + * First check if this is gonna work on a fixed direction line, + * if it doesn't (i.e. this is a fixed input line), then do not + * attempt to set the output value either and just bail out. + */ + if (gpio_regmap_fixed_direction(gpio, offset)) { + ret = gpio_regmap_try_direction_fixed(gpio, offset, true); + if (ret) + return ret; + } + gpio_regmap_set(chip, offset, value); return gpio_regmap_set_direction(chip, offset, true); @@ -302,12 +354,23 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config goto err_free_gpio; } + if (config->fixed_direction_mask) { + gpio->fixed_direction_mask = bitmap_alloc(chip->ngpio, + GFP_KERNEL); + if (!gpio->fixed_direction_mask) { + ret = -ENOMEM; + goto err_free_gpio; + } + bitmap_copy(gpio->fixed_direction_mask, + config->fixed_direction_mask, chip->ngpio); + } + if (config->fixed_direction_output) { gpio->fixed_direction_output = bitmap_alloc(chip->ngpio, GFP_KERNEL); if (!gpio->fixed_direction_output) { ret = -ENOMEM; - goto err_free_gpio; + goto err_free_bitmap_dirmask; } bitmap_copy(gpio->fixed_direction_output, config->fixed_direction_output, chip->ngpio); @@ -329,7 +392,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config ret = gpiochip_add_data(chip, gpio); if (ret < 0) - goto err_free_bitmap; + goto err_free_bitmap_output; #ifdef CONFIG_REGMAP_IRQ if (config->regmap_irq_chip) { @@ -355,8 +418,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config err_remove_gpiochip: gpiochip_remove(chip); -err_free_bitmap: +err_free_bitmap_output: bitmap_free(gpio->fixed_direction_output); +err_free_bitmap_dirmask: + bitmap_free(gpio->fixed_direction_mask); err_free_gpio: kfree(gpio); return ERR_PTR(ret); @@ -376,6 +441,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio) gpiochip_remove(&gpio->gpio_chip); bitmap_free(gpio->fixed_direction_output); + bitmap_free(gpio->fixed_direction_mask); kfree(gpio); } EXPORT_SYMBOL_GPL(gpio_regmap_unregister); diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 44d7ebd12724..9478a58f1caa 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -638,10 +638,17 @@ fail: return ret; } +static void rockchip_clk_put(void *data) +{ + struct clk *clk = data; + + clk_put(clk); +} + static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) { struct resource res; - int id = 0; + int id = 0, ret; if (of_address_to_resource(bank->of_node, 0, &res)) { dev_err(bank->dev, "cannot find IO resource for bank\n"); @@ -656,11 +663,10 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) if (!bank->irq) return -EINVAL; - bank->clk = of_clk_get(bank->of_node, 0); + bank->clk = devm_clk_get_enabled(bank->dev, NULL); if (IS_ERR(bank->clk)) return PTR_ERR(bank->clk); - clk_prepare_enable(bank->clk); id = readl(bank->reg_base + gpio_regs_v2.version_id); switch (id) { @@ -672,9 +678,13 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) bank->db_clk = of_clk_get(bank->of_node, 1); if (IS_ERR(bank->db_clk)) { dev_err(bank->dev, "cannot find debounce clk\n"); - clk_disable_unprepare(bank->clk); return -EINVAL; } + + ret = devm_add_action_or_reset(bank->dev, rockchip_clk_put, + bank->db_clk); + if (ret) + return ret; break; case GPIO_TYPE_V1: bank->gpio_regs = &gpio_regs_v1; @@ -751,7 +761,6 @@ static int rockchip_gpio_probe(struct platform_device *pdev) ret = rockchip_gpiolib_register(bank); if (ret) { - clk_disable_unprepare(bank->clk); mutex_unlock(&bank->deferred_lock); return ret; } @@ -792,7 +801,11 @@ static void rockchip_gpio_remove(struct platform_device *pdev) { struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); - clk_disable_unprepare(bank->clk); + irq_set_chained_handler_and_data(bank->irq, NULL, NULL); + if (bank->domain) { + irq_domain_remove_generic_chips(bank->domain); + irq_domain_remove(bank->domain); + } gpiochip_remove(&bank->gpio_chip); } diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c index 29d7d2e4dfc0..6941e4be6cf1 100644 --- a/drivers/gpio/gpio-shared-proxy.c +++ b/drivers/gpio/gpio-shared-proxy.c @@ -103,9 +103,18 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) { struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); struct gpio_shared_desc *shared_desc = proxy->shared_desc; + int ret; guard(gpio_shared_desc_lock)(shared_desc); + if (proxy->voted_high) { + ret = gpio_shared_proxy_set_unlocked(proxy, + shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0); + if (ret) + dev_err(proxy->dev, + "Failed to unset the shared GPIO value on release: %d\n", ret); + } + proxy->shared_desc->usecnt--; dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n", diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 0da2c5a45843..f0f570527cf2 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -695,9 +695,9 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, pdev = dev->pdev; if (pdev) - return sprintf(page, "%s\n", dev_name(&pdev->dev)); + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); - return sprintf(page, "gpio-sim.%d\n", dev->id); + return sysfs_emit(page, "gpio-sim.%d\n", dev->id); } CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name); @@ -711,7 +711,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page) scoped_guard(mutex, &dev->lock) live = gpio_sim_device_is_live(dev); - return sprintf(page, "%c\n", live ? '1' : '0'); + return sysfs_emit(page, "%c\n", live ? '1' : '0'); } static unsigned int gpio_sim_get_line_names_size(struct gpio_sim_bank *bank) @@ -1059,7 +1059,7 @@ static int gpio_sim_emit_chip_name(struct device *dev, void *data) return 0; if (device_match_fwnode(dev, ctx->swnode)) - return sprintf(ctx->page, "%s\n", dev_name(dev)); + return sysfs_emit(ctx->page, "%s\n", dev_name(dev)); return 0; } @@ -1077,7 +1077,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, return device_for_each_child(&dev->pdev->dev, &ctx, gpio_sim_emit_chip_name); - return sprintf(page, "none\n"); + return sysfs_emit(page, "none\n"); } CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name); @@ -1090,7 +1090,7 @@ gpio_sim_bank_config_label_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", bank->label ?: ""); + return sysfs_emit(page, "%s\n", bank->label ?: ""); } static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, @@ -1125,7 +1125,7 @@ gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%u\n", bank->num_lines); + return sysfs_emit(page, "%u\n", bank->num_lines); } static ssize_t @@ -1171,7 +1171,7 @@ gpio_sim_line_config_name_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", line->name ?: ""); + return sysfs_emit(page, "%s\n", line->name ?: ""); } static ssize_t gpio_sim_line_config_name_store(struct config_item *item, @@ -1206,7 +1206,7 @@ gpio_sim_line_config_valid_show(struct config_item *item, char *page) guard(mutex)(&dev->lock); - return sprintf(page, "%c\n", line->valid ? '1' : '0'); + return sysfs_emit(page, "%c\n", line->valid ? '1' : '0'); } static ssize_t gpio_sim_line_config_valid_store(struct config_item *item, @@ -1244,7 +1244,7 @@ static ssize_t gpio_sim_hog_config_name_show(struct config_item *item, guard(mutex)(&dev->lock); - return sprintf(page, "%s\n", hog->name ?: ""); + return sysfs_emit(page, "%s\n", hog->name ?: ""); } static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, @@ -1298,7 +1298,7 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item, return -EINVAL; } - return sprintf(page, "%s\n", repr); + return sysfs_emit(page, "%s\n", repr); } static ssize_t @@ -1338,7 +1338,7 @@ static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item, guard(mutex)(&dev->lock); - return sprintf(page, "%c\n", hog->active_low ? '1' : '0'); + return sysfs_emit(page, "%c\n", hog->active_low ? '1' : '0'); } static ssize_t diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index aa7c3e44234f..d9a2dedf50ea 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -19,6 +19,7 @@ #include <dt-bindings/gpio/tegra186-gpio.h> #include <dt-bindings/gpio/tegra194-gpio.h> #include <dt-bindings/gpio/tegra234-gpio.h> +#include <dt-bindings/gpio/nvidia,tegra238-gpio.h> #include <dt-bindings/gpio/tegra241-gpio.h> #include <dt-bindings/gpio/tegra256-gpio.h> #include <dt-bindings/gpio/nvidia,tegra264-gpio.h> @@ -1239,6 +1240,67 @@ static const struct tegra_gpio_soc tegra234_aon_soc = { .has_vm_support = false, }; +#define TEGRA238_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA238_MAIN, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra238_main_ports[] = { + TEGRA238_MAIN_GPIO_PORT(A, 0, 0, 8), + TEGRA238_MAIN_GPIO_PORT(B, 0, 1, 5), + TEGRA238_MAIN_GPIO_PORT(C, 0, 2, 8), + TEGRA238_MAIN_GPIO_PORT(D, 0, 3, 8), + TEGRA238_MAIN_GPIO_PORT(E, 0, 4, 4), + TEGRA238_MAIN_GPIO_PORT(F, 0, 5, 8), + TEGRA238_MAIN_GPIO_PORT(G, 0, 6, 8), + TEGRA238_MAIN_GPIO_PORT(H, 0, 7, 6), + TEGRA238_MAIN_GPIO_PORT(J, 1, 0, 8), + TEGRA238_MAIN_GPIO_PORT(K, 1, 1, 4), + TEGRA238_MAIN_GPIO_PORT(L, 1, 2, 8), + TEGRA238_MAIN_GPIO_PORT(M, 1, 3, 8), + TEGRA238_MAIN_GPIO_PORT(N, 1, 4, 3), + TEGRA238_MAIN_GPIO_PORT(P, 1, 5, 8), + TEGRA238_MAIN_GPIO_PORT(Q, 1, 6, 3), + TEGRA238_MAIN_GPIO_PORT(R, 2, 0, 8), + TEGRA238_MAIN_GPIO_PORT(S, 2, 1, 8), + TEGRA238_MAIN_GPIO_PORT(T, 2, 2, 8), + TEGRA238_MAIN_GPIO_PORT(U, 2, 3, 6), + TEGRA238_MAIN_GPIO_PORT(V, 2, 4, 2), + TEGRA238_MAIN_GPIO_PORT(W, 3, 0, 8), + TEGRA238_MAIN_GPIO_PORT(X, 3, 1, 2) +}; + +static const struct tegra_gpio_soc tegra238_main_soc = { + .num_ports = ARRAY_SIZE(tegra238_main_ports), + .ports = tegra238_main_ports, + .name = "tegra238-gpio", + .instance = 0, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + +#define TEGRA238_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA238_AON, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra238_aon_ports[] = { + TEGRA238_AON_GPIO_PORT(AA, 0, 0, 8), + TEGRA238_AON_GPIO_PORT(BB, 0, 1, 1), + TEGRA238_AON_GPIO_PORT(CC, 0, 2, 8), + TEGRA238_AON_GPIO_PORT(DD, 0, 3, 8), + TEGRA238_AON_GPIO_PORT(EE, 0, 4, 6), + TEGRA238_AON_GPIO_PORT(FF, 0, 5, 8), + TEGRA238_AON_GPIO_PORT(GG, 0, 6, 8), + TEGRA238_AON_GPIO_PORT(HH, 0, 7, 4) +}; + +static const struct tegra_gpio_soc tegra238_aon_soc = { + .num_ports = ARRAY_SIZE(tegra238_aon_ports), + .ports = tegra238_aon_ports, + .name = "tegra238-gpio-aon", + .instance = 1, + .num_irqs_per_bank = 8, + .has_gte = true, + .has_vm_support = false, +}; + #define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins) @@ -1333,6 +1395,7 @@ static const struct tegra_gpio_soc tegra264_aon_soc = { .name = "tegra264-gpio-aon", .instance = 1, .num_irqs_per_bank = 8, + .has_gte = true, .has_vm_support = true, }; @@ -1448,6 +1511,12 @@ static const struct of_device_id tegra186_gpio_of_match[] = { .compatible = "nvidia,tegra234-gpio-aon", .data = &tegra234_aon_soc }, { + .compatible = "nvidia,tegra238-gpio", + .data = &tegra238_main_soc + }, { + .compatible = "nvidia,tegra238-gpio-aon", + .data = &tegra238_aon_soc + }, { .compatible = "nvidia,tegra256-gpio", .data = &tegra256_main_soc }, { diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index f488939dd00a..78fe133f5d32 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -14,7 +14,6 @@ #include <linux/platform_device.h> #include <linux/irq.h> #include <linux/io.h> -#include <linux/timb_gpio.h> #include <linux/interrupt.h> #include <linux/slab.h> @@ -225,19 +224,21 @@ static int timbgpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct gpio_chip *gc; struct timbgpio *tgpio; - struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev); int irq = platform_get_irq(pdev, 0); - if (!pdata || pdata->nr_pins > 32) { - dev_err(dev, "Invalid platform data\n"); - return -EINVAL; - } - tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL); if (!tgpio) return -EINVAL; - tgpio->irq_base = pdata->irq_base; + gc = &tgpio->gpio; + + err = device_property_read_u32(dev, "irq-base", &tgpio->irq_base); + if (err) + return err; + + err = device_property_read_u32(dev, "gpio-base", &gc->base); + if (err) + return err; spin_lock_init(&tgpio->lock); @@ -245,8 +246,6 @@ static int timbgpio_probe(struct platform_device *pdev) if (IS_ERR(tgpio->membase)) return PTR_ERR(tgpio->membase); - gc = &tgpio->gpio; - gc->label = dev_name(&pdev->dev); gc->owner = THIS_MODULE; gc->parent = &pdev->dev; @@ -256,21 +255,22 @@ static int timbgpio_probe(struct platform_device *pdev) gc->set = timbgpio_gpio_set; gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; gc->dbg_show = NULL; - gc->base = pdata->gpio_base; - gc->ngpio = pdata->nr_pins; gc->can_sleep = false; err = devm_gpiochip_add_data(&pdev->dev, gc, tgpio); if (err) return err; + if (gc->ngpio > 32) + return dev_err_probe(dev, -EINVAL, "Invalid number of pins\n"); + /* make sure to disable interrupts */ iowrite32(0x0, tgpio->membase + TGPIO_IER); if (irq < 0 || tgpio->irq_base <= 0) return 0; - for (i = 0; i < pdata->nr_pins; i++) { + for (i = 0; i < gc->ngpio; i++) { irq_set_chip_and_handler(tgpio->irq_base + i, &timbgpio_irqchip, handle_simple_irq); irq_set_chip_data(tgpio->irq_base + i, tgpio); diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index 866ff2d436d5..c38538653e99 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -112,7 +112,7 @@ static int tpic2810_probe(struct i2c_client *client) } static const struct i2c_device_id tpic2810_id_table[] = { - { "tpic2810", }, + { .name = "tpic2810" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, tpic2810_id_table); diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index df770ecf28bc..f29d8485ab33 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -91,7 +91,7 @@ static int tps65086_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps65086_gpio_id_table[] = { - { "tps65086-gpio", }, + { .name = "tps65086-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 3b4c41f5ef55..bf85663349fb 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -201,7 +201,7 @@ static const struct of_device_id tps65218_dt_match[] = { MODULE_DEVICE_TABLE(of, tps65218_dt_match); static const struct platform_device_id tps65218_gpio_id_table[] = { - { "tps65218-gpio", }, + { .name = "tps65218-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 158f63bcf10c..457fd8a589e8 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -249,8 +249,8 @@ static int tps65219_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps6521x_gpio_id_table[] = { - { "tps65214-gpio", TPS65214 }, - { "tps65219-gpio", TPS65219 }, + { .name = "tps65214-gpio", .driver_data = TPS65214 }, + { .name = "tps65219-gpio", .driver_data = TPS65219 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table); diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index 7a2c5685c2fd..cf3fa49a7097 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -115,7 +115,7 @@ static int tps65912_gpio_probe(struct platform_device *pdev) } static const struct platform_device_id tps65912_gpio_id_table[] = { - { "tps65912-gpio", }, + { .name = "tps65912-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65912_gpio_id_table); diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index d9ee8fc77ccd..b46b48e56c56 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -175,7 +175,7 @@ static int ts4900_gpio_probe(struct i2c_client *client) } static const struct i2c_device_id ts4900_gpio_id_table[] = { - { "ts4900-gpio", }, + { .name = "ts4900-gpio" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table); diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c deleted file mode 100644 index 3c7f2efe10fd..000000000000 --- a/drivers/gpio/gpio-ts5500.c +++ /dev/null @@ -1,446 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Digital I/O driver for Technologic Systems TS-5500 - * - * Copyright (c) 2012 Savoir-faire Linux Inc. - * Vivien Didelot <vivien.didelot@savoirfairelinux.com> - * - * Technologic Systems platforms have pin blocks, exposing several Digital - * Input/Output lines (DIO). This driver aims to support single pin blocks. - * In that sense, the support is not limited to the TS-5500 blocks. - * Actually, the following platforms have DIO support: - * - * TS-5500: - * Documentation: https://docs.embeddedts.com/TS-5500 - * Blocks: DIO1, DIO2 and LCD port. - * - * TS-5600: - * Documentation: https://docs.embeddedts.com/TS-5600 - * Blocks: LCD port (identical to TS-5500 LCD). - */ - -#include <linux/bitops.h> -#include <linux/gpio/driver.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -/* List of supported Technologic Systems platforms DIO blocks */ -enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD }; - -struct ts5500_priv { - const struct ts5500_dio *pinout; - struct gpio_chip gpio_chip; - spinlock_t lock; - bool strap; - u8 hwirq; -}; - -/* - * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port). - * This flag ensures that the region has been requested by this driver. - */ -static bool hex7d_reserved; - -/* - * This structure is used to describe capabilities of DIO lines, - * such as available directions and connected interrupt (if any). - */ -struct ts5500_dio { - const u8 value_addr; - const u8 value_mask; - const u8 control_addr; - const u8 control_mask; - const bool no_input; - const bool no_output; - const u8 irq; -}; - -#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \ - { \ - .value_addr = vaddr, \ - .value_mask = BIT(vbit), \ - .control_addr = caddr, \ - .control_mask = BIT(cbit), \ - } - -#define TS5500_DIO_IN(addr, bit) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_output = true, \ - } - -#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_output = true, \ - .irq = _irq, \ - } - -#define TS5500_DIO_OUT(addr, bit) \ - { \ - .value_addr = addr, \ - .value_mask = BIT(bit), \ - .no_input = true, \ - } - -/* - * Input/Output DIO lines are programmed in groups of 4. Their values are - * available through 4 consecutive bits in a value port, whereas the direction - * of these 4 lines is driven by only 1 bit in a control port. - */ -#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \ - TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit) - -/* - * TS-5500 DIO1 block - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x7b 0 0x7a 0 x x DIO1_0 1 0 - * 0x7b 1 0x7a 0 x x DIO1_1 3 1 - * 0x7b 2 0x7a 0 x x DIO1_2 5 2 - * 0x7b 3 0x7a 0 x x DIO1_3 7 3 - * 0x7b 4 0x7a 1 x x DIO1_4 9 4 - * 0x7b 5 0x7a 1 x x DIO1_5 11 5 - * 0x7b 6 0x7a 1 x x DIO1_6 13 6 - * 0x7b 7 0x7a 1 x x DIO1_7 15 7 - * 0x7c 0 0x7a 5 x x DIO1_8 4 8 - * 0x7c 1 0x7a 5 x x DIO1_9 6 9 - * 0x7c 2 0x7a 5 x x DIO1_10 8 10 - * 0x7c 3 0x7a 5 x x DIO1_11 10 11 - * 0x7c 4 x DIO1_12 12 12 - * 0x7c 5 x 7 DIO1_13 14 13 - */ -static const struct ts5500_dio ts5500_dio1[] = { - TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0), - TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1), - TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5), - TS5500_DIO_IN(0x7c, 4), - TS5500_DIO_IN_IRQ(0x7c, 5, 7), -}; - -/* - * TS-5500 DIO2 block - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x7e 0 0x7d 0 x x DIO2_0 1 0 - * 0x7e 1 0x7d 0 x x DIO2_1 3 1 - * 0x7e 2 0x7d 0 x x DIO2_2 5 2 - * 0x7e 3 0x7d 0 x x DIO2_3 7 3 - * 0x7e 4 0x7d 1 x x DIO2_4 9 4 - * 0x7e 5 0x7d 1 x x DIO2_5 11 5 - * 0x7e 6 0x7d 1 x x DIO2_6 13 6 - * 0x7e 7 0x7d 1 x x DIO2_7 15 7 - * 0x7f 0 0x7d 5 x x DIO2_8 4 8 - * 0x7f 1 0x7d 5 x x DIO2_9 6 9 - * 0x7f 2 0x7d 5 x x DIO2_10 8 10 - * 0x7f 3 0x7d 5 x x DIO2_11 10 11 - * 0x7f 4 x 6 DIO2_13 14 12 - */ -static const struct ts5500_dio ts5500_dio2[] = { - TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0), - TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1), - TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5), - TS5500_DIO_IN_IRQ(0x7f, 4, 6), -}; - -/* - * TS-5500 LCD port used as DIO block - * TS-5600 LCD port is identical - * - * value control dir hw - * addr bit addr bit in out irq name pin offset - * - * 0x72 0 0x7d 2 x x LCD_0 8 0 - * 0x72 1 0x7d 2 x x LCD_1 7 1 - * 0x72 2 0x7d 2 x x LCD_2 10 2 - * 0x72 3 0x7d 2 x x LCD_3 9 3 - * 0x72 4 0x7d 3 x x LCD_4 12 4 - * 0x72 5 0x7d 3 x x LCD_5 11 5 - * 0x72 6 0x7d 3 x x LCD_6 14 6 - * 0x72 7 0x7d 3 x x LCD_7 13 7 - * 0x73 0 x LCD_EN 5 8 - * 0x73 6 x LCD_WR 6 9 - * 0x73 7 x 1 LCD_RS 3 10 - */ -static const struct ts5500_dio ts5500_lcd[] = { - TS5500_DIO_GROUP(0x72, 0, 0x7d, 2), - TS5500_DIO_GROUP(0x72, 4, 0x7d, 3), - TS5500_DIO_OUT(0x73, 0), - TS5500_DIO_IN(0x73, 6), - TS5500_DIO_IN_IRQ(0x73, 7, 1), -}; - -static inline void ts5500_set_mask(u8 mask, u8 addr) -{ - u8 val = inb(addr); - val |= mask; - outb(val, addr); -} - -static inline void ts5500_clear_mask(u8 mask, u8 addr) -{ - u8 val = inb(addr); - val &= ~mask; - outb(val, addr); -} - -static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - if (line.no_input) - return -ENXIO; - - if (line.no_output) - return 0; - - spin_lock_irqsave(&priv->lock, flags); - ts5500_clear_mask(line.control_mask, line.control_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - - return !!(inb(line.value_addr) & line.value_mask); -} - -static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - if (line.no_output) - return -ENXIO; - - spin_lock_irqsave(&priv->lock, flags); - if (!line.no_input) - ts5500_set_mask(line.control_mask, line.control_addr); - - if (val) - ts5500_set_mask(line.value_mask, line.value_addr); - else - ts5500_clear_mask(line.value_mask, line.value_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio line = priv->pinout[offset]; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (val) - ts5500_set_mask(line.value_mask, line.value_addr); - else - ts5500_clear_mask(line.value_mask, line.value_addr); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct ts5500_priv *priv = gpiochip_get_data(chip); - const struct ts5500_dio *block = priv->pinout; - const struct ts5500_dio line = block[offset]; - - /* Only one pin is connected to an interrupt */ - if (line.irq) - return line.irq; - - /* As this pin is input-only, we may strap it to another in/out pin */ - if (priv->strap) - return priv->hwirq; - - return -ENXIO; -} - -static int ts5500_enable_irq(struct ts5500_priv *priv) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->hwirq == 7) - ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */ - else if (priv->hwirq == 6) - ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */ - else if (priv->hwirq == 1) - ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */ - else - ret = -EINVAL; - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -static void ts5500_disable_irq(struct ts5500_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->hwirq == 7) - ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */ - else if (priv->hwirq == 6) - ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */ - else if (priv->hwirq == 1) - ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */ - else - dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n", - priv->hwirq); - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int ts5500_dio_probe(struct platform_device *pdev) -{ - enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data; - struct device *dev = &pdev->dev; - const char *name = dev_name(dev); - struct ts5500_priv *priv; - unsigned long flags; - int ret; - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - - priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_set_drvdata(pdev, priv); - priv->hwirq = ret; - spin_lock_init(&priv->lock); - - priv->gpio_chip.owner = THIS_MODULE; - priv->gpio_chip.label = name; - priv->gpio_chip.parent = dev; - priv->gpio_chip.direction_input = ts5500_gpio_input; - priv->gpio_chip.direction_output = ts5500_gpio_output; - priv->gpio_chip.get = ts5500_gpio_get; - priv->gpio_chip.set = ts5500_gpio_set; - priv->gpio_chip.to_irq = ts5500_gpio_to_irq; - priv->gpio_chip.base = -1; - - switch (block) { - case TS5500_DIO1: - priv->pinout = ts5500_dio1; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1); - - if (!devm_request_region(dev, 0x7a, 3, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - break; - case TS5500_DIO2: - priv->pinout = ts5500_dio2; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2); - - if (!devm_request_region(dev, 0x7e, 2, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - - if (hex7d_reserved) - break; - - if (!devm_request_region(dev, 0x7d, 1, name)) { - dev_err(dev, "failed to request %s 7D\n", name); - return -EBUSY; - } - - hex7d_reserved = true; - break; - case TS5500_LCD: - case TS5600_LCD: - priv->pinout = ts5500_lcd; - priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd); - - if (!devm_request_region(dev, 0x72, 2, name)) { - dev_err(dev, "failed to request %s ports\n", name); - return -EBUSY; - } - - if (!hex7d_reserved) { - if (!devm_request_region(dev, 0x7d, 1, name)) { - dev_err(dev, "failed to request %s 7D\n", name); - return -EBUSY; - } - - hex7d_reserved = true; - } - - /* Ensure usage of LCD port as DIO */ - spin_lock_irqsave(&priv->lock, flags); - ts5500_clear_mask(BIT(4), 0x7d); - spin_unlock_irqrestore(&priv->lock, flags); - break; - } - - ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); - if (ret) { - dev_err(dev, "failed to register the gpio chip\n"); - return ret; - } - - ret = ts5500_enable_irq(priv); - if (ret) { - dev_err(dev, "invalid interrupt %d\n", priv->hwirq); - return ret; - } - - return 0; -} - -static void ts5500_dio_remove(struct platform_device *pdev) -{ - struct ts5500_priv *priv = platform_get_drvdata(pdev); - - ts5500_disable_irq(priv); -} - -static const struct platform_device_id ts5500_dio_ids[] = { - { "ts5500-dio1", TS5500_DIO1 }, - { "ts5500-dio2", TS5500_DIO2 }, - { "ts5500-dio-lcd", TS5500_LCD }, - { "ts5600-dio-lcd", TS5600_LCD }, - { } -}; -MODULE_DEVICE_TABLE(platform, ts5500_dio_ids); - -static struct platform_driver ts5500_dio_driver = { - .driver = { - .name = "ts5500-dio", - }, - .probe = ts5500_dio_probe, - .remove = ts5500_dio_remove, - .id_table = ts5500_dio_ids, -}; - -module_platform_driver(ts5500_dio_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); -MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver"); diff --git a/drivers/gpio/gpio-usbio.c b/drivers/gpio/gpio-usbio.c index 34d42c743d5b..489c8ac6299e 100644 --- a/drivers/gpio/gpio-usbio.c +++ b/drivers/gpio/gpio-usbio.c @@ -31,6 +31,7 @@ static const struct acpi_device_id usbio_gpio_acpi_hids[] = { { "INTC10B5" }, /* LNL */ { "INTC10D1" }, /* MTL-CVF */ { "INTC10E2" }, /* PTL */ + { "INTC1116" }, /* NVL */ { } }; diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index 128520d340d4..846f8688fec5 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -397,7 +397,7 @@ static ssize_t gpio_virtuser_direction_do_write(struct file *file, char buf[32], *trimmed; int ret, dir, val = 0; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); @@ -622,7 +622,7 @@ static ssize_t gpio_virtuser_consumer_write(struct file *file, char buf[GPIO_VIRTUSER_NAME_BUF_LEN + 2]; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, GPIO_VIRTUSER_NAME_BUF_LEN, ppos, diff --git a/drivers/gpio/gpio-waveshare-dsi.c b/drivers/gpio/gpio-waveshare-dsi.c new file mode 100644 index 000000000000..38f52351bb58 --- /dev/null +++ b/drivers/gpio/gpio-waveshare-dsi.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Waveshare International Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +/* I2C registers of the microcontroller. */ +#define REG_TP 0x94 +#define REG_LCD 0x95 +#define REG_PWM 0x96 +#define REG_SIZE 0x97 +#define REG_ID 0x98 +#define REG_VERSION 0x99 + +enum { + GPIO_AVDD = 0, + GPIO_PANEL_RESET = 1, + GPIO_BL_ENABLE = 2, + GPIO_IOVCC = 4, + GPIO_VCC = 8, + GPIO_TS_RESET = 9, +}; + +#define NUM_GPIO 16 + +struct waveshare_gpio { + struct mutex dir_lock; + struct mutex pwr_lock; + struct regmap *regmap; + u16 poweron_state; + + struct gpio_chip gc; +}; + +static const struct regmap_config waveshare_gpio_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_VERSION, +}; + +static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset) +{ + u16 pwr_state; + + guard(mutex)(&state->pwr_lock); + pwr_state = state->poweron_state & BIT(offset); + + return !!pwr_state; +} + +static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value) +{ + u16 last_val; + int err; + + guard(mutex)(&state->pwr_lock); + + last_val = state->poweron_state; + if (value) + last_val |= BIT(offset); + else + last_val &= ~BIT(offset); + + state->poweron_state = last_val; + + err = regmap_write(state->regmap, REG_TP, last_val >> 8); + if (!err) + err = regmap_write(state->regmap, REG_LCD, last_val & 0xff); + + return err; +} + +static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct waveshare_gpio *state = gpiochip_get_data(gc); + + return waveshare_gpio_get(state, offset); +} + +static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct waveshare_gpio *state = gpiochip_get_data(gc); + + return waveshare_gpio_set(state, offset, value); +} + +static int waveshare_gpio_update_status(struct backlight_device *bl) +{ + struct waveshare_gpio *state = bl_get_data(bl); + int brightness = backlight_get_brightness(bl); + + waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness); + + return regmap_write(state->regmap, REG_PWM, brightness); +} + +static const struct backlight_ops waveshare_gpio_bl = { + .update_status = waveshare_gpio_update_status, +}; + +static int waveshare_gpio_probe(struct i2c_client *i2c) +{ + struct backlight_properties props = {}; + struct waveshare_gpio *state; + struct device *dev = &i2c->dev; + struct backlight_device *bl; + struct regmap *regmap; + unsigned int data; + int ret; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + ret = devm_mutex_init(dev, &state->dir_lock); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &state->pwr_lock); + if (ret) + return ret; + + regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n"); + + state->regmap = regmap; + i2c_set_clientdata(i2c, state); + + ret = regmap_read(regmap, REG_ID, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data); + + ret = regmap_read(regmap, REG_SIZE, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel size = %d\n", data); + + ret = regmap_read(regmap, REG_VERSION, &data); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read register\n"); + + dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data); + + ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1); + if (ret) + return dev_err_probe(dev, ret, "Failed to program GPIOs\n"); + + msleep(20); + + state->gc.parent = dev; + state->gc.label = i2c->name; + state->gc.owner = THIS_MODULE; + state->gc.base = -1; + state->gc.ngpio = NUM_GPIO; + + /* it is output only */ + state->gc.get = waveshare_gpio_gpio_get; + state->gc.set = waveshare_gpio_gpio_set; + state->gc.get_direction = waveshare_gpio_gpio_get_direction; + state->gc.can_sleep = true; + + ret = devm_gpiochip_add_data(dev, &state->gc, state); + if (ret) + return dev_err_probe(dev, ret, "Failed to create gpiochip\n"); + + props.type = BACKLIGHT_RAW; + props.max_brightness = 255; + props.brightness = 255; + bl = devm_backlight_device_register(dev, dev_name(dev), dev, state, + &waveshare_gpio_bl, &props); + return PTR_ERR_OR_ZERO(bl); +} + +static const struct of_device_id waveshare_gpio_dt_ids[] = { + { .compatible = "waveshare,dsi-touch-gpio" }, + {}, +}; +MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids); + +static struct i2c_driver waveshare_gpio_regulator_driver = { + .driver = { + .name = "waveshare-regulator", + .of_match_table = of_match_ptr(waveshare_gpio_dt_ids), + }, + .probe = waveshare_gpio_probe, +}; + +module_i2c_driver(waveshare_gpio_regulator_driver); + +MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index be4b4d730547..532205175827 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -495,13 +495,11 @@ static void xgpio_irqhandler(struct irq_desc *desc) xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, hw); - bitmap_complement(rising, chip->last_irq_read, 64); - bitmap_and(rising, rising, hw, 64); + bitmap_andnot(rising, hw, chip->last_irq_read, 64); bitmap_and(rising, rising, chip->enable, 64); bitmap_and(rising, rising, chip->rising_edge, 64); - bitmap_complement(falling, hw, 64); - bitmap_and(falling, falling, chip->last_irq_read, 64); + bitmap_andnot(falling, chip->last_irq_read, hw, 64); bitmap_and(falling, falling, chip->enable, 64); bitmap_and(falling, falling, chip->falling_edge, 64); diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index 29375bea2289..af0158522ac5 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -64,14 +64,14 @@ static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, unsigned port_offset) { unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; - return readl(IOMEM(c->regs + section_offset + port_offset)); + return readl(c->regs + section_offset + port_offset); } static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, unsigned port_offset, u32 val) { unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; - writel(val, IOMEM(c->regs + section_offset + port_offset)); + writel(val, c->regs + section_offset + port_offset); } /* Functions for struct gpio_chip */ diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 571e366624d2..15a79d9a2e9e 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -25,6 +25,7 @@ #define VERSAL_GPIO_MAX_BANK 4 #define PMC_GPIO_MAX_BANK 5 #define VERSAL_UNUSED_BANKS 2 +#define EIO_GPIO_MAX_BANK 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 @@ -818,6 +819,16 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL) }; +static const struct zynq_platform_data eio_gpio_def = { + .label = "eio_gpio", + .ngpio = 52, + .max_bank = EIO_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[1] = 26, + .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */ +}; + static const struct zynq_platform_data versal_gpio_def = { .label = "versal_gpio", .quirks = GPIO_QUIRK_VERSAL, @@ -882,6 +893,7 @@ static const struct of_device_id zynq_gpio_of_match[] = { { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def }, { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def }, + { .compatible = "xlnx,eio-gpio-1.0", .data = &eio_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); @@ -1014,6 +1026,7 @@ static void zynq_gpio_remove(struct platform_device *pdev) gpiochip_remove(&gpio->chip); device_set_wakeup_capable(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } static struct platform_driver zynq_gpio_driver = { diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 09f860200a05..fbd6945726bf 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -142,10 +142,6 @@ static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin) if (!gdev) return ERR_PTR(-EPROBE_DEFER); - /* - * FIXME: keep track of the reference to the GPIO device somehow - * instead of putting it here. - */ return gpio_device_get_desc(gdev, pin); } diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 72422c5db364..2ec825ffab7d 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -353,6 +353,13 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo { int ret; + /* + * We are passing the devres device here so if the user did not pass + * another parent, it's this one. + */ + if (!gc->parent) + gc->parent = dev; + ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key); if (ret < 0) return ret; diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c new file mode 100644 index 000000000000..380b68f879e5 --- /dev/null +++ b/drivers/gpio/gpiolib-kunit.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#include <linux/fwnode.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> +#include <linux/gpio/property.h> +#include <linux/notifier.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +#include <kunit/platform_device.h> +#include <kunit/test.h> + +#define GPIO_TEST_PROVIDER "gpio-test-provider" +#define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer" +#define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer" + +static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + return 0; +} + +static int gpio_test_provider_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_chip *gc; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->base = -1; + gc->ngpio = 4; + gc->label = "gpio-swnode-consumer-test-device"; + gc->parent = dev; + gc->owner = THIS_MODULE; + + gc->get_direction = gpio_test_provider_get_direction; + gc->set = gpio_test_provider_set; + + return devm_gpiochip_add_data(dev, gc, NULL); +} + +static struct platform_driver gpio_test_provider_driver = { + .probe = gpio_test_provider_probe, + .driver = { + .name = GPIO_TEST_PROVIDER, + }, +}; + +static const struct software_node gpio_test_provider_swnode = { + .name = "gpio-test-provider-primary", +}; + +struct gpio_swnode_consumer_pdata { + bool gpio_ok; +}; + +static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = { + .gpio_ok = false, +}; + +static int gpio_swnode_consumer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_swnode_consumer_pdata *pdata = dev_get_platdata(dev); + struct gpio_desc *desc; + + desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + pdata->gpio_ok = true; + + return 0; +} + +static struct platform_driver gpio_swnode_consumer_driver = { + .probe = gpio_swnode_consumer_probe, + .driver = { + .name = GPIO_SWNODE_TEST_CONSUMER, + }, +}; + +static void gpio_swnode_lookup_by_primary(struct kunit *test) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *pdev; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_SWNODE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_swnode_pdata_template, + .size_data = sizeof(gpio_swnode_pdata_template), + .properties = properties, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound = device_is_bound(&pdev->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +static void gpio_swnode_lookup_by_secondary(struct kunit *test) +{ + struct gpio_swnode_consumer_pdata *pdata; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct fwnode_handle *primary; + struct platform_device *pdev; + bool bound = false; + int ret; + + /* + * Can't live on the stack as it will still get referenced in cleanup + * path after this function returns. + */ + primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode_init(primary, NULL); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .fwnode = primary, + .swnode = &gpio_test_provider_swnode, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_SWNODE_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .data = &gpio_swnode_pdata_template, + .size_data = sizeof(gpio_swnode_pdata_template), + .properties = properties, + }; + + pdev = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound = device_is_bound(&pdev->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + pdata = dev_get_platdata(&pdev->dev); + KUNIT_ASSERT_TRUE(test, pdata->gpio_ok); +} + +static struct kunit_case gpio_swnode_lookup_tests[] = { + KUNIT_CASE(gpio_swnode_lookup_by_primary), + KUNIT_CASE(gpio_swnode_lookup_by_secondary), + { } +}; + +static struct kunit_suite gpio_swnode_lookup_test_suite = { + .name = "gpio-swnode-lookup", + .test_cases = gpio_swnode_lookup_tests, +}; + +static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier); + +struct gpio_unbind_consumer_drvdata { + struct device *dev; + struct gpio_desc *desc; + struct notifier_block nb; + int set_retval; +}; + +static int gpio_unbind_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct gpio_unbind_consumer_drvdata *drvdata = + container_of(nb, struct gpio_unbind_consumer_drvdata, nb); + struct device *dev = data; + + if (dev != drvdata->dev) + return NOTIFY_DONE; + + drvdata->set_retval = gpiod_set_value_cansleep(drvdata->desc, 0); + + return NOTIFY_OK; +} + +static void gpio_unbind_unregister_notifier(void *data) +{ + struct notifier_block *nb = data; + + blocking_notifier_chain_unregister(&gpio_unbind_notifier, nb); +} + +static int gpio_unbind_consumer_probe(struct platform_device *pdev) +{ + struct gpio_unbind_consumer_drvdata *data; + struct device *dev = &pdev->dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + + data->desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH); + if (IS_ERR(data->desc)) + return PTR_ERR(data->desc); + + data->nb.notifier_call = gpio_unbind_notify; + ret = blocking_notifier_chain_register(&gpio_unbind_notifier, &data->nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, gpio_unbind_unregister_notifier, &data->nb); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + return 0; +} + +static struct platform_driver gpio_unbind_consumer_driver = { + .probe = gpio_unbind_consumer_probe, + .driver = { + .name = GPIO_UNBIND_TEST_CONSUMER, + }, +}; + +static void gpio_unbind_with_consumers(struct kunit *test) +{ + struct gpio_unbind_consumer_drvdata *cons_data; + struct platform_device_info pdevinfo; + struct property_entry properties[2]; + struct platform_device *prvd, *cons; + bool bound = false; + int ret; + + ret = kunit_platform_driver_register(test, &gpio_test_provider_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_driver_register(test, &gpio_unbind_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdevinfo = (struct platform_device_info){ + .name = GPIO_TEST_PROVIDER, + .id = PLATFORM_DEVID_NONE, + .swnode = &gpio_test_provider_swnode, + }; + + prvd = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); + + properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios", + &gpio_test_provider_swnode, + 0, GPIO_ACTIVE_HIGH); + properties[1] = (struct property_entry){ }; + + pdevinfo = (struct platform_device_info){ + .name = GPIO_UNBIND_TEST_CONSUMER, + .id = PLATFORM_DEVID_NONE, + .properties = properties, + }; + + cons = kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + wait_for_device_probe(); + scoped_guard(device, &cons->dev) + bound = device_is_bound(&cons->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + kunit_platform_device_unregister(test, prvd); + + ret = blocking_notifier_call_chain(&gpio_unbind_notifier, 0, &cons->dev); + KUNIT_ASSERT_EQ(test, ret, NOTIFY_OK); + + scoped_guard(device, &cons->dev) { + cons_data = platform_get_drvdata(cons); + ret = cons_data->set_retval; + } + + KUNIT_ASSERT_EQ(test, ret, -ENODEV); +} + +static struct kunit_case gpio_unbind_with_consumers_tests[] = { + KUNIT_CASE(gpio_unbind_with_consumers), + { } +}; + +static struct kunit_suite gpio_unbind_with_consumers_test_suite = { + .name = "gpio-unbind-with-consumers", + .test_cases = gpio_unbind_with_consumers_tests, +}; + +kunit_test_suites( + &gpio_swnode_lookup_test_suite, + &gpio_unbind_with_consumers_test_suite, +); + +MODULE_DESCRIPTION("Test module for the GPIO subsystem"); +MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2c923d17541f..940b566946ce 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -241,6 +241,14 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np, */ { "ti,tsc2005", "reset-gpios", false }, #endif +#if IS_ENABLED(CONFIG_SND_SOC_WSA881X) + /* + * WSA881 powerdown is always active low, but some device trees + * missed this when first contributed. It also has a very strange + * compatible. + */ + { "sdw10217201000", "powerdown-gpios", false }, +#endif }; unsigned int i; @@ -1066,11 +1074,6 @@ int of_gpiochip_add(struct gpio_chip *chip) of_node_get(np); - for_each_available_child_of_node_scoped(np, child) { - if (of_property_read_bool(child, "gpio-hog")) - of_node_set_flag(child, OF_POPULATED); - } - return ret; } diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index e02d6b93a4ab..de72776fb154 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -53,7 +53,7 @@ struct gpio_shared_entry { unsigned int offset; /* Index in the property value array. */ size_t index; - /* Synchronizes the modification of shared_desc. */ + /* Synchronizes the modification of shared_desc and offset. */ struct mutex lock; struct gpio_shared_desc *shared_desc; struct kref ref; @@ -598,16 +598,13 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) struct gpio_shared_ref *ref; list_for_each_entry(entry, &gpio_shared_list, list) { - guard(mutex)(&entry->lock); - if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; - gpiod_free_commit(&gdev->descs[entry->offset]); + scoped_guard(mutex, &entry->lock) + gpiod_free_commit(&gdev->descs[entry->offset]); list_for_each_entry(ref, &entry->refs, list) { - guard(mutex)(&ref->lock); - if (ref->lookup) { gpiod_remove_lookup_table(ref->lookup); kfree(ref->lookup->table[0].key); diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index 4374067f621e..8d9591aa9304 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -114,10 +114,6 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode, if (IS_ERR(gdev)) return ERR_CAST(gdev); - /* - * FIXME: The GPIO device reference is put at return but the descriptor - * is passed on. Find a proper solution. - */ desc = gpio_device_get_desc(gdev, args.args[0]); *flags = args.args[1]; /* We expect native GPIO flags */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1e6dce430dca..8a5ff78a1149 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -55,7 +55,7 @@ /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); -static dev_t gpio_devt; +static dev_t gpio_devt __ro_after_init; #define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */ static int gpio_bus_match(struct device *dev, const struct device_driver *drv) @@ -114,7 +114,7 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc); static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc); static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc); -static bool gpiolib_initialized; +static bool gpiolib_initialized __ro_after_init; const char *gpiod_get_label(struct gpio_desc *desc) { @@ -143,13 +143,15 @@ static void desc_free_label(struct rcu_head *rh) static int desc_set_label(struct gpio_desc *desc, const char *label) { struct gpio_desc_label *new = NULL, *old; + size_t len; if (label) { - new = kzalloc_flex(*new, str, strlen(label) + 1); + len = strlen(label); + new = kzalloc_flex(*new, str, len + 1); if (!new) return -ENOMEM; - strcpy(new->str, label); + memcpy(new->str, label, len); } old = rcu_replace_pointer(desc->label, new, 1); @@ -491,6 +493,28 @@ int gpiod_get_direction(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_get_direction); +/** + * gpiod_is_single_ended - check if the GPIO is configured as single-ended + * @desc: the GPIO descriptor to check + * + * Returns true if the GPIO is configured as either Open Drain or Open Source. + * In these modes, the direction of the line cannot always be reliably + * determined by reading hardware registers, as the "off" state (High-Z) + * is physically indistinguishable from an input state. + */ +bool gpiod_is_single_ended(struct gpio_desc *desc) +{ + if (!desc) + return false; + + if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags) || + test_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(gpiod_is_single_ended); + /* * Add a new chip to the global chips list, keeping the list of chips sorted * by range(means [base, base + ngpio - 1]) order. @@ -1031,9 +1055,17 @@ static int gpiochip_hog_lines(struct gpio_chip *gc) if (!fwnode_property_present(fwnode, "gpio-hog")) continue; + /* The hog may have been handled by another gpio_chip on the same fwnode */ + if (is_of_node(fwnode) && + of_node_check_flag(to_of_node(fwnode), OF_POPULATED)) + continue; + ret = gpiochip_add_hog(gc, fwnode); if (ret) return ret; + + if (is_of_node(fwnode)) + of_node_set_flag(to_of_node(fwnode), OF_POPULATED); } return 0; @@ -1291,7 +1323,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiochip_hog_lines(gc); if (ret) - goto err_remove_of_chip; + goto err_free_hogs; ret = gpiochip_irqchip_init_valid_mask(gc); if (ret) @@ -3406,20 +3438,13 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) { - struct gpio_device *gdev; - struct gpio_chip *gc; int value; - /* FIXME Unable to use gpio_chip_guard due to const desc. */ - gdev = desc->gdev; - - guard(srcu)(&gdev->srcu); - - gc = srcu_dereference(gdev->chip, &gdev->srcu); - if (!gc) + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) return -ENODEV; - value = gpio_chip_get_value(gc, desc); + value = gpio_chip_get_value(guard.gc, desc); value = value < 0 ? value : !!value; trace_gpio_value(desc_to_gpio(desc), 1, value); return value; @@ -4126,8 +4151,6 @@ EXPORT_SYMBOL_GPL(gpiod_is_shared); */ int gpiod_to_irq(const struct gpio_desc *desc) { - struct gpio_device *gdev; - struct gpio_chip *gc; int offset; int ret; @@ -4135,16 +4158,13 @@ int gpiod_to_irq(const struct gpio_desc *desc) if (ret <= 0) return -EINVAL; - gdev = desc->gdev; - /* FIXME Cannot use gpio_chip_guard due to const desc. */ - guard(srcu)(&gdev->srcu); - gc = srcu_dereference(gdev->chip, &gdev->srcu); - if (!gc) + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) return -ENODEV; offset = gpiod_hwgpio(desc); - if (gc->to_irq) { - ret = gc->to_irq(gc, offset); + if (guard.gc->to_irq) { + ret = guard.gc->to_irq(guard.gc, offset); if (ret) return ret; @@ -4152,7 +4172,7 @@ int gpiod_to_irq(const struct gpio_desc *desc) return -ENXIO; } #ifdef CONFIG_GPIOLIB_IRQCHIP - if (gc->irq.chip) { + if (guard.gc->irq.chip) { /* * Avoid race condition with other code, which tries to lookup * an IRQ before the irqchip has been properly registered, @@ -5340,7 +5360,7 @@ EXPORT_SYMBOL_GPL(gpiod_put_array); * gpio_device of the GPIO chip with the firmware node and then simply * bind it to this stub driver. */ -static struct device_driver gpio_stub_drv = { +static struct device_driver gpio_stub_drv __ro_after_init = { .name = "gpio_stub_drv", .bus = &gpio_bus_type, }; @@ -5498,8 +5518,8 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) if (gc->label) seq_printf(s, ", %s", gc->label); if (gc->can_sleep) - seq_printf(s, ", can sleep"); - seq_printf(s, ":\n"); + seq_puts(s, ", can sleep"); + seq_puts(s, ":\n"); if (gc->dbg_show) gc->dbg_show(s, gc); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index dc4cb61a9318..650a702741df 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -244,7 +244,7 @@ DEFINE_CLASS(gpio_chip_guard, _guard; }), - struct gpio_desc *desc) + const struct gpio_desc *desc) int gpiod_request(struct gpio_desc *desc, const char *label); int gpiod_request_commit(struct gpio_desc *desc, const char *label); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index b24d5d21be5f..583fd67a2c64 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1277,6 +1277,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, { struct amdgpu_fpriv *fpriv = p->filp->driver_priv; struct amdgpu_job *leader = p->gang_leader; + struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry *e; struct drm_gem_object *gobj; unsigned long index; @@ -1322,7 +1323,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, amdgpu_hmm_range_free(e->range); e->range = NULL; } - if (r) { + + if (r || !list_empty(&vm->invalidated)) { r = -EAGAIN; mutex_unlock(&p->adev->notifier_lock); return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index b6f849d51c2e..c4c21dbbbdbf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -394,7 +394,8 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, uint64_t start_page, uint64_t num_pages, uint64_t flags, void *dst) { - u32 i, idx; + u32 i, j, t, idx; + u64 page_base; /* The SYSTEM flag indicates the pages aren't in VRAM. */ WARN_ON_ONCE(flags & AMDGPU_PTE_SYSTEM); @@ -402,9 +403,12 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, if (!drm_dev_enter(adev_to_drm(adev), &idx)) return; - for (i = 0; i < num_pages; ++i) { - amdgpu_gmc_set_pte_pde(adev, dst, - start_page + i, pa + AMDGPU_GPU_PAGE_SIZE * i, flags); + page_base = pa; + for (i = 0, t = 0; i < num_pages; i++) { + for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) { + amdgpu_gmc_set_pte_pde(adev, dst, start_page + t, page_base, flags); + page_base += AMDGPU_GPU_PAGE_SIZE; + } } drm_dev_exit(idx); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 123d4a09114d..fe6d988e7f24 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1093,9 +1093,16 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * If that number is larger than the size of the array, the ioctl must * be retried. */ + if (args->num_entries > INT_MAX / sizeof(*vm_entries)) { + r = -EINVAL; + goto out_exec; + } + vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); - if (!vm_entries) - return -ENOMEM; + if (!vm_entries) { + r = -ENOMEM; + goto out_exec; + } amdgpu_vm_bo_va_for_each_valid_mapping(bo_va, mapping) { if (num_mappings < args->num_entries) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 3d9497d121ca..c076c5f06e77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -170,7 +170,7 @@ int amdgpu_gmc_set_pte_pde(struct amdgpu_device *adev, void *cpu_pt_addr, /* * The following is for PTE only. GART does not have PDEs. */ - value = addr & 0x0000FFFFFFFFF000ULL; + value = addr & adev->gmc.pte_addr_mask; value |= flags; writeq(value, ptr + (gpu_page_idx * 8)); @@ -1003,7 +1003,7 @@ void amdgpu_gmc_noretry_set(struct amdgpu_device *adev) gc_ver == IP_VERSION(9, 4, 3) || gc_ver == IP_VERSION(9, 4, 4) || gc_ver == IP_VERSION(9, 5, 0) || - gc_ver >= IP_VERSION(10, 3, 0)); + gc_ver >= IP_VERSION(10, 1, 0)); if (!amdgpu_sriov_xnack_support(adev)) gmc->noretry = 1; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index 6ab4c1e297fc..d03536b969b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -280,6 +280,7 @@ struct amdgpu_gmc { u64 real_vram_size; int vram_mtrr; u64 mc_mask; + uint64_t pte_addr_mask; const struct firmware *fw; /* MC firmware */ uint32_t fw_version; struct amdgpu_irq_src vm_fault; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index a5d26b943f6d..d23a91d029aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -203,7 +203,7 @@ int amdgpu_gtt_mgr_alloc_entries(struct amdgpu_gtt_mgr *mgr, int r; /* Align to TLB L2 cache entry size to work around "V bit HW bug" */ - if (adev->asic_type == CHIP_TAHITI) { + if (adev->family == AMDGPU_FAMILY_SI) { alignment = 32 * 1024 / AMDGPU_GPU_PAGE_SIZE; num_pages = ALIGN(num_pages, alignment); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index f72990ac046e..e452444b33b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -51,8 +51,6 @@ #include "amdgpu_amdkfd.h" #include "amdgpu_hmm.h" -#define MAX_WALK_BYTE (2UL << 30) - /** * amdgpu_hmm_invalidate_gfx - callback to notify about mm change * @@ -69,6 +67,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, { struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + struct amdgpu_bo *vm_root = bo->vm_bo->vm->root.bo; long r; if (!mmu_notifier_range_blockable(range)) @@ -78,8 +77,10 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); - r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, - false, MAX_SCHEDULE_TIMEOUT); + amdgpu_vm_bo_invalidate(bo, false); + r = dma_resv_wait_timeout(vm_root->tbo.base.resv, + DMA_RESV_USAGE_BOOKKEEP, false, + MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); if (r <= 0) DRM_ERROR("(%ld) failed to wait for user bo\n", r); @@ -170,11 +171,13 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, void *owner, struct amdgpu_hmm_range *range) { - unsigned long end; + const u64 max_bytes = SZ_2G; + + struct hmm_range *hmm_range = &range->hmm_range; unsigned long timeout; unsigned long *pfns; - int r = 0; - struct hmm_range *hmm_range = &range->hmm_range; + unsigned long end; + int r; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); if (unlikely(!pfns)) { @@ -191,8 +194,9 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, end = start + npages * PAGE_SIZE; hmm_range->dev_private_owner = owner; + hmm_range->notifier_seq = mmu_interval_read_begin(notifier); do { - hmm_range->end = min(hmm_range->start + MAX_WALK_BYTE, end); + hmm_range->end = min(hmm_range->start + max_bytes, end); pr_debug("hmm range: start = 0x%lx, end = 0x%lx", hmm_range->start, hmm_range->end); @@ -200,7 +204,6 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT); retry: - hmm_range->notifier_seq = mmu_interval_read_begin(notifier); r = hmm_range_fault(hmm_range); if (unlikely(r)) { if (r == -EBUSY && !time_after(jiffies, timeout)) @@ -210,7 +213,7 @@ retry: if (hmm_range->end == end) break; - hmm_range->hmm_pfns += MAX_WALK_BYTE >> PAGE_SHIFT; + hmm_range->hmm_pfns += max_bytes >> PAGE_SHIFT; hmm_range->start = hmm_range->end; } while (hmm_range->end < end); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 6c644cfe6695..fc9f3adf9912 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -2280,7 +2280,8 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev) list_for_each_entry(obj, &con->head, node) { if (amdgpu_ras_is_supported(adev, obj->head.block) && (obj->attr_inuse == 1)) { - sprintf(fs_info.debugfs_name, "%s_err_inject", + snprintf(fs_info.debugfs_name, sizeof(fs_info.debugfs_name), + "%s_err_inject", get_ras_block_str(&obj->head)); fs_info.head = obj->head; amdgpu_ras_debugfs_create(adev, &fs_info, dir); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c index f4be19223588..21a225b0116a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c @@ -173,16 +173,17 @@ error: int amdgpu_seq64_alloc(struct amdgpu_device *adev, u64 *va, u64 *gpu_addr, u64 **cpu_addr) { - unsigned long bit_pos; + unsigned long bit_pos = 0; - for (;;) { - bit_pos = find_first_zero_bit(adev->seq64.used, adev->seq64.num_sem); + do { + bit_pos = find_next_zero_bit(adev->seq64.used, + adev->seq64.num_sem, bit_pos); if (bit_pos >= adev->seq64.num_sem) return -ENOSPC; - if (!test_and_set_bit(bit_pos, adev->seq64.used)) break; - } + bit_pos++; + } while (1); *va = bit_pos * sizeof(u64) + amdgpu_seq64_get_va_base(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index f070ea37d918..59ffaa7b61c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -215,33 +215,15 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell) xa_unlock_irqrestore(xa, flags); } -static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue, - struct amdgpu_bo_va_mapping *va_map, u64 addr) -{ - struct amdgpu_userq_va_cursor *va_cursor; - struct userq_va_list; - - va_cursor = kzalloc_obj(*va_cursor); - if (!va_cursor) - return -ENOMEM; - - INIT_LIST_HEAD(&va_cursor->list); - va_cursor->gpu_addr = addr; - va_map->bo_va->userq_va_mapped = true; - list_add(&va_cursor->list, &queue->userq_va_list); - - return 0; -} - int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, - u64 addr, u64 expected_size) + u64 addr, u64 expected_size, + u64 *va_out) { struct amdgpu_bo_va_mapping *va_map; struct amdgpu_vm *vm = queue->vm; u64 user_addr; u64 size; - int r = 0; /* Caller must hold vm->root.bo reservation */ dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); @@ -250,20 +232,18 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, size = expected_size >> AMDGPU_GPU_PAGE_SHIFT; va_map = amdgpu_vm_bo_lookup_mapping(vm, user_addr); - if (!va_map) { - r = -EINVAL; - goto out_err; - } + if (!va_map) + return -EINVAL; + /* Only validate the userq whether resident in the VM mapping range */ if (user_addr >= va_map->start && va_map->last - user_addr + 1 >= size) { - amdgpu_userq_buffer_va_list_add(queue, va_map, user_addr); + va_map->bo_va->userq_va_mapped = true; + *va_out = user_addr; return 0; } - r = -EINVAL; -out_err: - return r; + return -EINVAL; } static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) @@ -284,14 +264,16 @@ static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_va_cursor *va_cursor, *tmp; - int r = 0; + int i, r = 0; - list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) { - r += amdgpu_userq_buffer_va_mapped(queue->vm, va_cursor->gpu_addr); + for (i = 0; i < ARRAY_SIZE(queue->userq_vas.va_array); i++) { + if (!queue->userq_vas.va_array[i]) + continue; + r += amdgpu_userq_buffer_va_mapped(queue->vm, + queue->userq_vas.va_array[i]); dev_dbg(queue->userq_mgr->adev->dev, "validate the userq mapping:%p va:%llx r:%d\n", - queue, va_cursor->gpu_addr, r); + queue, queue->userq_vas.va_array[i], r); } if (r != 0) @@ -300,24 +282,7 @@ static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue) return false; } -static void amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev, - struct amdgpu_usermode_queue *queue) -{ - struct amdgpu_userq_va_cursor *va_cursor, *tmp; - struct amdgpu_bo_va_mapping *mapping; - - /* Caller must hold vm->root.bo reservation */ - dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); - list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) { - mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, va_cursor->gpu_addr); - if (mapping) - dev_dbg(adev->dev, "delete the userq:%p va:%llx\n", - queue, va_cursor->gpu_addr); - list_del(&va_cursor->list); - kfree(va_cursor); - } -} static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue) { @@ -417,18 +382,14 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) { struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; struct amdgpu_device *adev = uq_mgr->adev; - const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; /* Wait for mode-1 reset to complete */ down_read(&adev->reset_domain->sem); - uq_funcs->mqd_destroy(queue); /* Use interrupt-safe locking since IRQ handlers may access these XArrays */ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); amdgpu_userq_fence_driver_free(queue); queue->fence_drv = NULL; - queue->userq_mgr = NULL; - list_del(&queue->userq_va_list); up_read(&adev->reset_domain->sem); } @@ -467,81 +428,15 @@ retry: dma_fence_put(ev_fence); } -int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj, - int size) -{ - struct amdgpu_device *adev = uq_mgr->adev; - struct amdgpu_bo_param bp; - int r; - - memset(&bp, 0, sizeof(bp)); - bp.byte_align = PAGE_SIZE; - bp.domain = AMDGPU_GEM_DOMAIN_GTT; - bp.flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS | - AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; - bp.type = ttm_bo_type_kernel; - bp.size = size; - bp.resv = NULL; - bp.bo_ptr_size = sizeof(struct amdgpu_bo); - - r = amdgpu_bo_create(adev, &bp, &userq_obj->obj); - if (r) { - drm_file_err(uq_mgr->file, "Failed to allocate BO for userqueue (%d)", r); - return r; - } - - r = amdgpu_bo_reserve(userq_obj->obj, true); - if (r) { - drm_file_err(uq_mgr->file, "Failed to reserve BO to map (%d)", r); - goto free_obj; - } - - r = amdgpu_bo_pin(userq_obj->obj, AMDGPU_GEM_DOMAIN_GTT); - if (r) - goto unresv; - - r = amdgpu_ttm_alloc_gart(&(userq_obj->obj)->tbo); - if (r) { - drm_file_err(uq_mgr->file, "Failed to alloc GART for userqueue object (%d)", r); - goto unpin_bo; - } - - r = amdgpu_bo_kmap(userq_obj->obj, &userq_obj->cpu_ptr); - if (r) { - drm_file_err(uq_mgr->file, "Failed to map BO for userqueue (%d)", r); - goto unpin_bo; - } - userq_obj->gpu_addr = amdgpu_bo_gpu_offset(userq_obj->obj); - amdgpu_bo_unreserve(userq_obj->obj); - memset(userq_obj->cpu_ptr, 0, size); - return 0; - -unpin_bo: - amdgpu_bo_unpin(userq_obj->obj); -unresv: - amdgpu_bo_unreserve(userq_obj->obj); -free_obj: - amdgpu_bo_unref(&userq_obj->obj); - return r; -} - -void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj) -{ - amdgpu_bo_kunmap(userq_obj->obj); - amdgpu_bo_unpin(userq_obj->obj); - amdgpu_bo_unref(&userq_obj->obj); -} - -uint64_t +static int amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_db_info *db_info, - struct drm_file *filp) + struct drm_file *filp, + u64 *index) { - uint64_t index; + u64 doorbell_index; struct drm_gem_object *gobj; struct amdgpu_userq_obj *db_obj = db_info->db_obj; int r, db_size; @@ -588,12 +483,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, goto unpin_bo; } - index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, - db_info->doorbell_offset, db_size); + doorbell_index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, + db_info->doorbell_offset, db_size); drm_dbg_driver(adev_to_drm(uq_mgr->adev), - "[Usermode queues] doorbell index=%lld\n", index); + "[Usermode queues] doorbell index=%lld\n", doorbell_index); amdgpu_bo_unreserve(db_obj->obj); - return index; + *index = doorbell_index; + return 0; unpin_bo: amdgpu_bo_unpin(db_obj->obj); @@ -608,9 +504,7 @@ static int amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_queue *queue) { struct amdgpu_device *adev = uq_mgr->adev; - struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr); - struct amdgpu_vm *vm = &fpriv->vm; - + const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; int r = 0; cancel_delayed_work_sync(&uq_mgr->resume_work); @@ -618,14 +512,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); - r = amdgpu_bo_reserve(vm->root.bo, false); - if (r) { - drm_file_err(uq_mgr->file, "Failed to reserve root bo during userqueue destroy\n"); - return r; - } - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(vm->root.bo); - mutex_lock(&uq_mgr->userq_mutex); amdgpu_userq_wait_for_last_fence(queue); @@ -637,15 +523,15 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_userq_cleanup(queue); mutex_unlock(&uq_mgr->userq_mutex); + cancel_delayed_work_sync(&queue->hang_detect_work); + uq_funcs->mqd_destroy(queue); + queue->userq_mgr = NULL; + amdgpu_bo_reserve(queue->db_obj.obj, true); amdgpu_bo_unpin(queue->db_obj.obj); amdgpu_bo_unreserve(queue->db_obj.obj); amdgpu_bo_unref(&queue->db_obj.obj); - amdgpu_bo_reserve(queue->wptr_obj.obj, true); - amdgpu_bo_unpin(queue->wptr_obj.obj); - amdgpu_bo_unreserve(queue->wptr_obj.obj); - amdgpu_bo_unref(&queue->wptr_obj.obj); kfree(queue); pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); @@ -739,7 +625,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) } kref_init(&queue->refcount); - INIT_LIST_HEAD(&queue->userq_va_list); queue->doorbell_handle = args->in.doorbell_handle; queue->queue_type = args->in.ip_type; queue->vm = &fpriv->vm; @@ -748,26 +633,29 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work); - mutex_init(&queue->fence_drv_lock); - xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC); r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv); if (r) goto free_queue; + xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC); + mutex_init(&queue->fence_drv_lock); /* Make sure the queue can actually run with those virtual addresses. */ r = amdgpu_bo_reserve(fpriv->vm.root.bo, false); if (r) goto free_fence_drv; if (amdgpu_userq_input_va_validate(adev, queue, args->in.queue_va, - args->in.queue_size) || + args->in.queue_size, + &queue->userq_vas.va.queue_rb) || amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va, - AMDGPU_GPU_PAGE_SIZE) || + AMDGPU_GPU_PAGE_SIZE, + &queue->userq_vas.va.rptr) || amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va, - AMDGPU_GPU_PAGE_SIZE)) { + AMDGPU_GPU_PAGE_SIZE, + &queue->userq_vas.va.wptr)) { r = -EINVAL; amdgpu_bo_unreserve(fpriv->vm.root.bo); - goto clean_mapping; + goto free_fence_drv; } amdgpu_bo_unreserve(fpriv->vm.root.bo); @@ -776,18 +664,17 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) db_info.doorbell_handle = queue->doorbell_handle; db_info.db_obj = &queue->db_obj; db_info.doorbell_offset = args->in.doorbell_offset; - index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp); - if (index == (uint64_t)-EINVAL) { + r = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp, &index); + if (r) { drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n"); - r = -EINVAL; - goto clean_mapping; + goto free_fence_drv; } queue->doorbell_index = index; r = uq_funcs->mqd_create(queue, &args->in); if (r) { drm_file_err(uq_mgr->file, "Failed to create Queue\n"); - goto clean_mapping; + goto clean_doorbell_bo; } /* Update VM owner at userq submit-time for page-fault attribution. */ @@ -808,7 +695,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (r) { drm_file_err(uq_mgr->file, "Failed to map Queue\n"); mutex_unlock(&uq_mgr->userq_mutex); - goto clean_doorbell; + goto erase_doorbell; } } @@ -831,15 +718,15 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) args->out.queue_id = qid; return 0; -clean_doorbell: +erase_doorbell: xa_erase_irq(&adev->userq_doorbell_xa, index); clean_mqd: uq_funcs->mqd_destroy(queue); -clean_mapping: - amdgpu_bo_reserve(fpriv->vm.root.bo, true); - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(fpriv->vm.root.bo); - mutex_destroy(&queue->fence_drv_lock); +clean_doorbell_bo: + amdgpu_bo_reserve(queue->db_obj.obj, true); + amdgpu_bo_unpin(queue->db_obj.obj); + amdgpu_bo_unreserve(queue->db_obj.obj); + amdgpu_bo_unref(&queue->db_obj.obj); free_fence_drv: amdgpu_userq_fence_driver_free(queue); free_queue: @@ -996,7 +883,7 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) continue; } - r = amdgpu_userq_restore_helper(queue); + r = amdgpu_userq_map_helper(queue); if (r) ret = r; @@ -1233,7 +1120,7 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr) /* Try to unmap all the queues in this process ctx */ xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { - r = amdgpu_userq_preempt_helper(queue); + r = amdgpu_userq_unmap_helper(queue); if (r) ret = r; } @@ -1453,8 +1340,7 @@ int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev, } void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr) + struct amdgpu_bo_va_mapping *mapping) { u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev); struct amdgpu_bo_va *bo_va = mapping->bo_va; @@ -1463,12 +1349,9 @@ void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, if (!ip_mask) return; - dev_warn_once(adev->dev, "now unmapping a vital queue va:%llx\n", saddr); /** - * The userq VA mapping reservation should include the eviction fence, - * if the eviction fence can't signal successfully during unmapping, - * then driver will warn to flag this improper unmap of the userq VA. - * Note: The eviction fence may be attached to different BOs, and this + * The userq VA mapping reservation should include the eviction fence. + * Note: The eviction fence may be attached to different BOs and this * unmap is only for one kind of userq VAs, so at this point suppose * the eviction fence is always unsignaled. */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 49b33e2d6932..d1751febaefe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -48,11 +48,6 @@ struct amdgpu_userq_obj { struct amdgpu_bo *obj; }; -struct amdgpu_userq_va_cursor { - u64 gpu_addr; - struct list_head list; -}; - struct amdgpu_usermode_queue { int queue_type; enum amdgpu_userq_state state; @@ -93,7 +88,17 @@ struct amdgpu_usermode_queue { struct delayed_work hang_detect_work; struct kref refcount; - struct list_head userq_va_list; + union { + struct { + u64 queue_rb; + u64 wptr; + u64 rptr; + u64 eop; + u64 shadow; + u64 csa; + } va; + u64 va_array[6]; + } userq_vas; }; struct amdgpu_userq_funcs { @@ -151,22 +156,11 @@ void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev); void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr); void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr); -int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj, - int size); - -void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj); - void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr); void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr, struct amdgpu_eviction_fence_mgr *evf_mgr); -uint64_t amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_db_info *db_info, - struct drm_file *filp); - u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev); bool amdgpu_userq_enabled(struct drm_device *dev); @@ -185,8 +179,8 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell); int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, - u64 addr, u64 expected_size); + u64 addr, u64 expected_size, u64 *va_out); + void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr); + struct amdgpu_bo_va_mapping *mapping); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index a41fb72dba94..f74ad378e407 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -593,7 +593,7 @@ free_syncobj_handles: static int amdgpu_userq_wait_count_fences(struct drm_file *filp, struct drm_amdgpu_userq_wait *wait_info, - u32 *syncobj_handles, u32 *timeline_points, + u32 *syncobj_handles, u64 *timeline_points, u32 *timeline_handles, struct drm_gem_object **gobj_write, struct drm_gem_object **gobj_read) @@ -703,7 +703,7 @@ amdgpu_userq_wait_add_fence(struct drm_amdgpu_userq_wait *wait_info, static int amdgpu_userq_wait_return_fence_info(struct drm_file *filp, struct drm_amdgpu_userq_wait *wait_info, - u32 *syncobj_handles, u32 *timeline_points, + u32 *syncobj_handles, u64 *timeline_points, u32 *timeline_handles, struct drm_gem_object **gobj_write, struct drm_gem_object **gobj_read) @@ -906,7 +906,8 @@ int amdgpu_userq_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { int num_points, num_syncobj, num_read_bo_handles, num_write_bo_handles; - u32 *syncobj_handles, *timeline_points, *timeline_handles; + u32 *syncobj_handles, *timeline_handles; + u64 *timeline_points; struct drm_amdgpu_userq_wait *wait_info = data; struct drm_gem_object **gobj_write; struct drm_gem_object **gobj_read; @@ -935,7 +936,7 @@ int amdgpu_userq_wait_ioctl(struct drm_device *dev, void *data, } ptr = u64_to_user_ptr(wait_info->syncobj_timeline_points); - timeline_points = memdup_array_user(ptr, num_points, sizeof(u32)); + timeline_points = memdup_array_user(ptr, num_points, sizeof(u64)); if (IS_ERR(timeline_points)) { r = PTR_ERR(timeline_points); goto free_timeline_handles; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index fccd758b6699..381901bc539f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1631,6 +1631,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, { struct amdgpu_bo_va *bo_va; struct dma_resv *resv; + struct amdgpu_bo *bo; bool clear, unlock; int r; @@ -1650,11 +1651,13 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, while (!list_empty(&vm->invalidated)) { bo_va = list_first_entry(&vm->invalidated, struct amdgpu_bo_va, base.vm_status); - resv = bo_va->base.bo->tbo.base.resv; + bo = bo_va->base.bo; + resv = bo->tbo.base.resv; spin_unlock(&vm->status_lock); /* Try to reserve the BO to avoid clearing its ptes */ - if (!adev->debug_vm && dma_resv_trylock(resv)) { + if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) && + dma_resv_trylock(resv)) { clear = false; unlock = true; /* The caller is already holding the reservation lock */ @@ -2003,7 +2006,7 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, * from user space. */ if (unlikely(bo_va->userq_va_mapped)) - amdgpu_userq_gem_va_unmap_validate(adev, mapping, saddr); + amdgpu_userq_gem_va_unmap_validate(adev, mapping); list_del(&mapping->list); amdgpu_vm_it_remove(mapping, &vm->va); diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c index f9949fedfbb9..f2fe6f5bc7f7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c @@ -449,12 +449,10 @@ static void gfxhub_v11_5_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c index 7609b9cecae8..efcaca70c27a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c @@ -454,12 +454,10 @@ static void gfxhub_v12_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c index 3544eb42dca6..4c2fd1e6616e 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v12_1.c @@ -633,19 +633,17 @@ static void gfxhub_v12_1_xcc_set_fault_enable_default(struct amdgpu_device *adev tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL_LO32, OTHER_CLIENT_ID_NO_RETRY_FAULT_INTERRUPT, value); - if (!value) - tmp = REG_SET_FIELD(tmp, - GCVM_L2_PROTECTION_FAULT_CNTL_LO32, - CRASH_ON_NO_RETRY_FAULT, 1); + tmp = REG_SET_FIELD(tmp, + GCVM_L2_PROTECTION_FAULT_CNTL_LO32, + CRASH_ON_NO_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_LO32, tmp); tmp = RREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_HI32); - if (!value) - tmp = REG_SET_FIELD(tmp, - GCVM_L2_PROTECTION_FAULT_CNTL_HI32, - CRASH_ON_RETRY_FAULT, 1); + tmp = REG_SET_FIELD(tmp, + GCVM_L2_PROTECTION_FAULT_CNTL_HI32, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regGCVM_L2_PROTECTION_FAULT_CNTL_HI32, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c index a7bfc9f41d0e..bfe247b1a333 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c @@ -403,12 +403,10 @@ static void gfxhub_v1_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c index 6c03bf9f1ae8..fbdf46070b38 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c @@ -516,12 +516,10 @@ static void gfxhub_v1_2_xcc_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, VM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, GET_INST(GC, i), regVM_L2_PROTECTION_FAULT_CNTL, tmp); } } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c index 793faf62cb07..9ea593e2c719 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c @@ -418,12 +418,10 @@ static void gfxhub_v2_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c index aceb8447feac..30b90d35abd0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c @@ -449,12 +449,10 @@ static void gfxhub_v2_1_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, mmGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c index abe30c8bd2ba..f089f70571aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c @@ -446,12 +446,10 @@ static void gfxhub_v3_0_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c index b3ef6e71811f..128115a2cb45 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c @@ -434,12 +434,10 @@ static void gfxhub_v3_0_3_set_fault_enable_default(struct amdgpu_device *adev, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value); tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, value); - if (!value) { - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_NO_RETRY_FAULT, 1); - tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, - CRASH_ON_RETRY_FAULT, 1); - } + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_NO_RETRY_FAULT, !value); + tmp = REG_SET_FIELD(tmp, GCVM_L2_PROTECTION_FAULT_CNTL, + CRASH_ON_RETRY_FAULT, !value); WREG32_SOC15(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL, tmp); } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index e1ace7d44ffd..f5bdfea54afa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -847,6 +847,7 @@ static int gmc_v10_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 94d6631ce0bc..807bd180b9d4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -821,6 +821,7 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index e10ac9788d13..8dc9c053897b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -812,8 +812,9 @@ static int gmc_v12_0_gart_init(struct amdgpu_device *adev) static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) { - int r, vram_width = 0, vram_type = 0, vram_vendor = 0; + int r, vram_width = 0, vram_type = 0, vram_vendor = 0, dma_addr_bits; struct amdgpu_device *adev = ip_block->adev; + uint64_t pte_addr_mask = 0; int i; adev->mmhub.funcs->init(adev); @@ -843,6 +844,8 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * block size 512 (9bit) */ amdgpu_vm_adjust_size(adev, 256 * 1024, 9, 3, 48); + pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ + dma_addr_bits = 44; break; case IP_VERSION(12, 1, 0): bitmap_set(adev->vmhubs_mask, AMDGPU_GFXHUB(0), @@ -855,9 +858,13 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * block size 512 (9bit) */ amdgpu_vm_adjust_size(adev, 128 * 1024 * 1024, 9, 4, 57); + pte_addr_mask = 0x000FFFFFFFFFF000ULL; /* 52 bit PA */ + dma_addr_bits = 52; break; default: - break; + dev_warn(adev->dev, "Unrecognized GC IP version: 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return -EINVAL; } /* This interrupt is VMC page fault.*/ @@ -911,14 +918,15 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = AMDGPU_GMC_HOLE_MASK; + adev->gmc.pte_addr_mask = pte_addr_mask; - r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(44)); + r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(dma_addr_bits)); if (r) { drm_warn(adev_to_drm(adev), "No suitable DMA available.\n"); return r; } - adev->need_swiotlb = drm_need_swiotlb(44); + adev->need_swiotlb = drm_need_swiotlb(dma_addr_bits); r = gmc_v12_0_mc_init(adev); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index cc272a96fcef..6aa581b1c148 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -836,6 +836,7 @@ static int gmc_v6_0_sw_init(struct amdgpu_ip_block *ip_block) amdgpu_vm_adjust_size(adev, 64, 9, 1, 40); adev->gmc.mc_mask = 0xffffffffffULL; + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index bb16ba2ef6fd..2b0362c4d9eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -1016,6 +1016,7 @@ static int gmc_v7_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffULL; /* 40 bit MC */ + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; /* 40 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index a59174f6bcc1..fbccfcb3d7cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -1131,6 +1131,7 @@ static int gmc_v8_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffULL; /* 40 bit MC */ + adev->gmc.pte_addr_mask = 0x000000FFFFFFF000ULL; /* 40 bit PA */ r = dma_set_mask_and_coherent(adev->dev, DMA_BIT_MASK(40)); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index e7b78027002b..c6dbe25f2bd9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1994,6 +1994,7 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block) * internal address space. */ adev->gmc.mc_mask = 0xffffffffffffULL; /* 48 bit MC */ + adev->gmc.pte_addr_mask = 0x0000FFFFFFFFF000ULL; /* 48 bit PA */ dma_addr_bits = amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(9, 4, 2) ? diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 5b4121ddc78c..4cbd46f53e85 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -81,7 +81,7 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo); if (ret) { DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); - goto fail_map; + goto fail_alloc_gart; } queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj); @@ -89,6 +89,8 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, drm_exec_fini(&exec); return 0; +fail_alloc_gart: + amdgpu_bo_unpin(wptr_obj->obj); fail_map: amdgpu_bo_unref(&wptr_obj->obj); fail_lock: @@ -190,12 +192,16 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, * for the same. */ size = AMDGPU_USERQ_PROC_CTX_SZ + AMDGPU_USERQ_GANG_CTX_SZ; - r = amdgpu_userq_create_object(uq_mgr, ctx, size); + r = amdgpu_bo_create_kernel(uq_mgr->adev, size, 0, + AMDGPU_GEM_DOMAIN_GTT, + &ctx->obj, &ctx->gpu_addr, + &ctx->cpu_ptr); if (r) { DRM_ERROR("Failed to allocate ctx space bo for userqueue, err:%d\n", r); return r; } + memset(ctx->cpu_ptr, 0, size); return 0; } @@ -268,13 +274,19 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, return -ENOMEM; } - r = amdgpu_userq_create_object(uq_mgr, &queue->mqd, - AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size)); + r = amdgpu_bo_create_kernel(adev, + AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size), + 0, AMDGPU_GEM_DOMAIN_GTT, + &queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); if (r) { DRM_ERROR("Failed to create MQD object for userqueue\n"); goto free_props; } + memset(queue->mqd.cpu_ptr, 0, + AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size)); + /* Initialize the MQD BO with user given values */ userq_props->wptr_gpu_addr = mqd_user->wptr_va; userq_props->rptr_gpu_addr = mqd_user->rptr_va; @@ -306,8 +318,9 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, kfree(compute_mqd); goto free_mqd; } - r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va, - 2048); + r = amdgpu_userq_input_va_validate(adev, queue, + compute_mqd->eop_va, 2048, + &queue->userq_vas.va.eop); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(compute_mqd); @@ -356,7 +369,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, - shadow_info.shadow_size); + shadow_info.shadow_size, + &queue->userq_vas.va.shadow); if (r) { amdgpu_bo_unreserve(queue->vm->root.bo); kfree(mqd_gfx_v11); @@ -364,7 +378,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va, - shadow_info.csa_size); + shadow_info.csa_size, + &queue->userq_vas.va.csa); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_gfx_v11); @@ -394,7 +409,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va, - 32); + 32, + &queue->userq_vas.va.csa); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_sdma_v11); @@ -430,10 +446,12 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, return 0; free_ctx: - amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj); + amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr, + &queue->fw_obj.cpu_ptr); free_mqd: - amdgpu_userq_destroy_object(uq_mgr, &queue->mqd); + amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); free_props: kfree(userq_props); @@ -443,11 +461,17 @@ free_props: static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; - amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj); + amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr, + &queue->fw_obj.cpu_ptr); kfree(queue->userq_prop); - amdgpu_userq_destroy_object(uq_mgr, &queue->mqd); + amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); + + amdgpu_bo_reserve(queue->wptr_obj.obj, true); + amdgpu_bo_unpin(queue->wptr_obj.obj); + amdgpu_bo_unreserve(queue->wptr_obj.obj); + amdgpu_bo_unref(&queue->wptr_obj.obj); } static int mes_userq_preempt(struct amdgpu_usermode_queue *queue) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index 061934a2e93a..9c9bbe043a47 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -1316,6 +1316,7 @@ static int sdma_v7_1_sw_init(struct amdgpu_ip_block *ip_block) ring->ring_obj = NULL; ring->use_doorbell = true; ring->me = i; + ring->no_user_submission = adev->sdma.no_user_submission; for (xcc_id = 0; xcc_id < fls(adev->gfx.xcc_mask); xcc_id++) { if (adev->sdma.instance[i].xcc_id == GET_INST(GC, xcc_id)) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 03b266b26738..8785f7810157 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -2300,6 +2300,11 @@ static int criu_restore_devices(struct kfd_process *p, ret = -EINVAL; goto exit; } + + if (pdd->drm_file) { + ret = -EINVAL; + goto exit; + } pdd->user_gpu_id = device_buckets[i].user_gpu_id; drm_file = fget(device_buckets[i].drm_fd); @@ -2310,11 +2315,6 @@ static int criu_restore_devices(struct kfd_process *p, goto exit; } - if (pdd->drm_file) { - ret = -EINVAL; - goto exit; - } - /* create the vm using render nodes for kfd pdd */ if (kfd_process_device_init_vm(pdd, drm_file)) { pr_err("could not init vm for given pdd\n"); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_debug.c b/drivers/gpu/drm/amd/amdkfd/kfd_debug.c index 0f7aa51b629e..0dd1fd448059 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_debug.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_debug.c @@ -832,6 +832,12 @@ int kfd_dbg_trap_enable(struct kfd_process *target, uint32_t fd, if (copy_to_user(runtime_info, (void *)&target->runtime_info, copy_size)) { kfd_dbg_trap_deactivate(target, false, 0); + fput(target->dbg_ev_file); + target->dbg_ev_file = NULL; + if (target->debugger_process) + atomic_dec(&target->debugger_process->debugged_process_count); + target->debug_trap_enabled = false; + kfd_unref_process(target); r = -EFAULT; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index e0a31e11f0ff..31187ddbb79e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -2502,6 +2502,9 @@ static int wait_on_destroy_queue(struct device_queue_manager *dqm, if (pdd->qpd.is_debug) return ret; + if (q->properties.is_being_destroyed) + return -EBUSY; + q->properties.is_being_destroyed = true; if (pdd->process->debug_trap_enabled && q->properties.is_suspended) { @@ -2514,6 +2517,9 @@ static int wait_on_destroy_queue(struct device_queue_manager *dqm, dqm_lock(dqm); } + if (ret) + q->properties.is_being_destroyed = false; + return ret; } @@ -2607,7 +2613,7 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm, return retval; failed_try_destroy_debugged_queue: - + q->properties.is_being_destroyed = false; dqm_unlock(dqm); return retval; } @@ -3308,12 +3314,14 @@ static void copy_context_work_handler(struct work_struct *work) static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array) { - size_t array_size = num_queues * sizeof(uint32_t); - if (!usr_queue_id_array) - return NULL; + return num_queues ? ERR_PTR(-EINVAL) : NULL; + + if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) + return ERR_PTR(-EINVAL); - return memdup_user(usr_queue_id_array, array_size); + return memdup_user(usr_queue_id_array, + array_size(num_queues, sizeof(uint32_t))); } int resume_queues(struct kfd_process *p, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 44150a71ffd5..e65b323aafbf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -795,6 +795,8 @@ static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) struct kfd_event_waiter *event_waiters; uint32_t i; + if (num_events > KFD_SIGNAL_EVENT_LIMIT) + return NULL; event_waiters = kzalloc_objs(struct kfd_event_waiter, num_events); if (!event_waiters) return NULL; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index a1e3cf2384dd..527c531676e4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -320,8 +320,7 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi static void restore_mqd(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, - struct queue_properties *qp, - const void *mqd_src, + struct queue_properties *qp, const void *mqd_src, const void *ctl_stack_src, const u32 ctl_stack_size) { uint64_t addr; @@ -337,14 +336,48 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd, *gart_addr = addr; m->cp_hqd_pq_doorbell_control = - qp->doorbell_off << - CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; - pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", - m->cp_hqd_pq_doorbell_control); + qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control); qp->is_active = 0; } +static void checkpoint_mqd_sdma(struct mqd_manager *mm, + void *mqd, + void *mqd_dst, + void *ctl_stack_dst) +{ + struct v11_sdma_mqd *m; + + m = get_sdma_mqd(mqd); + + memcpy(mqd_dst, m, sizeof(struct v11_sdma_mqd)); +} + +static void restore_mqd_sdma(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, + const u32 ctl_stack_size) +{ + uint64_t addr; + struct v11_sdma_mqd *m; + + m = (struct v11_sdma_mqd *) mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memcpy(m, mqd_src, sizeof(*m)); + + m->sdmax_rlcx_doorbell_offset = + qp->doorbell_off << SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT; + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + qp->is_active = 0; +} static void init_mqd_hiq(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, @@ -529,8 +562,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->update_mqd = update_mqd_sdma; mqd->destroy_mqd = kfd_destroy_mqd_sdma; mqd->is_occupied = kfd_is_occupied_sdma; - mqd->checkpoint_mqd = checkpoint_mqd; - mqd->restore_mqd = restore_mqd; + mqd->checkpoint_mqd = checkpoint_mqd_sdma; + mqd->restore_mqd = restore_mqd_sdma; mqd->mqd_size = sizeof(struct v11_sdma_mqd); mqd->mqd_stride = kfd_mqd_stride; #if defined(CONFIG_DEBUG_FS) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c index 15975c23a88e..dfbde5a571f6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c @@ -254,8 +254,10 @@ void kfd_smi_event_update_vmfault(struct kfd_node *dev, uint16_t pasid) if (task_info) { /* Report VM faults from user applications, not retry from kernel */ if (task_info->task.pid) - kfd_smi_event_add(0, dev, KFD_SMI_EVENT_VMFAULT, KFD_EVENT_FMT_VMFAULT( - task_info->task.pid, task_info->task.comm)); + kfd_smi_event_add(task_info->tgid, dev, + KFD_SMI_EVENT_VMFAULT, + KFD_EVENT_FMT_VMFAULT(task_info->task.pid, + task_info->task.comm)); amdgpu_vm_put_task_info(task_info); } } @@ -356,7 +358,7 @@ void kfd_smi_event_process(struct kfd_process_device *pdd, bool start) task_info = amdgpu_vm_get_task_info_vm(avm); if (task_info) { - kfd_smi_event_add(0, pdd->dev, + kfd_smi_event_add(task_info->tgid, pdd->dev, start ? KFD_SMI_EVENT_PROCESS_START : KFD_SMI_EVENT_PROCESS_END, KFD_EVENT_FMT_PROCESS(task_info->task.pid, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 35ec67d9739b..3841943da5ec 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -3732,6 +3732,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, svms = &p->svms; + if (!process_info) + return -EINVAL; + mutex_lock(&process_info->lock); svm_range_list_lock_and_flush_work(svms, mm); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 5fc5d5608506..f8c13bad4ac2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -10083,7 +10083,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, continue; bundle->surface_updates[planes_count].surface = dc_plane; - if (new_pcrtc_state->color_mgmt_changed) { + if (new_pcrtc_state->color_mgmt_changed || new_plane_state->color_mgmt_changed) { bundle->surface_updates[planes_count].gamma = &dc_plane->gamma_correction; bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func; bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix; @@ -11824,6 +11824,10 @@ static bool should_reset_plane(struct drm_atomic_state *state, if (new_crtc_state->color_mgmt_changed) return true; + /* Plane color pipeline or its colorop changes. */ + if (new_plane_state->color_mgmt_changed) + return true; + /* * On zpos change, planes need to be reordered by removing and re-adding * them one by one to the dc state, in order of descending zpos. @@ -13446,17 +13450,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, } /* Handle MCCS */ - if (do_mccs) + if (do_mccs) { dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); - if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A || - as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) && - (!sink->edid_caps.freesync_vcp_code || - (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported))) - freesync_capable = false; + if (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported) + freesync_capable = false; - if (do_mccs && sink->mccs_caps.freesync_supported && freesync_capable) - dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + if (sink->mccs_caps.freesync_supported && freesync_capable) + dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + } update: if (dm_con_state) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index 2409ac72b166..3a3d01ce0d42 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -1344,8 +1344,13 @@ static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *b if (size == 0) return 0; + if (!connector->base.state || !connector->base.state->crtc) + return -ENODEV; + acrtc_state = to_dm_crtc_state(connector->base.state->crtc->state); + write_size = min_t(size_t, size, sizeof(data)); + r = copy_from_user(data, buf, write_size); write_size -= r; diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c index e8736c134b8d..60bd9ead928a 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/vector.c +++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c @@ -289,8 +289,8 @@ bool dal_vector_reserve(struct vector *vector, uint32_t capacity) if (capacity <= vector->capacity) return true; - new_container = krealloc(vector->container, - capacity * vector->struct_size, GFP_KERNEL); + new_container = krealloc_array(vector->container, + capacity, vector->struct_size, GFP_KERNEL); if (new_container) { vector->container = new_container; diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index c307f42fe0b9..507b628abdb5 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -222,6 +222,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, ATOM_COMMON_RECORD_HEADER *header; ATOM_I2C_RECORD *record; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -234,7 +235,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -293,11 +294,12 @@ static enum bp_result bios_parser_get_device_tag_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -966,6 +968,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -975,7 +978,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -1670,6 +1673,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -1679,7 +1683,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -2769,6 +2773,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, { (void)i; unsigned int j; + unsigned int n; struct bios_parser *bp; ATOM_BRACKET_LAYOUT_RECORD *record; ATOM_COMMON_RECORD_HEADER *record_header; @@ -2778,7 +2783,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, record = NULL; record_header = NULL; - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); if (record_header == NULL) { 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 c51c4b2c6fae..0e1f973326ed 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -396,6 +396,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, struct atom_i2c_record *record; struct atom_i2c_record dummy_record = {0}; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -429,7 +430,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, break; } - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -534,6 +535,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -542,7 +544,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -611,6 +613,7 @@ static struct atom_hpd_int_record *get_hpd_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -620,7 +623,7 @@ static struct atom_hpd_int_record *get_hpd_record( offset = le16_to_cpu(object->disp_recordoffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -701,8 +704,10 @@ static enum bp_result bios_parser_get_gpio_pin_info( info->offset_en = info->offset + 1; info->offset_mask = info->offset - 1; - info->mask = (uint32_t) (1 << - header->gpio_pin[i].gpio_bitshift); + if (header->gpio_pin[i].gpio_bitshift >= 32) + return BP_RESULT_BADBIOSTABLE; + + info->mask = 1u << header->gpio_pin[i].gpio_bitshift; info->mask_y = info->mask + 2; info->mask_en = info->mask + 1; info->mask_mask = info->mask - 1; @@ -2193,6 +2198,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2201,7 +2207,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( offset = object->encoder_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2230,6 +2236,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2238,7 +2245,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2266,6 +2273,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2274,7 +2282,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2352,6 +2360,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -2360,7 +2369,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2600,14 +2609,16 @@ static enum bp_result get_integrated_info_v11( info_v11->extdispconninfo.checksum; info->dp0_ext_hdmi_slv_addr = info_v11->dp0_retimer_set.HdmiSlvAddr; - info->dp0_ext_hdmi_reg_num = info_v11->dp0_retimer_set.HdmiRegNum; + info->dp0_ext_hdmi_reg_num = min_t(u8, info_v11->dp0_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp0_ext_hdmi_6g_reg_num = info_v11->dp0_retimer_set.Hdmi6GRegNum; + info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp0_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2616,14 +2627,16 @@ static enum bp_result get_integrated_info_v11( } info->dp1_ext_hdmi_slv_addr = info_v11->dp1_retimer_set.HdmiSlvAddr; - info->dp1_ext_hdmi_reg_num = info_v11->dp1_retimer_set.HdmiRegNum; + info->dp1_ext_hdmi_reg_num = min_t(u8, info_v11->dp1_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp1_ext_hdmi_6g_reg_num = info_v11->dp1_retimer_set.Hdmi6GRegNum; + info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp1_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2632,14 +2645,16 @@ static enum bp_result get_integrated_info_v11( } info->dp2_ext_hdmi_slv_addr = info_v11->dp2_retimer_set.HdmiSlvAddr; - info->dp2_ext_hdmi_reg_num = info_v11->dp2_retimer_set.HdmiRegNum; + info->dp2_ext_hdmi_reg_num = min_t(u8, info_v11->dp2_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp2_ext_hdmi_6g_reg_num = info_v11->dp2_retimer_set.Hdmi6GRegNum; + info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp2_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2648,14 +2663,16 @@ static enum bp_result get_integrated_info_v11( } info->dp3_ext_hdmi_slv_addr = info_v11->dp3_retimer_set.HdmiSlvAddr; - info->dp3_ext_hdmi_reg_num = info_v11->dp3_retimer_set.HdmiRegNum; + info->dp3_ext_hdmi_reg_num = min_t(u8, info_v11->dp3_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp3_ext_hdmi_6g_reg_num = info_v11->dp3_retimer_set.Hdmi6GRegNum; + info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v11->dp3_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2805,14 +2822,16 @@ static enum bp_result get_integrated_info_v2_1( info->ext_disp_conn_info.checksum = info_v2_1->extdispconninfo.checksum; info->dp0_ext_hdmi_slv_addr = info_v2_1->dp0_retimer_set.HdmiSlvAddr; - info->dp0_ext_hdmi_reg_num = info_v2_1->dp0_retimer_set.HdmiRegNum; + info->dp0_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp0_ext_hdmi_6g_reg_num = info_v2_1->dp0_retimer_set.Hdmi6GRegNum; + info->dp0_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp0_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp0_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2820,14 +2839,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp1_ext_hdmi_slv_addr = info_v2_1->dp1_retimer_set.HdmiSlvAddr; - info->dp1_ext_hdmi_reg_num = info_v2_1->dp1_retimer_set.HdmiRegNum; + info->dp1_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp1_ext_hdmi_6g_reg_num = info_v2_1->dp1_retimer_set.Hdmi6GRegNum; + info->dp1_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp1_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp1_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2835,14 +2856,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp2_ext_hdmi_slv_addr = info_v2_1->dp2_retimer_set.HdmiSlvAddr; - info->dp2_ext_hdmi_reg_num = info_v2_1->dp2_retimer_set.HdmiRegNum; + info->dp2_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp2_ext_hdmi_6g_reg_num = info_v2_1->dp2_retimer_set.Hdmi6GRegNum; + info->dp2_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp2_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp2_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -2850,14 +2873,16 @@ static enum bp_result get_integrated_info_v2_1( info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; } info->dp3_ext_hdmi_slv_addr = info_v2_1->dp3_retimer_set.HdmiSlvAddr; - info->dp3_ext_hdmi_reg_num = info_v2_1->dp3_retimer_set.HdmiRegNum; + info->dp3_ext_hdmi_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.HdmiRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; } - info->dp3_ext_hdmi_6g_reg_num = info_v2_1->dp3_retimer_set.Hdmi6GRegNum; + info->dp3_ext_hdmi_6g_reg_num = min_t(u8, info_v2_1->dp3_retimer_set.Hdmi6GRegNum, + ARRAY_SIZE(info->dp3_ext_hdmi_6g_reg_settings)); for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = info_v2_1->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; @@ -3245,6 +3270,7 @@ static enum bp_result update_slot_layout_info( { unsigned int record_offset; unsigned int j; + unsigned int n; struct atom_display_object_path_v2 *object; struct atom_bracket_layout_record *record; struct atom_common_record_header *record_header; @@ -3266,7 +3292,7 @@ static enum bp_result update_slot_layout_info( (object->disp_recordoffset) + (unsigned int)(bp->object_info_tbl_offset); - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (struct atom_common_record_header *) GET_IMAGE(struct atom_common_record_header, @@ -3360,6 +3386,7 @@ static enum bp_result update_slot_layout_info_v2( struct slot_layout_info *slot_layout_info) { unsigned int record_offset; + unsigned int n; struct atom_display_object_path_v3 *object; struct atom_bracket_layout_record_v2 *record; struct atom_common_record_header *record_header; @@ -3382,7 +3409,7 @@ static enum bp_result update_slot_layout_info_v2( (object->disp_recordoffset) + (unsigned int)(bp->object_info_tbl_offset); - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (struct atom_common_record_header *) GET_IMAGE(struct atom_common_record_header, diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h index ab162f2fe577..19fd7aea18f1 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h @@ -37,4 +37,9 @@ void bios_set_scratch_critical_state(struct dc_bios *bios, bool state); #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) +/* Upper bound on the number of records in a VBIOS record chain. Prevents + * unbounded looping if the VBIOS image is malformed and lacks a terminator. + */ +#define BIOS_MAX_NUM_RECORD 256 + #endif diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h index 7fa336bf1115..7dd73eaaf940 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h @@ -1217,7 +1217,7 @@ struct dc_lttpr_caps { union dp_main_link_channel_coding_lttpr_cap main_link_channel_coding; union dp_128b_132b_supported_lttpr_link_rates supported_128b_132b_rates; union dp_alpm_lttpr_cap alpm; - uint8_t aux_rd_interval[MAX_REPEATER_CNT - 1]; + uint8_t aux_rd_interval[MAX_REPEATER_CNT]; uint8_t lttpr_ieee_oui[3]; // Always read from closest LTTPR to host uint8_t lttpr_device_id[6]; // Always read from closest LTTPR to host }; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c index c4d4eea140f3..1f23dfccf07a 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c @@ -105,15 +105,26 @@ static void dccg21_update_dpp_dto(struct dccg *dccg, int dpp_inst, int req_dppcl * dccg2_init() unconditionally overwrites MICROSECOND_TIME_BASE_DIV to * 0x00120264, destroying the marker before it can be read. * - * Guard the call: if the S0i3 marker is present, skip dccg2_init() so the + * Guard the call: if the S0i3 marker is present, skip init so the * WA can function correctly. bios_golden_init() will handle init in that case. + * + * DCN21 uses 48MHz refclk, not 100MHz, so we must explicitly set the correct + * values (48MHz is taken from rn_clk_mgr_construct()). */ static void dccg21_init(struct dccg *dccg) { + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + if (dccg2_is_s0i3_golden_init_wa_done(dccg)) return; - dccg2_init(dccg); + /* 48MHz refclk from rn_clk_mgr_construct() */ + REG_WRITE(MICROSECOND_TIME_BASE_DIV, 0x00120230); + REG_WRITE(MILLISECOND_TIME_BASE_DIV, 0x0010bb80); + REG_WRITE(DISPCLK_FREQ_CHANGE_CNTL, 0x0e01003c); + + if (REG(REFCLK_CNTL)) + REG_WRITE(REFCLK_CNTL, 0); } static const struct dccg_funcs dccg21_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index c1448ae47366..0d312b40bcfa 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -110,7 +110,15 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, { COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, - 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +{ COLOR_SPACE_2020_RGB_FULLRANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_2020_RGB_LIMITEDRANGE, + { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, + 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +{ COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, + 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } }; static bool setup_scaling_configuration( diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c index cf63fac82832..1ed018aaa4bb 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -88,7 +88,15 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, { COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, - 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +{ COLOR_SPACE_2020_RGB_FULLRANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_2020_RGB_LIMITEDRANGE, + { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, + 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +{ COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, + 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } }; enum csc_color_mode { diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c index 0ca39873f807..324413a090bf 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c @@ -529,7 +529,8 @@ enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp) } else { status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST, hdcp->auth.msg.hdcp2.rx_id_list, - hdcp->auth.msg.hdcp2.rx_id_list_size); + MIN(hdcp->auth.msg.hdcp2.rx_id_list_size, + sizeof(hdcp->auth.msg.hdcp2.rx_id_list))); } return status; } diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 36942467d4ad..c3aff5d0c53d 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3076,6 +3076,10 @@ static bool si_dpm_vblank_too_short(void *handle) /* we never hit the non-gddr5 limit so disable it */ u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0; + /* Disregard vblank time when there are no displays connected */ + if (!adev->pm.pm_display_cfg.num_display) + return false; + /* Consider zero vblank time too short and disable MCLK switching. * Note that the vblank time is set to maximum when no displays are attached, * so we'll still enable MCLK switching in that case. diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 0a7f5fa3c1d3..7f8d4bb47d02 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2390,28 +2390,30 @@ static int smu_v13_0_0_enable_mgpu_fan_boost(struct smu_context *smu) } static int smu_v13_0_0_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) + uint32_t *current_power_limit, + uint32_t *default_power_limit, + uint32_t *max_power_limit, + uint32_t *min_power_limit) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_0_powerplay_table *powerplay_table = (struct smu_13_0_0_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - - if (smu_v13_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? + uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + int ret; + + if (current_power_limit) { + ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + if (ret) + *current_power_limit = pp_limit; + } - if (current_power_limit) - *current_power_limit = power_limit; if (default_power_limit) - *default_power_limit = power_limit; + *default_power_limit = pp_limit; if (powerplay_table) { if (smu->od_enabled && @@ -2425,15 +2427,15 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, } dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit = pp_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); + *min_power_limit = pp_limit * (100 - od_percent_lower); *min_power_limit /= 100; } @@ -2801,11 +2803,19 @@ static void smu_v13_0_0_i2c_control_fini(struct smu_context *smu) static int smu_v13_0_0_set_mp1_state(struct smu_context *smu, enum pp_mp1_state mp1_state) { + uint32_t param; int ret; switch (mp1_state) { case PP_MP1_STATE_UNLOAD: - ret = smu_cmn_set_mp1_state(smu, mp1_state); + /* + * NOTE: Param 0x55 comes from PMFW 80.31.0, ignored in older versions. + * No PMFW version check required. + */ + param = amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 10) ? + 0x55 : 0x00; + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PrepareMp1ForUnload, + param, NULL); break; default: /* Ignore others */ diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 5abf2b0703c6..0f774b0920ce 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2372,28 +2372,32 @@ static int smu_v13_0_7_enable_mgpu_fan_boost(struct smu_context *smu) } static int smu_v13_0_7_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) + uint32_t *current_power_limit, + uint32_t *default_power_limit, + uint32_t *max_power_limit, + uint32_t *min_power_limit) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_7_powerplay_table *powerplay_table = (struct smu_13_0_7_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - - if (smu_v13_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? + uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + int ret; + + if (current_power_limit) { + ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + if (ret) + power_limit = pp_limit; - if (current_power_limit) *current_power_limit = power_limit; + } + if (default_power_limit) - *default_power_limit = power_limit; + *default_power_limit = pp_limit; if (powerplay_table) { if (smu->od_enabled && @@ -2407,15 +2411,15 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, } dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit = pp_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); + *min_power_limit = pp_limit * (100 - od_percent_lower); *min_power_limit /= 100; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index a28624d4847a..75719c47a41e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -1231,7 +1231,8 @@ static int smu_v14_0_0_set_soft_freq_limited_range(struct smu_context *smu, switch (clk_type) { case SMU_GFXCLK: case SMU_SCLK: - msg_set_min = SMU_MSG_SetHardMinGfxClk; + /* SoftMin lets PMFW throttle gfxclk; HardMin would override SoftMax. */ + msg_set_min = SMU_MSG_SetSoftMinGfxclk; msg_set_max = SMU_MSG_SetSoftMaxGfxClk; break; case SMU_FCLK: diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index 5ce4e982ca33..fdc1456b885c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -2152,7 +2152,6 @@ static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu, metrics->Vcn1ActivityPercentage); gpu_metrics->average_socket_power = metrics->AverageSocketPower; - gpu_metrics->energy_accumulator = metrics->EnergyAccumulator; if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD) gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index 90c7127beabf..fe97fda8bfe9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -272,11 +272,15 @@ static void __smu_msg_v1_send(struct smu_msg_ctl *ctl, u16 index, { struct amdgpu_device *adev = ctl->smu->adev; struct smu_msg_config *cfg = &ctl->config; + u32 arg; int i; WREG32(cfg->resp_reg, 0); - for (i = 0; i < args->num_args; i++) - WREG32(cfg->arg_regs[i], args->args[i]); + for (i = 0; i < cfg->num_arg_regs; i++) { + /* NOTE: Clear unused argument registers to avoid stale values. */ + arg = i < args->num_args ? args->args[i] : 0; + WREG32(cfg->arg_regs[i], arg); + } WREG32(cfg->msg_reg, index); } diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 41c57063f3b4..0eb52d1d5af2 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -830,7 +830,7 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, case DRM_COLOROP_1D_LUT: drm_printf(p, "\tsize=%d\n", colorop->size); drm_printf(p, "\tinterpolation=%s\n", - drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation)); + drm_get_colorop_lut1d_interpolation_name(state->lut1d_interpolation)); drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; case DRM_COLOROP_CTM_3X4: @@ -842,7 +842,7 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, case DRM_COLOROP_3D_LUT: drm_printf(p, "\tsize=%d\n", colorop->size); drm_printf(p, "\tinterpolation=%s\n", - drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation)); + drm_get_colorop_lut3d_interpolation_name(state->lut3d_interpolation)); drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; default: diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 5bd5bf6661df..5eaf0e8a494b 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -265,13 +265,19 @@ EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); * * Helper function to select the color pipeline on a plane by setting * it to the first drm_colorop element of the pipeline. + * + * Return: true if plane color pipeline value changed, false otherwise. */ -void +bool drm_atomic_set_colorop_for_plane(struct drm_plane_state *plane_state, struct drm_colorop *colorop) { struct drm_plane *plane = plane_state->plane; + /* Color pipeline didn't change */ + if (plane_state->color_pipeline == colorop) + return false; + if (colorop) drm_dbg_atomic(plane->dev, "Set [COLOROP:%d] for [PLANE:%d:%s] state %p\n", @@ -283,6 +289,8 @@ drm_atomic_set_colorop_for_plane(struct drm_plane_state *plane_state, plane->base.id, plane->name, plane_state); plane_state->color_pipeline = colorop; + + return true; } EXPORT_SYMBOL(drm_atomic_set_colorop_for_plane); @@ -604,7 +612,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, if (val && !colorop) return -EACCES; - drm_atomic_set_colorop_for_plane(state, colorop); + state->color_mgmt_changed |= drm_atomic_set_colorop_for_plane(state, colorop); } else if (property == config->prop_fb_damage_clips) { ret = drm_property_replace_blob_from_id(dev, &state->fb_damage_clips, @@ -713,11 +721,11 @@ drm_atomic_plane_get_property(struct drm_plane *plane, static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + bool *replaced) { ssize_t elem_size = -1; ssize_t size = -1; - bool replaced = false; switch (colorop->type) { case DRM_COLOROP_1D_LUT: @@ -739,28 +747,45 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, &state->data, val, -1, size, elem_size, - &replaced); + replaced); } static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, struct drm_property *property, - uint64_t val) + uint64_t val, + bool *replaced) { if (property == colorop->bypass_property) { - state->bypass = val; + if (state->bypass != val) { + state->bypass = val; + *replaced = true; + } } else if (property == colorop->lut1d_interpolation_property) { - colorop->lut1d_interpolation = val; + if (state->lut1d_interpolation != val) { + state->lut1d_interpolation = val; + *replaced = true; + } } else if (property == colorop->curve_1d_type_property) { - state->curve_1d_type = val; + if (state->curve_1d_type != val) { + state->curve_1d_type = val; + *replaced = true; + } } else if (property == colorop->multiplier_property) { - state->multiplier = val; + if (state->multiplier != val) { + state->multiplier = val; + *replaced = true; + } } else if (property == colorop->lut3d_interpolation_property) { - colorop->lut3d_interpolation = val; + if (state->lut3d_interpolation != val) { + state->lut3d_interpolation = val; + *replaced = true; + } } else if (property == colorop->data_property) { return drm_atomic_color_set_data_property(colorop, state, - property, val); + property, val, + replaced); } else { drm_dbg_atomic(colorop->dev, "[COLOROP:%d:%d] unknown property [PROP:%d:%s]\n", @@ -782,7 +807,7 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, else if (property == colorop->bypass_property) *val = state->bypass; else if (property == colorop->lut1d_interpolation_property) - *val = colorop->lut1d_interpolation; + *val = state->lut1d_interpolation; else if (property == colorop->curve_1d_type_property) *val = state->curve_1d_type; else if (property == colorop->multiplier_property) @@ -790,7 +815,7 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, else if (property == colorop->size_property) *val = colorop->size; else if (property == colorop->lut3d_interpolation_property) - *val = colorop->lut3d_interpolation; + *val = state->lut3d_interpolation; else if (property == colorop->data_property) *val = (state->data) ? state->data->base.id : 0; else @@ -1275,8 +1300,10 @@ int drm_atomic_set_property(struct drm_atomic_state *state, break; } case DRM_MODE_OBJECT_COLOROP: { + struct drm_plane_state *plane_state; struct drm_colorop *colorop = obj_to_colorop(obj); struct drm_colorop_state *colorop_state; + bool replaced = false; colorop_state = drm_atomic_get_colorop_state(state, colorop); if (IS_ERR(colorop_state)) { @@ -1285,7 +1312,18 @@ int drm_atomic_set_property(struct drm_atomic_state *state, } ret = drm_atomic_colorop_set_property(colorop, colorop_state, - file_priv, prop, prop_value); + file_priv, prop, prop_value, + &replaced); + if (ret || !replaced) + break; + + plane_state = drm_atomic_get_plane_state(state, colorop->plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + break; + } + plane_state->color_mgmt_changed |= replaced; + break; } default: diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 566816e3c6f0..509678e5371f 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -342,7 +342,6 @@ int drm_plane_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_color colorop->lut1d_interpolation_property = prop; drm_object_attach_property(&colorop->base, prop, interpolation); - colorop->lut1d_interpolation = interpolation; /* data */ ret = drm_colorop_create_data_prop(dev, colorop); @@ -442,7 +441,6 @@ int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *col colorop->lut3d_interpolation_property = prop; drm_object_attach_property(&colorop->base, prop, interpolation); - colorop->lut3d_interpolation = interpolation; /* data */ ret = drm_colorop_create_data_prop(dev, colorop); @@ -521,6 +519,20 @@ static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state, &val); colorop_state->curve_1d_type = val; } + + if (colorop->lut1d_interpolation_property) { + if (!drm_object_property_get_default_value(&colorop->base, + colorop->lut1d_interpolation_property, + &val)) + colorop_state->lut1d_interpolation = val; + } + + if (colorop->lut3d_interpolation_property) { + if (!drm_object_property_get_default_value(&colorop->base, + colorop->lut3d_interpolation_property, + &val)) + colorop_state->lut3d_interpolation = val; + } } /** diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index e2b62e5fb891..2156dbe601c9 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -70,8 +70,11 @@ static int drm_mode_align_dumb(struct drm_mode_create_dumb *args, if (!pitch) return -EINVAL; - if (hw_pitch_align) + if (hw_pitch_align) { pitch = roundup(pitch, hw_pitch_align); + if (pitch < hw_pitch_align) + return -EINVAL; + } if (!hw_size_align) hw_size_align = PAGE_SIZE; @@ -80,7 +83,7 @@ static int drm_mode_align_dumb(struct drm_mode_create_dumb *args, if (check_mul_overflow(args->height, pitch, &size)) return -EINVAL; - size = ALIGN(size, hw_size_align); + size = roundup(size, hw_size_align); if (!size) return -EINVAL; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e3b8a1f353cb..3b2448a3a9de 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1015,12 +1015,25 @@ err: return ret; } +/* + * This ioctl is disabled for security reasons but also it failed + * to follow process in terms of adding testing in igt and verifying + * all the corner cases which made fixing security bugs in it even + * harder than necessary. + * + * To re-enable this ioctl + * 1. land working IGT tests in igt-gpu-tools that cover + * all corner cases and race conditions. + * 2. handle idr_preload + * 3. handle == 0 + * 4. handle == new_handle semantics definition. + */ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_gem_change_handle *args = data; - struct drm_gem_object *obj, *idrobj; - int handle, ret; + struct drm_gem_object *obj; + int new_handle, ret; if (!drm_core_check_feature(dev, DRIVER_GEM)) return -EOPNOTSUPP; @@ -1028,51 +1041,37 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, /* idr_alloc() limitation. */ if (args->new_handle > INT_MAX) return -EINVAL; - handle = args->new_handle; - - obj = drm_gem_object_lookup(file_priv, args->handle); - if (!obj) - return -ENOENT; + new_handle = args->new_handle; - if (args->handle == handle) { - ret = 0; - goto out; - } + if (args->handle == new_handle) + return 0; mutex_lock(&file_priv->prime.lock); - spin_lock(&file_priv->table_lock); - - /* When create_tail allocs an obj idr, it needs to first alloc as NULL, - * then later replace with the correct object. This is not necessary - * here, because the only operations that could race are drm_prime - * bookkeeping, and we hold the prime lock. - */ - ret = idr_alloc(&file_priv->object_idr, obj, handle, handle + 1, + ret = idr_alloc(&file_priv->object_idr, NULL, new_handle, new_handle + 1, GFP_NOWAIT); - if (ret < 0) { - spin_unlock(&file_priv->table_lock); - goto out_unlock; - } - - idrobj = idr_replace(&file_priv->object_idr, NULL, handle); - if (idrobj != obj) { - idr_replace(&file_priv->object_idr, idrobj, handle); - idr_remove(&file_priv->object_idr, args->new_handle); - spin_unlock(&file_priv->table_lock); - ret = -ENOENT; - goto out_unlock; - } + if (ret < 0) { + spin_unlock(&file_priv->table_lock); + goto out_unlock; + } + obj = idr_replace(&file_priv->object_idr, NULL, args->handle); + if (IS_ERR_OR_NULL(obj)) { + idr_remove(&file_priv->object_idr, new_handle); + spin_unlock(&file_priv->table_lock); + ret = -ENOENT; + goto out_unlock; + } spin_unlock(&file_priv->table_lock); if (obj->dma_buf) { ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf, - handle); + new_handle); if (ret < 0) { spin_lock(&file_priv->table_lock); - idr_remove(&file_priv->object_idr, handle); + idr_remove(&file_priv->object_idr, new_handle); + idr_replace(&file_priv->object_idr, obj, args->handle); spin_unlock(&file_priv->table_lock); goto out_unlock; } @@ -1084,14 +1083,12 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, spin_lock(&file_priv->table_lock); idr_remove(&file_priv->object_idr, args->handle); - idrobj = idr_replace(&file_priv->object_idr, obj, handle); + obj = idr_replace(&file_priv->object_idr, obj, new_handle); spin_unlock(&file_priv->table_lock); - WARN_ON(idrobj != NULL); + WARN_ON(obj != NULL); out_unlock: mutex_unlock(&file_priv->prime.lock); -out: - drm_gem_object_put(obj); return ret; } diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index ff193155129e..e2df4becce62 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -660,7 +660,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_gem_change_handle_ioctl, DRM_RENDER_ALLOW), + /* see drm_gem.c:drm_gem_change_handle_ioctl for why this is invalid */ + DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_invalid_op, DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, 0), diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index a891d4f1f843..552631c3554a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1791,8 +1791,9 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master, int ret; if (IS_ENABLED(CONFIG_DRM_ETNAVIV_THERMAL)) { - gpu->cooling = thermal_of_cooling_device_register(dev->of_node, - (char *)dev_name(dev), gpu, &cooling_ops); + gpu->cooling = thermal_of_cooling_device_register(dev->of_node, 0, + dev_name(dev), + gpu, &cooling_ops); if (IS_ERR(gpu->cooling)) return PTR_ERR(gpu->cooling); } diff --git a/drivers/gpu/drm/gma500/mmu.c b/drivers/gpu/drm/gma500/mmu.c index 6b6b44e426cf..4fbc22a59ac7 100644 --- a/drivers/gpu/drm/gma500/mmu.c +++ b/drivers/gpu/drm/gma500/mmu.c @@ -7,6 +7,8 @@ #include <linux/highmem.h> #include <linux/vmalloc.h> +#include <asm/cpuid/api.h> + #include "mmu.h" #include "psb_drv.h" #include "psb_reg.h" diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index 06b5d96e6eaf..b6bf6412ae34 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -150,6 +150,10 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, goto err_free_mmio; } + /* If DRM panic path is stubbed out VMBus code must do the unload */ + if (IS_ENABLED(CONFIG_DRM_PANIC)) + vmbus_set_skip_unload(true); + drm_client_setup(dev, NULL); return 0; @@ -169,6 +173,7 @@ static void hyperv_vmbus_remove(struct hv_device *hdev) struct drm_device *dev = hv_get_drvdata(hdev); struct hyperv_drm_device *hv = to_hv(dev); + vmbus_set_skip_unload(false); drm_dev_unplug(dev); drm_atomic_helper_shutdown(dev); vmbus_close(hdev->channel); diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 7978f8c8108c..d48ca6c23b7c 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -212,15 +212,16 @@ static void hyperv_plane_panic_flush(struct drm_plane *plane) struct hyperv_drm_device *hv = to_hv(plane->dev); struct drm_rect rect; - if (!plane->state || !plane->state->fb) - return; + if (plane->state && plane->state->fb) { + rect.x1 = 0; + rect.y1 = 0; + rect.x2 = plane->state->fb->width; + rect.y2 = plane->state->fb->height; - rect.x1 = 0; - rect.y1 = 0; - rect.x2 = plane->state->fb->width; - rect.y2 = plane->state->fb->height; + hyperv_update_dirt(hv->hdev, &rect); + } - hyperv_update_dirt(hv->hdev, &rect); + vmbus_initiate_unload(true); } static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = { diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index 051ecc526832..4e6f703a1b33 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -391,8 +391,11 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return -ETIMEDOUT; } - if (msg->resolution_resp.resolution_count == 0) { - drm_err(dev, "No supported resolutions\n"); + if (msg->resolution_resp.resolution_count == 0 || + msg->resolution_resp.resolution_count > + SYNTHVID_MAX_RESOLUTION_COUNT) { + drm_err(dev, "Invalid resolution count: %d\n", + msg->resolution_resp.resolution_count); return -ENODEV; } @@ -417,30 +420,92 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return 0; } -static void hyperv_receive_sub(struct hv_device *hdev) +static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) { struct hyperv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg; + size_t hdr_size; + size_t need; if (!hv) return; - msg = (struct synthvid_msg *)hv->recv_buf; - - /* Complete the wait event */ - if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { - memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE); - complete(&hv->wait); + hdr_size = sizeof(struct pipe_msg_hdr) + + sizeof(struct synthvid_msg_hdr); + if (bytes_recvd < hdr_size) { + drm_err_ratelimited(&hv->dev, + "synthvid packet too small for header: %u\n", + bytes_recvd); return; } - if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) { + msg = (struct synthvid_msg *)hv->recv_buf; + need = hdr_size; + + switch (msg->vid_hdr.type) { + case SYNTHVID_VERSION_RESPONSE: + need += sizeof(struct synthvid_version_resp); + break; + case SYNTHVID_RESOLUTION_RESPONSE: + /* + * The resolution response is variable length: the host + * fills resolution_count entries, not the full + * SYNTHVID_MAX_RESOLUTION_COUNT array. Require the fixed + * prefix first so resolution_count can be read, then + * demand exactly the count-sized array. + */ + need += offsetof(struct synthvid_supported_resolution_resp, + supported_resolution); + if (bytes_recvd < need) + break; + if (msg->resolution_resp.resolution_count > + SYNTHVID_MAX_RESOLUTION_COUNT) { + drm_err_ratelimited(&hv->dev, + "synthvid resolution count too large: %u\n", + msg->resolution_resp.resolution_count); + return; + } + need += msg->resolution_resp.resolution_count * + sizeof(struct hvd_screen_info); + break; + case SYNTHVID_VRAM_LOCATION_ACK: + need += sizeof(struct synthvid_vram_location_ack); + break; + case SYNTHVID_FEATURE_CHANGE: + /* + * Not a completion-driving message: validate its own payload + * and consume it here rather than falling through to the + * memcpy/complete shared by the wait-event responses. + */ + if (bytes_recvd < need + + sizeof(struct synthvid_feature_change)) { + drm_err_ratelimited(&hv->dev, + "synthvid feature change packet too small: %u\n", + bytes_recvd); + return; + } hv->dirt_needed = msg->feature_chg.is_dirt_needed; if (hv->dirt_needed) hyperv_hide_hw_ptr(hv->hdev); + return; + default: + return; + } + + /* + * Shared completion path for the wait-event responses + * (VERSION_RESPONSE, RESOLUTION_RESPONSE, VRAM_LOCATION_ACK): + * require the type-specific payload before handing the buffer to + * the waiter. + */ + if (bytes_recvd < need) { + drm_err_ratelimited(&hv->dev, + "synthvid packet too small for type %u: %u < %zu\n", + msg->vid_hdr.type, bytes_recvd, need); + return; } + memcpy(hv->init_buf, msg, bytes_recvd); + complete(&hv->wait); } static void hyperv_receive(void *ctx) @@ -461,9 +526,21 @@ static void hyperv_receive(void *ctx) ret = vmbus_recvpacket(hdev->channel, recv_buf, VMBUS_MAX_PACKET_SIZE, &bytes_recvd, &req_id); - if (bytes_recvd > 0 && - recv_buf->pipe_hdr.type == PIPE_MSG_DATA) - hyperv_receive_sub(hdev); + if (ret) { + /* + * A nonzero return (e.g. -ENOBUFS for an oversized + * packet) is itself a malformed message: bytes_recvd + * then reports the required length rather than a copied + * payload, so it must not be forwarded to the + * sub-handler. Channel recovery is not attempted. + */ + drm_err_ratelimited(&hv->dev, + "vmbus_recvpacket failed: %d (need %u)\n", + ret, bytes_recvd); + } else if (bytes_recvd > 0 && + recv_buf->pipe_hdr.type == PIPE_MSG_DATA) { + hyperv_receive_sub(hdev, bytes_recvd); + } } while (bytes_recvd > 0 && ret == 0); } @@ -508,9 +585,13 @@ int hyperv_connect_vsp(struct hv_device *hdev) ret = hyperv_get_supported_resolution(hdev); if (ret) drm_err(dev, "Failed to get supported resolution from host, use default\n"); - } else { + } + + if (!hv->screen_width_max) { hv->screen_width_max = SYNTHVID_WIDTH_WIN8; hv->screen_height_max = SYNTHVID_HEIGHT_WIN8; + hv->preferred_width = SYNTHVID_WIDTH_WIN8; + hv->preferred_height = SYNTHVID_HEIGHT_WIN8; } hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes; diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index e7950655434b..6d1cffc6d2be 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3976,7 +3976,7 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), (1 << 24)); - } while (i++ > 130); + } while (i++ < 130); } else { for (i = 0; i < lut_size; i++) { u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index d9baca2d5aaf..78afcd42f44c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -497,6 +497,7 @@ struct intel_display { u8 vblank_enabled; int vblank_enable_count; + bool vblank_status_last_notified; struct work_struct vblank_notify_work; diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index 70c1bba7c0a8..aedf3928a089 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1773,8 +1773,12 @@ static void intel_display_vblank_notify_work(struct work_struct *work) struct intel_display *display = container_of(work, typeof(*display), irq.vblank_notify_work); int vblank_enable_count = READ_ONCE(display->irq.vblank_enable_count); + bool vblank_status = !!vblank_enable_count; - intel_psr_notify_vblank_enable_disable(display, vblank_enable_count); + if (display->irq.vblank_status_last_notified != vblank_status) { + intel_psr_notify_vblank_enable_disable(display, vblank_status); + display->irq.vblank_status_last_notified = vblank_status; + } } int bdw_enable_vblank(struct drm_crtc *_crtc) @@ -1787,10 +1791,10 @@ int bdw_enable_vblank(struct drm_crtc *_crtc) if (gen11_dsi_configure_te(crtc, true)) return 0; + spin_lock_irqsave(&display->irq.lock, irqflags); if (crtc->vblank_psr_notify && display->irq.vblank_enable_count++ == 0) schedule_work(&display->irq.vblank_notify_work); - spin_lock_irqsave(&display->irq.lock, irqflags); bdw_enable_pipe_irq(display, pipe, GEN8_PIPE_VBLANK); spin_unlock_irqrestore(&display->irq.lock, irqflags); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 9c7c357afb09..2e6a85708555 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1790,6 +1790,8 @@ struct intel_psr { u8 active_non_psr_pipes; const char *no_psr_reason; + + struct ref_tracker *vblank_wakeref; }; struct intel_dp { diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 6ef2a0043cda..5c3e816b0135 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4678,10 +4678,17 @@ intel_edp_set_sink_rates(struct intel_dp *intel_dp) if (intel_dp->edp_dpcd[0] >= DP_EDP_14) { __le16 sink_rates[DP_MAX_SUPPORTED_RATES]; + int ret; int i; - drm_dp_dpcd_read(&intel_dp->aux, DP_SUPPORTED_LINK_RATES, - sink_rates, sizeof(sink_rates)); + ret = drm_dp_dpcd_read_data(&intel_dp->aux, + DP_SUPPORTED_LINK_RATES, + sink_rates, sizeof(sink_rates)); + if (ret < 0) { + drm_dbg_kms(display->drm, + "Unable to read eDP supported link rates, using default rates\n"); + memset(sink_rates, 0, sizeof(sink_rates)); + } for (i = 0; i < ARRAY_SIZE(sink_rates); i++) { int rate; diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c index b20ec3e589fa..9c9b6410366d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c @@ -12,6 +12,7 @@ #include "intel_dp.h" #include "intel_dp_aux.h" #include "intel_dp_aux_regs.h" +#include "intel_parent.h" #include "intel_pps.h" #include "intel_quirks.h" #include "intel_tc.h" @@ -60,18 +61,29 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp) struct intel_display *display = to_intel_display(intel_dp); i915_reg_t ch_ctl = intel_dp->aux_ch_ctl_reg(intel_dp); const unsigned int timeout_ms = 10; + bool done = true; u32 status; - bool done; + int ret; + if (intel_parent_irq_enabled(display)) { #define C (((status = intel_de_read_notrace(display, ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) - done = wait_event_timeout(display->gmbus.wait_queue, C, - msecs_to_jiffies_timeout(timeout_ms)); + done = wait_event_timeout(display->gmbus.wait_queue, C, + msecs_to_jiffies_timeout(timeout_ms)); + +#undef C + } else { + ret = intel_de_wait_ms(display, ch_ctl, + DP_AUX_CH_CTL_SEND_BUSY, 0, + timeout_ms, &status); + + if (ret == -ETIMEDOUT) + done = false; + } if (!done) drm_err(display->drm, "%s: did not complete or timeout within %ums (status 0x%08x)\n", intel_dp->aux.name, timeout_ms, status); -#undef C return status; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index a8d56ebf06a2..7a6c07f6aaeb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -691,10 +691,9 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_device *dev = connector->base.dev; struct intel_panel *panel = &connector->panel; - bool try_intel_interface = false; + bool try_intel_interface = false, try_vesa_interface = false; - /* - * Check the VBT and user's module parameters to figure out which + /* Check the VBT and user's module parameters to figure out which * interfaces to probe */ switch (display->params.enable_dpcd_backlight) { @@ -703,6 +702,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) case INTEL_DP_AUX_BACKLIGHT_AUTO: switch (panel->vbt.backlight.type) { case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE: + try_vesa_interface = true; break; case INTEL_BACKLIGHT_DISPLAY_DDI: try_intel_interface = true; @@ -715,12 +715,20 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) if (panel->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE) try_intel_interface = true; + try_vesa_interface = true; + break; + case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA: + try_vesa_interface = true; break; case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL: try_intel_interface = true; break; } + /* For eDP 1.5 and above we are supposed to use VESA interface for brightness control */ + if (intel_dp->edp_dpcd[0] >= DP_EDP_15) + try_vesa_interface = true; + /* * Since Intel has their own backlight control interface, the majority of machines out there * using DPCD backlight controls with Intel GPUs will be using this interface as opposed to @@ -733,9 +741,6 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) * panel with Intel's OUI - which is also required for us to be able to detect Intel's * backlight interface at all. This means that the only sensible way for us to detect both * interfaces is to probe for Intel's first, and VESA's second. - * - * Also there is a chance some VBTs may advertise false Intel backlight support even if the - * TCON DPCD says otherwise. This means we keep VESA interface as fallback in that case. */ if (try_intel_interface && intel_dp->edp_dpcd[0] <= DP_EDP_14b && intel_dp_aux_supports_hdr_backlight(connector)) { @@ -745,7 +750,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) return 0; } - if (intel_dp_aux_supports_vesa_backlight(connector)) { + if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) { drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Using VESA eDP backlight controls\n", connector->base.base.id, connector->base.name); panel->backlight.funcs = &intel_dp_vesa_bl_funcs; diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 82f445c83158..07eae4176dad 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -144,6 +144,15 @@ intel_plane_duplicate_state(struct drm_plane *plane) if (intel_state->hw.fb) drm_framebuffer_get(intel_state->hw.fb); + if (intel_state->hw.degamma_lut) + drm_property_blob_get(intel_state->hw.degamma_lut); + if (intel_state->hw.gamma_lut) + drm_property_blob_get(intel_state->hw.gamma_lut); + if (intel_state->hw.ctm) + drm_property_blob_get(intel_state->hw.ctm); + if (intel_state->hw.lut_3d) + drm_property_blob_get(intel_state->hw.lut_3d); + return &intel_state->uapi; } @@ -167,6 +176,16 @@ intel_plane_destroy_state(struct drm_plane *plane, __drm_atomic_helper_plane_destroy_state(&plane_state->uapi); if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); + kfree(plane_state); } @@ -317,6 +336,14 @@ static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state) { if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); memset(&plane_state->hw, 0, sizeof(plane_state->hw)); } diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 29904a037575..598fe769a402 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -4156,27 +4156,22 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display, struct intel_dp *intel_dp = enc_to_intel_dp(encoder); mutex_lock(&intel_dp->psr.lock); - if (intel_dp->psr.panel_replay_enabled) { - mutex_unlock(&intel_dp->psr.lock); - break; + if (CAN_PANEL_REPLAY(intel_dp)) { + if (enable) + intel_dp->psr.vblank_wakeref = + intel_display_power_get(display, + POWER_DOMAIN_DC_OFF); + else + intel_display_power_put(display, POWER_DOMAIN_DC_OFF, + intel_dp->psr.vblank_wakeref); } - if (intel_dp->psr.enabled && intel_dp->psr.pkg_c_latency_used) + if (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled && + intel_dp->psr.pkg_c_latency_used) intel_psr_apply_underrun_on_idle_wa_locked(intel_dp); mutex_unlock(&intel_dp->psr.lock); - return; } - - /* - * NOTE: intel_display_power_set_target_dc_state is used - * only by PSR * code for DC3CO handling. DC3CO target - * state is currently disabled in * PSR code. If DC3CO - * is taken into use we need take that into account here - * as well. - */ - intel_display_power_set_target_dc_state(display, enable ? DC_STATE_DISABLE : - DC_STATE_EN_UPTO_DC6); } static void diff --git a/drivers/gpu/drm/i915/gem/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/i915_gem_phys.c index e375afbf458e..d53129eb5603 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_phys.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_phys.c @@ -18,6 +18,17 @@ #include "i915_gem_tiling.h" #include "i915_scatterlist.h" +/* Abuse scatterlist to store pointer instead of struct page. */ +static inline void __set_phys_vaddr(struct scatterlist *sg, void *vaddr) +{ + sg_assign_page(sg, (struct page *)vaddr); +} + +static inline void *__get_phys_vaddr(struct scatterlist *sg) +{ + return (void *)sg_page(sg); +} + static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj) { struct address_space *mapping = obj->base.filp->f_mapping; @@ -58,7 +69,7 @@ static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj) sg->offset = 0; sg->length = obj->base.size; - sg_assign_page(sg, (struct page *)vaddr); + __set_phys_vaddr(sg, vaddr); sg_dma_address(sg) = dma; sg_dma_len(sg) = obj->base.size; @@ -99,7 +110,7 @@ i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj, struct sg_table *pages) { dma_addr_t dma = sg_dma_address(pages->sgl); - void *vaddr = sg_page(pages->sgl); + void *vaddr = __get_phys_vaddr(pages->sgl); __i915_gem_object_release_shmem(obj, pages, false); @@ -139,7 +150,7 @@ i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj, int i915_gem_object_pwrite_phys(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pwrite *args) { - void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset; + void *vaddr = __get_phys_vaddr(obj->mm.pages->sgl) + args->offset; char __user *user_data = u64_to_user_ptr(args->data_ptr); struct drm_i915_private *i915 = to_i915(obj->base.dev); int err; @@ -170,7 +181,7 @@ int i915_gem_object_pwrite_phys(struct drm_i915_gem_object *obj, int i915_gem_object_pread_phys(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pread *args) { - void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset; + void *vaddr = __get_phys_vaddr(obj->mm.pages->sgl) + args->offset; char __user *user_data = u64_to_user_ptr(args->data_ptr); int err; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index de70517b4ef2..df3fcc2b1248 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -419,8 +419,6 @@ void i915_ttm_free_cached_io_rsgt(struct drm_i915_gem_object *obj) int i915_ttm_purge(struct drm_i915_gem_object *obj) { struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); - struct i915_ttm_tt *i915_tt = - container_of(bo->ttm, typeof(*i915_tt), ttm); struct ttm_operation_ctx ctx = { .interruptible = true, .no_wait_gpu = false, @@ -435,16 +433,22 @@ int i915_ttm_purge(struct drm_i915_gem_object *obj) if (ret) return ret; - if (bo->ttm && i915_tt->filp) { - /* - * The below fput(which eventually calls shmem_truncate) might - * be delayed by worker, so when directly called to purge the - * pages(like by the shrinker) we should try to be more - * aggressive and release the pages immediately. - */ - shmem_truncate_range(file_inode(i915_tt->filp), - 0, (loff_t)-1); - fput(fetch_and_zero(&i915_tt->filp)); + if (bo->ttm) { + struct i915_ttm_tt *i915_tt = + container_of(bo->ttm, typeof(*i915_tt), ttm); + + if (i915_tt->filp) { + /* + * The below fput(which eventually calls shmem_truncate) + * might be delayed by worker, so when directly called + * to purge the pages(like by the shrinker) we should + * try to be more aggressive and release the pages + * immediately. + */ + shmem_truncate_range(file_inode(i915_tt->filp), + 0, (loff_t)-1); + fput(fetch_and_zero(&i915_tt->filp)); + } } obj->write_domain = 0; diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c index 32c3f46b21da..5c7f8d952ec1 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-scaler.c +++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c @@ -166,6 +166,7 @@ static int exp_approx_q(int x) * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] * @use_5_taps: indicates whether to use 5 taps or 7 taps + * @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output filter coefficients */ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps, @@ -262,7 +263,9 @@ static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps, * @src_length: length of input * @dst_length: length of output * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps + * @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output coefficients + * @nn_interpolation: whether to use nearest neighbor instead of gaussian filter */ static void dcss_scaler_filter_design(int src_length, int dst_length, bool use_5_taps, bool phase0_identity, diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 058c71c82cf5..897e42c8d5c8 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -330,17 +330,20 @@ static int msm_iommu_pagetable_prealloc_allocate(struct msm_mmu *mmu, struct msm_mmu_prealloc *p) { struct kmem_cache *pt_cache = get_pt_cache(mmu); - int ret; + + if (!p->count) { + p->pages = NULL; + return 0; + } p->pages = kvmalloc_objs(*p->pages, p->count); if (!p->pages) return -ENOMEM; - ret = kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, p->count, p->pages); - if (ret != p->count) { - kfree(p->pages); + if (!kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, p->count, p->pages)) { + kvfree(p->pages); p->pages = NULL; - p->count = ret; + p->count = 0; return -ENOMEM; } diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index b1af0a099551..aa08644012f7 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -51,9 +51,13 @@ kernel::auxiliary_device_table!( impl auxiliary::Driver for NovaDriver { type IdInfo = (); + type Data<'bound> = Self; const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE; - fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> { + fn probe<'bound>( + adev: &'bound auxiliary::Device<Core<'_>>, + _info: &'bound Self::IdInfo, + ) -> impl PinInit<Self, Error> + 'bound { let data = try_pin_init!(NovaData { adev: adev.into() }); let drm = drm::Device::<Self>::new(adev.as_ref(), data)?; diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 75d98dad7b1d..b12c641af46c 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -1274,13 +1274,13 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, goto err_cleanup; } - ret = kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, pt_count, - op_ctx->rsvd_page_tables.pages); - op_ctx->rsvd_page_tables.count = ret; - if (ret != pt_count) { + if (!kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, pt_count, + op_ctx->rsvd_page_tables.pages)) { + op_ctx->rsvd_page_tables.count = 0; ret = -ENOMEM; goto err_cleanup; } + op_ctx->rsvd_page_tables.count = pt_count; /* Insert BO into the extobj list last, when we know nothing can fail. */ dma_resv_lock(panthor_vm_resv(vm), NULL); @@ -1328,9 +1328,8 @@ static int panthor_vm_prepare_unmap_op_ctx(struct panthor_vm_op_ctx *op_ctx, goto err_cleanup; } - ret = kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, pt_count, - op_ctx->rsvd_page_tables.pages); - if (ret != pt_count) { + if (!kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, pt_count, + op_ctx->rsvd_page_tables.pages)) { ret = -ENOMEM; goto err_cleanup; } diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c index 17e1f34b7610..3402f993d7d3 100644 --- a/drivers/gpu/drm/tests/drm_rect_test.c +++ b/drivers/gpu/drm/tests/drm_rect_test.c @@ -10,6 +10,7 @@ #include <drm/drm_rect.h> #include <drm/drm_mode.h> +#include <linux/limits.h> #include <linux/string_helpers.h> #include <linux/errno.h> @@ -407,10 +408,27 @@ KUNIT_ARRAY_PARAM(drm_rect_scale, drm_rect_scale_cases, drm_rect_scale_case_desc static void drm_test_rect_calc_hscale(struct kunit *test) { const struct drm_rect_scale_case *params = test->param_value; - int scaling_factor; + int expected_warnings = params->expected_scaling_factor == -EINVAL; + int scaling_factor = INT_MIN; - scaling_factor = drm_rect_calc_hscale(¶ms->src, ¶ms->dst, - params->min_range, params->max_range); + /* + * Without CONFIG_BUG, WARN_ON() is a no-op and the suppressed warning + * count stays zero, failing the assertion. + */ + if (expected_warnings && !IS_ENABLED(CONFIG_BUG)) + kunit_skip(test, "requires CONFIG_BUG"); + + /* + * drm_rect_calc_hscale() generates a warning backtrace whenever bad + * parameters are passed to it. This affects unit tests with -EINVAL + * error code in expected_scaling_factor. + */ + kunit_warning_suppress(test) { + scaling_factor = drm_rect_calc_hscale(¶ms->src, ¶ms->dst, + params->min_range, + params->max_range); + KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings); + } KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor); } @@ -418,10 +436,26 @@ static void drm_test_rect_calc_hscale(struct kunit *test) static void drm_test_rect_calc_vscale(struct kunit *test) { const struct drm_rect_scale_case *params = test->param_value; - int scaling_factor; + int expected_warnings = params->expected_scaling_factor == -EINVAL; + int scaling_factor = INT_MIN; - scaling_factor = drm_rect_calc_vscale(¶ms->src, ¶ms->dst, - params->min_range, params->max_range); + /* + * Without CONFIG_BUG, WARN_ON() is a no-op and the suppressed warning + * count stays zero, failing the assertion. + */ + if (expected_warnings && !IS_ENABLED(CONFIG_BUG)) + kunit_skip(test, "requires CONFIG_BUG"); + + /* + * drm_rect_calc_vscale() generates a warning backtrace whenever bad + * parameters are passed to it. This affects unit tests with -EINVAL + * error code in expected_scaling_factor. + */ + kunit_warning_suppress(test) { + scaling_factor = drm_rect_calc_vscale(¶ms->src, ¶ms->dst, + params->min_range, params->max_range); + KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings); + } KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor); } diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 279710b36a10..04f83fcf0937 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -37,7 +37,7 @@ use crate::{ regs, // }; -pub(crate) type IoMem = kernel::io::mem::IoMem<SZ_2M>; +pub(crate) type IoMem = kernel::io::mem::IoMem<'static, SZ_2M>; pub(crate) struct TyrDrmDriver; @@ -91,12 +91,13 @@ kernel::of_device_table!( impl platform::Driver for TyrPlatformDriverData { type IdInfo = (); + type Data<'bound> = Self; const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); - fn probe( - pdev: &platform::Device<Core>, - _info: Option<&Self::IdInfo>, - ) -> impl PinInit<Self, Error> { + fn probe<'bound>( + pdev: &'bound platform::Device<Core<'_>>, + _info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit<Self, Error> + 'bound { let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?; let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?; @@ -109,7 +110,7 @@ impl platform::Driver for TyrPlatformDriverData { let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?; let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; - let iomem = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?; + let iomem = Arc::new(request.iomap_sized::<SZ_2M>()?.into_devres()?, GFP_KERNEL)?; issue_soft_reset(pdev.as_ref(), &iomem)?; gpu::l2_power_on(pdev.as_ref(), &iomem)?; diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 75d9eccd7966..dd7da419702f 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -213,6 +213,14 @@ v3d_clean_caches(struct v3d_dev *v3d) trace_v3d_cache_clean_begin(dev); + /* GFXH-1897: Ensure pending flushes complete before writing L2TCACTL */ + if (v3d->ver < V3D_GEN_71) { + if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & + V3D_L2TCACTL_L2TFLS), 100)) { + drm_err(dev, "Timeout waiting for L2T clean\n"); + } + } + V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & V3D_L2TCACTL_TMUWCF), 100)) { diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c index 8e0249580bba..ecfd446ff75f 100644 --- a/drivers/gpu/drm/v3d/v3d_perfmon.c +++ b/drivers/gpu/drm/v3d/v3d_perfmon.c @@ -309,8 +309,11 @@ static void v3d_perfmon_delete(struct v3d_file_priv *v3d_priv, if (perfmon == v3d->active_perfmon) v3d_perfmon_stop(v3d, perfmon, false); - /* If the global perfmon is being destroyed, set it to NULL */ - cmpxchg(&v3d->global_perfmon, perfmon, NULL); + /* If the global perfmon is being destroyed, clean it and release + * the reference stashed in v3d_perfmon_set_global_ioctl(). + */ + if (cmpxchg(&v3d->global_perfmon, perfmon, NULL) == perfmon) + v3d_perfmon_put(perfmon); v3d_perfmon_put(perfmon); } @@ -461,16 +464,27 @@ int v3d_perfmon_set_global_ioctl(struct drm_device *dev, void *data, /* If the request is to clear the global performance monitor */ if (req->flags & DRM_V3D_PERFMON_CLEAR_GLOBAL) { - if (!v3d->global_perfmon) + struct v3d_perfmon *old; + + /* DRM_V3D_PERFMON_CLEAR_GLOBAL doesn't check if + * v3d->global_perfmon == perfmon. Therefore, there + * is no need to keep perfmon's reference. + */ + v3d_perfmon_put(perfmon); + + old = xchg(&v3d->global_perfmon, NULL); + if (!old) return -EINVAL; - xchg(&v3d->global_perfmon, NULL); + v3d_perfmon_put(old); return 0; } - if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) + if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) { + v3d_perfmon_put(perfmon); return -EBUSY; + } return 0; } diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 94bf628dc91c..8a635a9ec046 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -352,6 +352,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) return NULL; } + /* The HW interprets a workgroup size of 0 as 65536; however, the + * user-space driver exposes a maximum of 65535. Therefore, a 0 in + * any dimension means that we have no workgroups and the compute + * shader should not be dispatched. + */ + if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) || + !V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) || + !V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z)) + return NULL; + v3d->queue[V3D_CSD].active_job = &job->base; v3d_invalidate_caches(v3d); @@ -402,13 +412,13 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) wg_counts = (uint32_t *)(bo->vaddr + indirect_csd->offset); - if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) - return; - args->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT; args->cfg[2] = wg_counts[2] << V3D_CSD_CFG012_WG_COUNT_SHIFT; + if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) + goto unmap_bo; + num_batches = DIV_ROUND_UP(indirect_csd->wg_size, 16) * (wg_counts[0] * wg_counts[1] * wg_counts[2]); @@ -428,6 +438,7 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) } } +unmap_bo: v3d_put_bo_vaddr(indirect); v3d_put_bo_vaddr(bo); } diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index d48cf76983c0..66502a6a4a8e 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -290,15 +290,16 @@ static bool require_uniform_address_uniform(struct vc4_validated_shader_info *va { uint32_t o = validated_shader->num_uniform_addr_offsets; uint32_t num_uniforms = validated_shader->uniforms_size / 4; + u32 *offsets; - validated_shader->uniform_addr_offsets = - krealloc(validated_shader->uniform_addr_offsets, - (o + 1) * - sizeof(*validated_shader->uniform_addr_offsets), - GFP_KERNEL); - if (!validated_shader->uniform_addr_offsets) + offsets = krealloc_array(validated_shader->uniform_addr_offsets, + o + 1, + sizeof(*validated_shader->uniform_addr_offsets), + GFP_KERNEL); + if (!offsets) return false; + validated_shader->uniform_addr_offsets = offsets; validated_shader->uniform_addr_offsets[o] = num_uniforms; validated_shader->num_uniform_addr_offsets++; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index a5ce96fb8a1d..9af740bda835 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -124,7 +124,10 @@ static void virtio_gpu_remove(struct virtio_device *vdev) struct drm_device *dev = vdev->priv; drm_dev_unplug(dev); - drm_atomic_helper_shutdown(dev); + + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) + drm_atomic_helper_shutdown(dev); + virtio_gpu_deinit(dev); drm_dev_put(dev); } diff --git a/drivers/gpu/drm/virtio/virtgpu_submit.c b/drivers/gpu/drm/virtio/virtgpu_submit.c index dae761fa5788..32cb1e4aa425 100644 --- a/drivers/gpu/drm/virtio/virtgpu_submit.c +++ b/drivers/gpu/drm/virtio/virtgpu_submit.c @@ -65,8 +65,10 @@ static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit, dma_fence_unwrap_for_each(f, &itr, fence) { err = virtio_gpu_do_fence_wait(submit, f); - if (err) + if (err) { + dma_fence_put(itr.chain); return err; + } } return 0; diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 00dfa68af29a..b17fb698d2f8 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -124,6 +124,15 @@ int xe_display_init_early(struct xe_device *xe) intel_display_driver_early_probe(display); + intel_display_device_info_runtime_init(display); + + /* Display may have been disabled at runtime init */ + if (!intel_display_device_present(display)) { + xe->info.probe_display = false; + unset_display_features(xe); + return 0; + } + /* Early display init.. */ intel_opregion_setup(display); @@ -137,8 +146,6 @@ int xe_display_init_early(struct xe_device *xe) intel_bw_init_hw(display); - intel_display_device_info_runtime_init(display); - err = intel_display_driver_probe_noirq(display); if (err) goto err_opregion; diff --git a/drivers/gpu/drm/xe/xe_drm_ras.c b/drivers/gpu/drm/xe/xe_drm_ras.c index e07dc23a155e..c6cd32b7eeda 100644 --- a/drivers/gpu/drm/xe/xe_drm_ras.c +++ b/drivers/gpu/drm/xe/xe_drm_ras.c @@ -52,7 +52,7 @@ static struct xe_drm_ras_counter *allocate_and_copy_counters(struct xe_device *x struct xe_drm_ras_counter *counter; int i; - counter = kcalloc(DRM_XE_RAS_ERR_COMP_MAX, sizeof(*counter), GFP_KERNEL); + counter = drmm_kcalloc(&xe->drm, DRM_XE_RAS_ERR_COMP_MAX, sizeof(*counter), GFP_KERNEL); if (!counter) return ERR_PTR(-ENOMEM); @@ -100,54 +100,47 @@ static int assign_node_params(struct xe_device *xe, struct drm_ras_node *node, return 0; } -static void cleanup_node_param(struct xe_drm_ras *ras, const enum drm_xe_ras_error_severity severity) +static void cleanup_node_param(struct drm_ras_node *node) { - struct drm_ras_node *node = &ras->node[severity]; - - kfree(ras->info[severity]); - ras->info[severity] = NULL; - kfree(node->device_name); node->device_name = NULL; } +static void cleanup_node(struct drm_device *drm, void *node) +{ + drm_ras_node_unregister(node); + cleanup_node_param(node); +} + static int register_nodes(struct xe_device *xe) { struct xe_drm_ras *ras = &xe->ras; - int i; + struct drm_ras_node *node; + int i, ret; for_each_error_severity(i) { - struct drm_ras_node *node = &ras->node[i]; - int ret; + node = &ras->node[i]; ret = assign_node_params(xe, node, i); - if (ret) { - cleanup_node_param(ras, i); - return ret; - } + if (ret) + goto free_param; ret = drm_ras_node_register(node); - if (ret) { - cleanup_node_param(ras, i); - return ret; - } + if (ret) + goto free_param; + + ret = drmm_add_action_or_reset(&xe->drm, cleanup_node, node); + if (ret) + goto null_info; } return 0; -} - -static void xe_drm_ras_unregister_nodes(struct drm_device *device, void *arg) -{ - struct xe_device *xe = arg; - struct xe_drm_ras *ras = &xe->ras; - int i; - - for_each_error_severity(i) { - struct drm_ras_node *node = &ras->node[i]; - drm_ras_node_unregister(node); - cleanup_node_param(ras, i); - } +free_param: + cleanup_node_param(node); +null_info: + ras->info[i] = NULL; + return ret; } /** @@ -176,11 +169,5 @@ int xe_drm_ras_init(struct xe_device *xe) return err; } - err = drmm_add_action_or_reset(&xe->drm, xe_drm_ras_unregister_nodes, xe); - if (err) { - drm_err(&xe->drm, "Failed to add action for Xe DRM RAS (%pe)\n", ERR_PTR(err)); - return err; - } - return 0; } diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h index a82d99bd77bc..0225426c57b0 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.h +++ b/drivers/gpu/drm/xe/xe_exec_queue.h @@ -162,21 +162,4 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch); struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q); struct xe_lrc *xe_exec_queue_get_lrc(struct xe_exec_queue *q, u16 idx); -/** - * xe_exec_queue_idle_skip_suspend() - Can exec queue skip suspend - * @q: The exec_queue - * - * If an exec queue is not parallel and is idle, the suspend steps can be - * skipped in the submission backend immediatley signaling the suspend fence. - * Parallel queues cannot skip this step due to limitations in the submission - * backend. - * - * Return: True if exec queue is idle and can skip suspend steps, False - * otherwise - */ -static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q); -} - #endif diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index 2b835d48b565..5760251cb685 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -767,6 +767,11 @@ static unsigned int guc_mmio_regset_write(struct xe_guc_ads *ads, } } + if (XE_GT_WA(hwe->gt, 16023105232)) + guc_mmio_regset_write_one(ads, regset_map, + RING_IDLEDLY(hwe->mmio_base), + count++); + return count; } diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 912182dc7704..42110e01b7d0 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -71,7 +71,6 @@ exec_queue_to_guc(struct xe_exec_queue *q) #define EXEC_QUEUE_STATE_WEDGED (1 << 8) #define EXEC_QUEUE_STATE_BANNED (1 << 9) #define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 10) -#define EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND (1 << 11) static bool exec_queue_registered(struct xe_exec_queue *q) { @@ -158,6 +157,11 @@ static void set_exec_queue_banned(struct xe_exec_queue *q) atomic_or(EXEC_QUEUE_STATE_BANNED, &q->guc->state); } +static void clear_exec_queue_banned(struct xe_exec_queue *q) +{ + atomic_andnot(EXEC_QUEUE_STATE_BANNED, &q->guc->state); +} + static bool exec_queue_suspended(struct xe_exec_queue *q) { return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_SUSPENDED; @@ -218,21 +222,6 @@ static void clear_exec_queue_pending_resume(struct xe_exec_queue *q) atomic_and(~EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state); } -static bool exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND; -} - -static void set_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_or(EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - -static void clear_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_and(~EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q) { return (atomic_read(&q->guc->state) & @@ -1153,7 +1142,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (!job->restore_replay || job->last_replay) { if (xe_exec_queue_is_parallel(q)) wq_item_append(q); - else if (!exec_queue_idle_skip_suspend(q)) + else xe_lrc_set_ring_tail(lrc, lrc->ring.tail); job->last_replay = false; } @@ -1163,9 +1152,12 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) /* * All queues in a multi-queue group will use the primary queue - * of the group to interface with GuC. + * of the group to interface with GuC. If primay is suspended, + * just return. Jobs will get scheduled once primary is resumed. */ q = xe_exec_queue_multi_queue_primary(q); + if (exec_queue_suspended(q)) + return; if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) { action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET; @@ -1374,7 +1366,8 @@ static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job) xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job), q->guc->id); - return xe_sched_invalidate_job(job, 2); + /* GuC never scheduled this job - let the caller trigger a GT reset. */ + return true; } ctx_timestamp = lower_32_bits(xe_lrc_timestamp(q->lrc[0])); @@ -1471,6 +1464,21 @@ static void disable_scheduling(struct xe_exec_queue *q, bool immediate) G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1); } +/* + * Recover via GT reset for a kernel queue, or for a GuC scheduling failure (job + * never started) on a queue that was not already killed or banned. An already + * banned queue must stay banned, so its unstarted jobs do not clear the ban or + * trigger a reset. + */ +static bool timeout_needs_gt_reset(struct xe_exec_queue *q, struct xe_sched_job *job, + bool skip_timeout_check) +{ + if (q->flags & EXEC_QUEUE_FLAG_KERNEL) + return true; + + return !skip_timeout_check && !xe_sched_job_started(job); +} + static enum drm_gpu_sched_stat guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) { @@ -1619,19 +1627,19 @@ trigger_reset: xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job), q->guc->id, q->flags); - /* - * Kernel jobs should never fail, nor should VM jobs if they do - * somethings has gone wrong and the GT needs a reset - */ - xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_KERNEL, - "Kernel-submitted job timed out\n"); - xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q), - "VM job timed out on non-killed execqueue\n"); - if (!wedged && (q->flags & EXEC_QUEUE_FLAG_KERNEL || - (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)))) { - if (!xe_sched_invalidate_job(job, 2)) { - xe_gt_reset_async(q->gt); - goto rearm; + if (!wedged) { + if (timeout_needs_gt_reset(q, job, skip_timeout_check)) { + if (!xe_sched_invalidate_job(job, 2)) { + clear_exec_queue_banned(q); + xe_gt_reset_async(q->gt); + goto rearm; + } + if (q->flags & EXEC_QUEUE_FLAG_KERNEL) { + xe_gt_WARN(q->gt, true, "Kernel-submitted job timed out\n"); + xe_device_declare_wedged(gt_to_xe(q->gt)); + } + } else if (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)) { + xe_gt_WARN(q->gt, true, "VM job timed out on non-killed execqueue\n"); } } @@ -1810,10 +1818,9 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; struct xe_guc *guc = exec_queue_to_guc(q); - bool idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && guc_exec_queue_allowed_to_change_state(q) && - !exec_queue_suspended(q) && exec_queue_enabled(q)) { + if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) && + exec_queue_enabled(q)) { wait_event(guc->ct.wq, vf_recovery(guc) || ((q->guc->resume_time != RESUME_PENDING || xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q))); @@ -1832,33 +1839,11 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) disable_scheduling(q, false); } } else if (q->guc->suspend_pending) { - if (idle_skip_suspend) - set_exec_queue_idle_skip_suspend(q); set_exec_queue_suspended(q); suspend_fence_signal(q); } } -static void sched_context(struct xe_exec_queue *q) -{ - struct xe_guc *guc = exec_queue_to_guc(q); - struct xe_lrc *lrc = q->lrc[0]; - u32 action[] = { - XE_GUC_ACTION_SCHED_CONTEXT, - q->guc->id, - }; - - xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_parallel(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); - xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q)); - - trace_xe_exec_queue_submit(q); - - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0); -} - static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; @@ -1866,22 +1851,12 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) if (guc_exec_queue_allowed_to_change_state(q)) { clear_exec_queue_suspended(q); if (!exec_queue_enabled(q)) { - if (exec_queue_idle_skip_suspend(q)) { - struct xe_lrc *lrc = q->lrc[0]; - - clear_exec_queue_idle_skip_suspend(q); - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - } q->guc->resume_time = RESUME_PENDING; set_exec_queue_pending_resume(q); enable_scheduling(q); - } else if (exec_queue_idle_skip_suspend(q)) { - clear_exec_queue_idle_skip_suspend(q); - sched_context(q); } } else { clear_exec_queue_suspended(q); - clear_exec_queue_idle_skip_suspend(q); } } @@ -2853,8 +2828,8 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q)); if (q->guc->suspend_pending) { - suspend_fence_signal(q); clear_exec_queue_pending_disable(q); + suspend_fence_signal(q); } else { if (exec_queue_banned(q)) { smp_wmb(); diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index ced58f46f846..cf6d106e6036 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -255,9 +255,8 @@ static int send_tlb_inval_ctx_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, #undef EXEC_QUEUE_COUNT_FULL_THRESHOLD /* - * Move exec queues to a temporary list to issue invalidations. The exec - * queue must active and a reference must be taken to prevent concurrent - * deregistrations. + * Move exec queues to a temporary list to issue invalidations. A + * reference must be taken to prevent concurrent deregistrations. * * List modification is safe because we hold 'vm->exec_queues.lock' for * reading, which prevents external modifications. Using a per-GT list @@ -266,7 +265,7 @@ static int send_tlb_inval_ctx_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, */ list_for_each_entry_safe(q, next, &vm->exec_queues.list[id], vm_exec_queue_link) { - if (q->ops->active(q) && xe_exec_queue_get_unless_zero(q)) { + if (xe_exec_queue_get_unless_zero(q)) { last_q = q; list_move_tail(&q->vm_exec_queue_link, &tlb_inval_list); } diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.c b/drivers/gpu/drm/xe/xe_hw_engine_group.c index 4c2b113364d3..02cf32ae5aa9 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_group.c +++ b/drivers/gpu/drm/xe/xe_hw_engine_group.c @@ -208,21 +208,15 @@ static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group lockdep_assert_held_write(&group->mode_sem); list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { - bool idle_skip_suspend; if (!xe_vm_in_fault_mode(q->vm)) continue; - idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && has_deps) + if (has_deps) return -EAGAIN; xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1); - if (idle_skip_suspend) - xe_gt_stats_incr(q->gt, - XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1); - - need_resume |= !idle_skip_suspend; + need_resume = true; q->ops->suspend(q); gt = q->gt; } diff --git a/drivers/gpu/drm/xe/xe_hw_error.c b/drivers/gpu/drm/xe/xe_hw_error.c index 2a31b430570e..e869bc3948d9 100644 --- a/drivers/gpu/drm/xe/xe_hw_error.c +++ b/drivers/gpu/drm/xe/xe_hw_error.c @@ -219,9 +219,9 @@ static void log_hw_error(struct xe_tile *tile, const char *name, struct xe_device *xe = tile_to_xe(tile); if (severity == DRM_XE_RAS_ERR_SEV_CORRECTABLE) - drm_warn(&xe->drm, "%s %s detected\n", name, severity_str); + drm_warn(&xe->drm, HW_ERR "%s %s detected\n", name, severity_str); else - drm_err_ratelimited(&xe->drm, "%s %s detected\n", name, severity_str); + drm_err_ratelimited(&xe->drm, HW_ERR "%s %s detected\n", name, severity_str); } static void log_gt_err(struct xe_tile *tile, const char *name, int i, u32 err, @@ -231,10 +231,10 @@ static void log_gt_err(struct xe_tile *tile, const char *name, int i, u32 err, struct xe_device *xe = tile_to_xe(tile); if (severity == DRM_XE_RAS_ERR_SEV_CORRECTABLE) - drm_warn(&xe->drm, "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", + drm_warn(&xe->drm, HW_ERR "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", name, severity_str, i, err); else - drm_err_ratelimited(&xe->drm, "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", + drm_err_ratelimited(&xe->drm, HW_ERR "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", name, severity_str, i, err); } @@ -251,9 +251,9 @@ static void log_soc_error(struct xe_tile *tile, const char * const *reg_info, if (strcmp(name, "Undefined")) { if (severity == DRM_XE_RAS_ERR_SEV_CORRECTABLE) - drm_warn(&xe->drm, "%s SOC %s detected", name, severity_str); + drm_warn(&xe->drm, HW_ERR "%s SOC %s detected", name, severity_str); else - drm_err_ratelimited(&xe->drm, "%s SOC %s detected", name, severity_str); + drm_err_ratelimited(&xe->drm, HW_ERR "%s SOC %s detected", name, severity_str); atomic_inc(&info[index].counter); } } diff --git a/drivers/gpu/drm/xe/xe_range_fence.c b/drivers/gpu/drm/xe/xe_range_fence.c index 372378e89e98..3d8fa194a7b0 100644 --- a/drivers/gpu/drm/xe/xe_range_fence.c +++ b/drivers/gpu/drm/xe/xe_range_fence.c @@ -77,6 +77,8 @@ int xe_range_fence_insert(struct xe_range_fence_tree *tree, } else if (err == 0) { xe_range_fence_tree_insert(rfence, &tree->root); return 0; + } else { + dma_fence_put(fence); } free: diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c index 9cebb2490245..18ebefd444fe 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -115,7 +115,6 @@ struct fw_blobs_by_type { #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ - fw_def(NOVALAKE_S, GT_TYPE_ANY, mmp_ver(xe, guc, nvl, 70, 55, 4)) \ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 54, 0)) \ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 54, 0)) \ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \ diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs index 02efdcf78d89..660c3911402d 100644 --- a/drivers/gpu/nova-core/bitfield.rs +++ b/drivers/gpu/nova-core/bitfield.rs @@ -170,7 +170,7 @@ macro_rules! bitfield { (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => { #[allow(clippy::eq_op)] const _: () = { - ::kernel::build_assert!( + ::kernel::build_assert::build_assert!( $hi == $lo, concat!("boolean field `", stringify!($field), "` covers more than one bit") ); @@ -181,7 +181,7 @@ macro_rules! bitfield { (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => { #[allow(clippy::eq_op)] const _: () = { - ::kernel::build_assert!( + ::kernel::build_assert::build_assert!( $hi >= $lo, concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB") ); diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 84b0e1703150..d3f2245ba2e0 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -3,7 +3,6 @@ use kernel::{ auxiliary, device::Core, - devres::Devres, dma::Device, dma::DmaMask, pci, @@ -21,6 +20,7 @@ use kernel::{ }, Arc, }, + types::ForLt, }; use crate::gpu::Gpu; @@ -29,13 +29,15 @@ use crate::gpu::Gpu; static AUXILIARY_ID_COUNTER: Atomic<u32> = Atomic::new(0); #[pin_data] -pub(crate) struct NovaCore { +pub(crate) struct NovaCore<'bound> { #[pin] pub(crate) gpu: Gpu, - #[pin] - _reg: Devres<auxiliary::Registration>, + #[allow(clippy::type_complexity)] + _reg: auxiliary::Registration<'bound, ForLt!(())>, } +pub(crate) struct NovaCoreDriver; + const BAR0_SIZE: usize = SZ_16M; // For now we only support Ampere which can use up to 47-bit DMA addresses. @@ -46,12 +48,12 @@ const BAR0_SIZE: usize = SZ_16M; // DMA addresses. These systems should be quite rare. const GPU_DMA_BITS: u32 = 47; -pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>; +pub(crate) type Bar0 = pci::Bar<'static, BAR0_SIZE>; kernel::pci_device_table!( PCI_TABLE, MODULE_PCI_TABLE, - <NovaCore as pci::Driver>::IdInfo, + <NovaCoreDriver as pci::Driver>::IdInfo, [ // Modern NVIDIA GPUs will show up as either VGA or 3D controllers. ( @@ -73,11 +75,15 @@ kernel::pci_device_table!( ] ); -impl pci::Driver for NovaCore { +impl pci::Driver for NovaCoreDriver { type IdInfo = (); + type Data<'bound> = NovaCore<'bound>; const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; - fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> { + fn probe<'bound>( + pdev: &'bound pci::Device<Core<'_>>, + _info: &'bound Self::IdInfo, + ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound { pin_init::pin_init_scope(move || { dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); @@ -89,26 +95,28 @@ impl pci::Driver for NovaCore { // other threads of execution. unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? }; - let bar = Arc::pin_init( - pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0"), + let bar = Arc::new( + pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0")? + .into_devres()?, GFP_KERNEL, )?; - Ok(try_pin_init!(Self { + Ok(try_pin_init!(NovaCore { gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), - _reg <- auxiliary::Registration::new( + _reg: auxiliary::Registration::new( pdev.as_ref(), c"nova-drm", // TODO[XARR]: Use XArray or perhaps IDA for proper ID allocation/recycling. For // now, use a simple atomic counter that never recycles IDs. AUXILIARY_ID_COUNTER.fetch_add(1, Relaxed), - crate::MODULE_NAME - ), + crate::MODULE_NAME, + (), + )?, })) }) } - fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) { + fn unbind<'bound>(pdev: &'bound pci::Device<Core<'_>>, this: Pin<&Self::Data<'bound>>) { this.gpu.unbind(pdev.as_ref()); } } diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 6c2ab69cb605..ad37994ac15a 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -48,7 +48,7 @@ fn request_firmware( /// Structure used to describe some firmwares, notably FWSEC-FRTS. #[repr(C)] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, FromBytes)] pub(crate) struct FalconUCodeDescV2 { /// Header defined by 'NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*' in OpenRM. hdr: u32, @@ -84,9 +84,6 @@ pub(crate) struct FalconUCodeDescV2 { pub(crate) alt_dmem_load_size: u32, } -// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. -unsafe impl FromBytes for FalconUCodeDescV2 {} - /// Structure used to describe some firmwares, notably FWSEC-FRTS. #[repr(C)] #[derive(Debug, Clone)] diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 0f6fe9a1b955..4ffb506342a9 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -278,7 +278,7 @@ impl Gpu { /// Called when the corresponding [`Device`](device::Device) is unbound. /// /// Note: This method must only be called from `Driver::unbind`. - pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) { + pub(crate) fn unbind(&self, dev: &device::Device<device::Core<'_>>) { kernel::warn_on!(self .bar .access(dev) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 275da9b1ee0e..1c9b2085f5e4 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -237,7 +237,7 @@ impl DmaGspMem { let start = gsp_mem.dma_handle(); // Write values one by one to avoid an on-stack instance of `PteArray`. for i in 0..GspMem::PTE_ARRAY_SIZE { - dma_write!(gsp_mem, .ptes.0[i], PteArray::<0>::entry(start, i)?); + dma_write!(gsp_mem, .ptes.0[build: i], PteArray::<0>::entry(start, i)?); } dma_write!( @@ -260,7 +260,7 @@ impl DmaGspMem { let rx = self.gsp_read_ptr(); // Pointer to the first entry of the CPU message queue. - let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[0]); + let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[build: 0]); let (tail_end, wrap_end) = if rx == 0 { // The write area is non-wrapping, and stops at the second-to-last entry of the command @@ -322,7 +322,7 @@ impl DmaGspMem { let rx = self.cpu_read_ptr(); // Pointer to the first entry of the GSP message queue. - let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[0]); + let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[build: 0]); let (tail_end, wrap_end) = if rx <= tx { // Read area is non-wrapping and stops right before `tx`. diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 04a1fa6b25f8..073d87714d3a 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -47,7 +47,7 @@ struct NovaCoreModule { // Fields are dropped in declaration order, so `_driver` is dropped first, // then `_debugfs_guard` clears `DEBUGFS_ROOT`. #[pin] - _driver: Registration<pci::Adapter<driver::NovaCore>>, + _driver: Registration<pci::Adapter<driver::NovaCoreDriver>>, _debugfs_guard: DebugfsRootGuard, } diff --git a/drivers/gpu/nova-core/num.rs b/drivers/gpu/nova-core/num.rs index 6c824b8d7b97..6eb174d136ab 100644 --- a/drivers/gpu/nova-core/num.rs +++ b/drivers/gpu/nova-core/num.rs @@ -49,7 +49,7 @@ macro_rules! impl_safe_as { #[allow(unused)] #[inline(always)] pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into { - kernel::static_assert!(size_of::<$into>() >= size_of::<$from>()); + ::kernel::build_assert::static_assert!(size_of::<$into>() >= size_of::<$from>()); value as $into } diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index ebda28e596c5..8b7d17a24660 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -16,6 +16,8 @@ use kernel::{ transmute::FromBytes, }; +use zerocopy::FromBytes as _; + use crate::{ driver::Bar0, firmware::{ @@ -1011,8 +1013,8 @@ impl FwSecBiosImage { let data = self.base.data.get(falcon_ucode_offset..).ok_or(EINVAL)?; match ver { 2 => { - let v2 = FalconUCodeDescV2::from_bytes_copy_prefix(data) - .ok_or(EINVAL)? + let v2 = FalconUCodeDescV2::read_from_prefix(data) + .map_err(|_| EINVAL)? .0; Ok(FalconUCodeDesc::V2(v2)) } diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig index eb8b14ab22c3..bad4bcee313f 100644 --- a/drivers/gpu/vga/Kconfig +++ b/drivers/gpu/vga/Kconfig @@ -4,7 +4,7 @@ config VGA_SWITCHEROO depends on X86 depends on ACPI depends on PCI - depends on (FRAMEBUFFER_CONSOLE=n || FB=y) + depends on FB=y select VGA_ARB help Many laptops released in 2008/9/10 have two GPUs with a multiplexer diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 8fe1ae3c71bb..22cf52b78b75 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -31,11 +31,9 @@ #define pr_fmt(fmt) "vga_switcheroo: " fmt #include <linux/apple-gmux.h> -#include <linux/console.h> #include <linux/debugfs.h> #include <linux/fb.h> #include <linux/fs.h> -#include <linux/fbcon.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/pm_domain.h> @@ -735,8 +733,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) if (!active->driver_power_control) set_audio_state(active->id, VGA_SWITCHEROO_OFF); +#if defined(CONFIG_FB) if (new_client->fb_info) - fbcon_remap_all(new_client->fb_info); + fb_switch_outputs(new_client->fb_info); +#endif mutex_lock(&vgasr_priv.mux_hw_lock); ret = vgasr_priv.handler->switchto(new_client->id); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4657d96fb083..426ff78c1c03 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1284,6 +1284,7 @@ #define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f #define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002 +#define USB_DEVICE_ID_SIGMA_MICRO_USB_MOUSE 0x0034 #define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD2 0x0059 #define USB_VENDOR_ID_SIGMATEL 0x066F diff --git a/drivers/hid/hid-lenovo-go-s.c b/drivers/hid/hid-lenovo-go-s.c index ff1782a75191..a72f7f748cb5 100644 --- a/drivers/hid/hid-lenovo-go-s.c +++ b/drivers/hid/hid-lenovo-go-s.c @@ -382,11 +382,9 @@ static int get_endpoint_address(struct hid_device *hdev) struct usb_interface *intf = to_usb_interface(hdev->dev.parent); struct usb_host_endpoint *ep; - if (intf) { - ep = intf->cur_altsetting->endpoint; - if (ep) - return ep->desc.bEndpointAddress; - } + ep = intf->cur_altsetting->endpoint; + if (ep) + return ep->desc.bEndpointAddress; return -ENODEV; } @@ -1461,6 +1459,9 @@ static int hid_gos_probe(struct hid_device *hdev, { int ret, ep; + if (!hid_is_usb(hdev)) + return -EINVAL; + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "Parse failed\n"); diff --git a/drivers/hid/hid-lenovo-go.c b/drivers/hid/hid-lenovo-go.c index d4d26c783356..e0c9d5ec9451 100644 --- a/drivers/hid/hid-lenovo-go.c +++ b/drivers/hid/hid-lenovo-go.c @@ -641,9 +641,6 @@ static int get_endpoint_address(struct hid_device *hdev) struct usb_interface *intf = to_usb_interface(hdev->dev.parent); struct usb_host_endpoint *ep; - if (!intf) - return -ENODEV; - ep = intf->cur_altsetting->endpoint; if (!ep) return -ENODEV; @@ -2419,6 +2416,9 @@ static int hid_go_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, ep; + if (!hid_is_usb(hdev)) + return -EINVAL; + hdev->quirks |= HID_QUIRK_INPUT_PER_APP | HID_QUIRK_MULTI_INPUT; ret = hid_parse(hdev); diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index a6b73e03c16b..c11957ae8b77 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -30,6 +30,7 @@ #include <linux/hid.h> #include <linux/input.h> #include <linux/leds.h> +#include <linux/unaligned.h> #include <linux/workqueue.h> #include "hid-ids.h" @@ -793,8 +794,8 @@ static int lenovo_raw_event(struct hid_device *hdev, */ if (unlikely((hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB || hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2) - && size >= 3 && report->id == 0x03)) - return lenovo_raw_event_TP_X12_tab(hdev, le32_to_cpu(*(__le32 *)data)); + && size >= 4 && report->id == 0x03)) + return lenovo_raw_event_TP_X12_tab(hdev, get_unaligned_le32(data)); return 0; } diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 512049963978..57d8efdd9b89 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -187,6 +187,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_USB_MOUSE), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH), HID_QUIRK_NOGET }, diff --git a/drivers/hid/hid-u2fzero.c b/drivers/hid/hid-u2fzero.c index 744a91e6e78c..82404b6e2d25 100644 --- a/drivers/hid/hid-u2fzero.c +++ b/drivers/hid/hid-u2fzero.c @@ -341,29 +341,33 @@ static int u2fzero_probe(struct hid_device *hdev, if (ret) return ret; - u2fzero_fill_in_urb(dev); + ret = u2fzero_fill_in_urb(dev); + if (ret) + goto err_hid_hw_stop; dev->present = true; minor = ((struct hidraw *) hdev->hidraw)->minor; ret = u2fzero_init_led(dev, minor); - if (ret) { - hid_hw_stop(hdev); - return ret; - } + if (ret) + goto err_free_urb; hid_info(hdev, "%s LED initialised\n", hw_configs[dev->hw_revision].name); ret = u2fzero_init_hwrng(dev, minor); - if (ret) { - hid_hw_stop(hdev); - return ret; - } + if (ret) + goto err_free_urb; hid_info(hdev, "%s RNG initialised\n", hw_configs[dev->hw_revision].name); return 0; + +err_free_urb: + usb_free_urb(dev->urb); +err_hid_hw_stop: + hid_hw_stop(hdev); + return ret; } static void u2fzero_remove(struct hid_device *hdev) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index a32320b351e3..2220168bf116 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -356,6 +356,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, hid_data->inputmode = field->report->id; hid_data->inputmode_index = usage->usage_index; + hid_data->inputmode_field_index = field->index; break; case HID_UP_DIGITIZER: @@ -571,9 +572,14 @@ static int wacom_hid_set_device_mode(struct hid_device *hdev) re = &(hdev->report_enum[HID_FEATURE_REPORT]); r = re->report_id_hash[hid_data->inputmode]; - if (r) { - r->field[0]->value[hid_data->inputmode_index] = 2; - hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + if (r && hid_data->inputmode_field_index >= 0 && + hid_data->inputmode_field_index < r->maxfield) { + struct hid_field *field = r->field[hid_data->inputmode_field_index]; + + if (field && hid_data->inputmode_index < field->report_count) { + field->value[hid_data->inputmode_index] = 2; + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + } } return 0; } @@ -2846,6 +2852,7 @@ static int wacom_probe(struct hid_device *hdev, return -ENODEV; wacom_wac->hid_data.inputmode = -1; + wacom_wac->hid_data.inputmode_field_index = -1; wacom_wac->mode_report = -1; if (hid_is_usb(hdev)) { diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index d4f7d8ca1e7e..126bec6e5c0c 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -295,6 +295,7 @@ struct wacom_shared { struct hid_data { __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ __s16 inputmode_index; /* InputMode HID feature index in the report */ + __s16 inputmode_field_index; /* InputMode HID feature field index in the report */ bool sense_state; bool inrange_state; bool eraser; diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index 2d0b3fcb0ff8..aa11bcefddf2 100644 --- a/drivers/hv/Kconfig +++ b/drivers/hv/Kconfig @@ -74,6 +74,7 @@ config MSHV_ROOT # e.g. When withdrawing memory, the hypervisor gives back 4k pages in # no particular order, making it impossible to reassemble larger pages depends on PAGE_SIZE_4KB + depends on HYPERV_VMBUS if HYPERV_VMBUS select EVENTFD select VIRT_XFER_TO_GUEST_WORK select HMM_MIRROR diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 84eb0a6a0b54..89d214dda360 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -952,6 +952,7 @@ void vmbus_initiate_unload(bool crash) else vmbus_wait_for_unload(); } +EXPORT_SYMBOL_GPL(vmbus_initiate_unload); static void vmbus_setup_channel_state(struct vmbus_channel *channel, struct vmbus_channel_offer_channel *offer) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ae60fd542292..ef4b1b03395d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -272,6 +272,9 @@ void hv_synic_free(void) /* * hv_hyp_synic_enable_regs - Initialize the Synthetic Interrupt Controller * with the hypervisor. + * + * Note: When MSHV is present, mshv_synic_cpu_init() intializes further + * registers later. */ void hv_hyp_synic_enable_regs(unsigned int cpu) { diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0d73daf745a7..336b278b2182 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -27,6 +27,7 @@ #include <linux/connector.h> #include <linux/workqueue.h> #include <linux/hyperv.h> +#include <linux/string.h> #include <hyperv/hvhdk.h> #include "hyperv_vmbus.h" @@ -93,7 +94,7 @@ static void kvp_send_key(struct work_struct *dummy); static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error); static void kvp_timeout_func(struct work_struct *dummy); static void kvp_host_handshake_func(struct work_struct *dummy); -static void kvp_register(int); +static int kvp_register(int); static DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func); static DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func); @@ -127,24 +128,23 @@ static void kvp_register_done(void) hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); } -static void +static int kvp_register(int reg_value) { - struct hv_kvp_msg *kvp_msg; - char *version; + int ret; kvp_msg = kzalloc_obj(*kvp_msg); + if (!kvp_msg) + return -ENOMEM; - if (kvp_msg) { - version = kvp_msg->body.kvp_register.version; - kvp_msg->kvp_hdr.operation = reg_value; - strcpy(version, HV_DRV_VERSION); + kvp_msg->kvp_hdr.operation = reg_value; + strscpy(kvp_msg->body.kvp_register.version, HV_DRV_VERSION); - hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), - kvp_register_done); - kfree(kvp_msg); - } + ret = hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), + kvp_register_done); + kfree(kvp_msg); + return ret; } static void kvp_timeout_func(struct work_struct *dummy) @@ -186,9 +186,8 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg) */ pr_debug("KVP: userspace daemon ver. %d connected\n", msg->kvp_hdr.operation); - kvp_register(dm_reg_value); - return 0; + return kvp_register(dm_reg_value); } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 05a36854389a..eb8bdd8bb1f5 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -441,7 +441,6 @@ void hv_vss_deinit(void); int hv_vss_pre_suspend(void); int hv_vss_pre_resume(void); void hv_vss_onchannelcallback(void *context); -void vmbus_initiate_unload(bool crash); static inline void hv_poll_channel(struct vmbus_channel *channel, void (*cb)(void *)) diff --git a/drivers/hv/mshv_debugfs.c b/drivers/hv/mshv_debugfs.c index 418b6dc8f3c2..3c3e02237ae9 100644 --- a/drivers/hv/mshv_debugfs.c +++ b/drivers/hv/mshv_debugfs.c @@ -674,8 +674,10 @@ int __init mshv_debugfs_init(void) mshv_debugfs = debugfs_create_dir("mshv", NULL); if (IS_ERR(mshv_debugfs)) { + err = PTR_ERR(mshv_debugfs); + mshv_debugfs = NULL; pr_err("%s: failed to create debugfs directory\n", __func__); - return PTR_ERR(mshv_debugfs); + return err; } if (hv_root_partition()) { @@ -710,6 +712,9 @@ remove_mshv_dir: void mshv_debugfs_exit(void) { + if (!mshv_debugfs) + return; + mshv_debugfs_parent_partition_remove(); if (hv_root_partition()) { diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c index fdffd4f002f6..6d65e5b42152 100644 --- a/drivers/hv/mshv_regions.c +++ b/drivers/hv/mshv_regions.c @@ -29,29 +29,27 @@ * Uses huge page stride if the backing page is huge and the guest mapping * is properly aligned; otherwise falls back to single page stride. * - * Return: Stride in pages, or -EINVAL if page order is unsupported. + * Return: Stride in pages. */ -static int mshv_chunk_stride(struct page *page, - u64 gfn, u64 page_count) +static unsigned int mshv_chunk_stride(struct page *page, u64 gfn, + u64 page_count) { - unsigned int page_order; + unsigned int page_order = folio_order(page_folio(page)); /* * Use single page stride by default. For huge page stride, the - * page must be compound and point to the head of the compound - * page, and both gfn and page_count must be huge-page aligned. + * folio order must be at least PMD_ORDER, the page's PFN must be + * 2M-aligned (so that a 2M-aligned tail page of a larger folio is + * acceptable), and both gfn and page_count must be 2M-aligned. */ - if (!PageCompound(page) || !PageHead(page) || + if (page_order < PMD_ORDER || + !IS_ALIGNED(page_to_pfn(page), PTRS_PER_PMD) || !IS_ALIGNED(gfn, PTRS_PER_PMD) || !IS_ALIGNED(page_count, PTRS_PER_PMD)) return 1; - page_order = folio_order(page_folio(page)); - /* The hypervisor only supports 2M huge page */ - if (page_order != PMD_ORDER) - return -EINVAL; - - return 1 << page_order; + /* Use 2M stride always i.e. process 1G folios as 2M chunks */ + return 1 << PMD_ORDER; } /** @@ -86,15 +84,14 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region, u64 gfn = region->start_gfn + page_offset; u64 count; struct page *page; - int stride, ret; + unsigned int stride; + int ret; page = region->mreg_pages[page_offset]; if (!page) return -EINVAL; stride = mshv_chunk_stride(page, gfn, page_count); - if (stride < 0) - return stride; /* Start at stride since the first stride is validated */ for (count = stride; count < page_count; count += stride) { diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index bd1359eb58dd..146726cc4e9b 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -2241,7 +2241,7 @@ static int mshv_root_scheduler_init(unsigned int cpu) outputarg = (void **)this_cpu_ptr(root_scheduler_output); /* Allocate two consecutive pages. One for input, one for output. */ - p = kmalloc(2 * HV_HYP_PAGE_SIZE, GFP_KERNEL); + p = kmalloc_array(2, HV_HYP_PAGE_SIZE, GFP_KERNEL); if (!p) return -ENOMEM; diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index e2288a726fec..88170ce6b83f 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -13,6 +13,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/cpuhotplug.h> +#include <linux/hyperv.h> #include <linux/reboot.h> #include <asm/mshyperv.h> #include <linux/acpi.h> @@ -456,46 +457,75 @@ static int mshv_synic_cpu_init(unsigned int cpu) union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; union hv_synic_sint sint; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_event_ring_page; + /* + * VMBus owns SIMP/SIEFP/SCONTROL when it is active. + * See hv_hyp_synic_enable_regs() for that initialization. + */ + bool vmbus_active = hv_vmbus_exists(); - /* Setup the Synic's message page */ + /* + * Map the SYNIC message page. When VMBus is not active the + * hypervisor pre-provisions the SIMP GPA but may not set + * simp_enabled — enable it here. + */ simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = true; + if (!vmbus_active) { + simp.simp_enabled = true; + hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + } *msg_page = memremap(simp.base_simp_gpa << HV_HYP_PAGE_SHIFT, HV_HYP_PAGE_SIZE, MEMREMAP_WB); if (!(*msg_page)) - return -EFAULT; - - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + goto cleanup_simp; - /* Setup the Synic's event flags page */ + /* + * Map the event flags page. Same as SIMP: enable when + * VMBus is not active, already enabled by VMBus otherwise. + */ siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = true; - *event_flags_page = memremap(siefp.base_siefp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); + if (!vmbus_active) { + siefp.siefp_enabled = true; + hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + } + *event_flags_page = memremap(siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT, + HV_HYP_PAGE_SIZE, MEMREMAP_WB); if (!(*event_flags_page)) - goto cleanup; - - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + goto cleanup_siefp; /* Setup the Synic's event ring page */ sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP); - sirbp.sirbp_enabled = true; - *event_ring_page = memremap(sirbp.base_sirbp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); - if (!(*event_ring_page)) - goto cleanup; + if (hv_root_partition()) { + *event_ring_page = memremap(sirbp.base_sirbp_gpa << HV_HYP_PAGE_SHIFT, + HV_HYP_PAGE_SIZE, MEMREMAP_WB); + if (!(*event_ring_page)) + goto cleanup_siefp; + } else { + /* + * On L1VH the hypervisor does not provide a SIRBP page. + * Allocate one and program its GPA into the MSR. + */ + *event_ring_page = (struct hv_synic_event_ring_page *) + get_zeroed_page(GFP_KERNEL); + + if (!(*event_ring_page)) + goto cleanup_siefp; + + sirbp.base_sirbp_gpa = virt_to_phys(*event_ring_page) + >> HV_HYP_PAGE_SHIFT; + } + + sirbp.sirbp_enabled = true; hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); if (mshv_sint_irq != -1) @@ -518,28 +548,30 @@ static int mshv_synic_cpu_init(unsigned int cpu) hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX, sint.as_uint64); - /* Enable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 1; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it already enabled SCONTROL. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 1; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; -cleanup: - if (*event_ring_page) { - sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - } - if (*event_flags_page) { +cleanup_siefp: + if (*event_flags_page) + memunmap(*event_flags_page); + if (!vmbus_active) { siefp.siefp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); - memunmap(*event_flags_page); } - if (*msg_page) { +cleanup_simp: + if (*msg_page) + memunmap(*msg_page); + if (!vmbus_active) { simp.simp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); - memunmap(*msg_page); } return -EFAULT; @@ -548,16 +580,15 @@ cleanup: static int mshv_synic_cpu_exit(unsigned int cpu) { union hv_synic_sint sint; - union hv_synic_simp simp; - union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_event_ring_page; + /* VMBus owns SIMP/SIEFP/SCONTROL when it is active */ + bool vmbus_active = hv_vmbus_exists(); /* Disable the interrupt */ sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX); @@ -574,28 +605,47 @@ static int mshv_synic_cpu_exit(unsigned int cpu) if (mshv_sint_irq != -1) disable_percpu_irq(mshv_sint_irq); - /* Disable Synic's event ring page */ + /* Disable SYNIC event ring page owned by MSHV */ sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP); sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - /* Disable Synic's event flags page */ - siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + if (hv_root_partition()) { + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + memunmap(*event_ring_page); + } else { + sirbp.base_sirbp_gpa = 0; + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + free_page((unsigned long)*event_ring_page); + } + + /* + * Release our mappings of the message and event flags pages. + * When VMBus is not active, we enabled SIMP/SIEFP — disable + * them. Otherwise VMBus owns the MSRs — leave them. + */ memunmap(*event_flags_page); + if (!vmbus_active) { + union hv_synic_simp simp; + union hv_synic_siefp siefp; - /* Disable Synic's message page */ - simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); + siefp.siefp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + + simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); + simp.simp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + } memunmap(*msg_page); - /* Disable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 0; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it owns SCONTROL — leave it. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 0; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; } @@ -673,9 +723,7 @@ mshv_unregister_doorbell(u64 partition_id, int doorbell_portid) static int mshv_synic_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { - if (!hv_root_partition()) - return 0; - + mshv_debugfs_exit(); cpuhp_remove_state(synic_cpuhp_online); return 0; } diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index c19400701467..0d3d4161974f 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -596,9 +596,9 @@ static int mshv_vtl_get_set_reg(struct hv_register_assoc *regs, bool set) } else { /* Handle MSRs */ if (set) - wrmsrl(reg_table[i].msr_addr, *reg64); + wrmsrq(reg_table[i].msr_addr, *reg64); else - rdmsrl(reg_table[i].msr_addr, *reg64); + rdmsrq(reg_table[i].msr_addr, *reg64); } return 0; } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index d28ff45d4cfd..23206640c613 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -69,19 +69,29 @@ bool vmbus_is_confidential(void) } EXPORT_SYMBOL_GPL(vmbus_is_confidential); +static bool skip_vmbus_unload; + +/* + * Allow a VMBus framebuffer driver to specify that in the case of a panic, + * it will do the VMbus unload operation once it has flushed any dirty + * portions of the framebuffer to the Hyper-V host. + */ +void vmbus_set_skip_unload(bool skip) +{ + skip_vmbus_unload = skip; +} +EXPORT_SYMBOL_GPL(vmbus_set_skip_unload); + /* * The panic notifier below is responsible solely for unloading the * vmbus connection, which is necessary in a panic event. - * - * Notice an intrincate relation of this notifier with Hyper-V - * framebuffer panic notifier exists - we need vmbus connection alive - * there in order to succeed, so we need to order both with each other - * [see hvfb_on_panic()] - this is done using notifiers' priorities. */ static int hv_panic_vmbus_unload(struct notifier_block *nb, unsigned long val, void *args) { - vmbus_initiate_unload(true); + if (!skip_vmbus_unload) + vmbus_initiate_unload(true); + return NOTIFY_DONE; } static struct notifier_block hyperv_panic_vmbus_unload_block = { @@ -538,34 +548,6 @@ static ssize_t device_show(struct device *dev, } static DEVICE_ATTR_RO(device); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - int ret; - - ret = driver_set_override(dev, &hv_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", hv_dev->driver_override); - device_unlock(dev); - - return len; -} -static DEVICE_ATTR_RW(driver_override); - /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_id.attr, @@ -596,7 +578,6 @@ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_channel_vp_mapping.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -708,9 +689,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * { const guid_t *guid = &dev->dev_type; const struct hv_vmbus_device_id *id; + int ret; - /* When driver_override is set, only bind to the matching driver */ - if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + /* If a driver override is set, only bind to the matching driver */ + ret = device_match_driver_override(&dev->device, &drv->driver); + if (ret == 0) return NULL; /* Look at the dynamic ids first, before the static ones */ @@ -718,8 +701,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * if (!id) id = hv_vmbus_dev_match(drv->id_table, guid); - /* driver_override will always match, send a dummy id */ - if (!id && dev->driver_override) + /* + * If there's a matching driver override, this function should succeed, + * thus return a dummy device ID if no matching ID is found. + */ + if (!id && ret > 0) id = &vmbus_device_null; return id; @@ -1021,6 +1007,7 @@ static const struct dev_pm_ops vmbus_pm = { /* The one and only one */ static const struct bus_type hv_bus = { .name = "vmbus", + .driver_override = true, .match = vmbus_match, .shutdown = vmbus_shutdown, .remove = vmbus_remove, @@ -2317,8 +2304,8 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) return AE_NO_MEMORY; /* If this range overlaps the virtual TPM, truncate it. */ - if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) - end = VTPM_BASE_ADDRESS; + if (end >= VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) + end = VTPM_BASE_ADDRESS - 1; new_res->name = "hyperv mmio"; new_res->flags = IORESOURCE_MEM; @@ -2385,6 +2372,7 @@ static void vmbus_mmio_remove(void) static void __maybe_unused vmbus_reserve_fb(void) { resource_size_t start = 0, size; + resource_size_t low_mmio_base; struct pci_dev *pdev; if (efi_enabled(EFI_BOOT)) { @@ -2392,6 +2380,24 @@ static void __maybe_unused vmbus_reserve_fb(void) if (IS_ENABLED(CONFIG_SYSFB)) { start = sysfb_primary_display.screen.lfb_base; size = max_t(__u32, sysfb_primary_display.screen.lfb_size, 0x800000); + + low_mmio_base = hyperv_mmio->start; + if (!low_mmio_base || upper_32_bits(low_mmio_base) || + (start && start < low_mmio_base)) { + pr_warn("Unexpected low mmio base %pa\n", &low_mmio_base); + } else { + /* + * If the kdump/kexec or CVM kernel's lfb_base + * is 0, fall back to the low mmio base. + */ + if (!start) + start = low_mmio_base; + /* + * Reserve half of the space below 4GB for high + * resolutions, but cap the reservation to 128MB. + */ + size = min((SZ_4G - start) / 2, SZ_128M); + } } } else { /* Gen1 VM: get FB base from PCI */ @@ -2412,8 +2418,10 @@ static void __maybe_unused vmbus_reserve_fb(void) pci_dev_put(pdev); } - if (!start) + if (!start) { + pr_warn("Unexpected framebuffer mmio base of zero\n"); return; + } /* * Make a claim for the frame buffer in the resource tree under the @@ -2423,6 +2431,8 @@ static void __maybe_unused vmbus_reserve_fb(void) */ for (; !fb_mmio && (size >= 0x100000); size >>= 1) fb_mmio = __request_region(hyperv_mmio, start, size, fb_mmio_name, 0); + + pr_info("hv_mmio=%pR,%pR fb=%pR\n", hyperv_mmio, hyperv_mmio->sibling, fb_mmio); } /** @@ -2897,7 +2907,8 @@ static void hv_crash_handler(struct pt_regs *regs) { int cpu; - vmbus_initiate_unload(true); + if (!skip_vmbus_unload) + vmbus_initiate_unload(true); /* * In crash handler we can't schedule synic cleanup for all CPUs, * doing the cleanup for current CPU only. This should be sufficient diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 14e4cea48acc..e4c4f2b09732 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -388,6 +388,18 @@ config SENSORS_APPLESMC Say Y here if you have an applicable laptop and want to experience the awesome power of applesmc. +config SENSORS_ARCTIC_FAN_CONTROLLER + tristate "ARCTIC Fan Controller" + depends on USB_HID + help + If you say yes here you get support for the ARCTIC Fan Controller, + a USB HID device (VID 0x3904, PID 0xF001) with 10 fan channels. + The driver exposes fan speed (RPM) and PWM control via the hwmon + sysfs interface. + + This driver can also be built as a module. If so, the module + will be called arctic_fan_controller. + config SENSORS_ARM_SCMI tristate "ARM SCMI Sensors" depends on ARM_SCMI_PROTOCOL @@ -1157,6 +1169,18 @@ config SENSORS_LTC4282 This driver can also be built as a module. If so, the module will be called ltc4282. +config SENSORS_LTC4283 + tristate "Analog Devices LTC4283" + depends on I2C + select REGMAP_I2C + select AUXILIARY_BUS + help + If you say yes here you get support for Analog Devices LTC4283 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4283. + config SENSORS_LTQ_CPUTEMP bool "Lantiq cpu temperature sensor driver" depends on SOC_XWAY @@ -2044,6 +2068,17 @@ config SENSORS_EMC1403 Threshold values can be configured using sysfs. Data from the different diodes are accessible via sysfs. +config SENSORS_EMC1812 + tristate "Microchip Technology EMC1812 driver" + depends on I2C + select REGMAP_I2C + help + If you say yes here to build support for Microchip Technology's + EMC181X/33 Multichannel Low-Voltage Remote Diode Sensor Family. + + This driver can also be built as a module. If so, the module + will be called emc1812. + config SENSORS_EMC2103 tristate "SMSC EMC2103" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 982ee2c6f9de..d5c8bc05123a 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_AHT10) += aht10.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o +obj-$(CONFIG_SENSORS_ARCTIC_FAN_CONTROLLER) += arctic_fan_controller.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o @@ -72,6 +73,7 @@ obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o +obj-$(CONFIG_SENSORS_EMC1812) += emc1812.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o obj-$(CONFIG_SENSORS_EMC2305) += emc2305.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o @@ -147,6 +149,7 @@ obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_LTC4282) += ltc4282.o +obj-$(CONFIG_SENSORS_LTC4283) += ltc4283.o obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o obj-$(CONFIG_SENSORS_MACSMC_HWMON) += macsmc-hwmon.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index f0b17e59827f..c2df631ae93a 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -205,8 +205,8 @@ static int ad7414_probe(struct i2c_client *client) } static const struct i2c_device_id ad7414_id[] = { - { "ad7414" }, - {} + { .name = "ad7414" }, + { } }; MODULE_DEVICE_TABLE(i2c, ad7414_id); diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index 7a132accdf8a..a0c9cf5e12cc 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -281,9 +281,9 @@ static int ad7418_probe(struct i2c_client *client) } static const struct i2c_device_id ad7418_id[] = { - { "ad7416", ad7416 }, - { "ad7417", ad7417 }, - { "ad7418", ad7418 }, + { .name = "ad7416", .driver_data = ad7416 }, + { .name = "ad7417", .driver_data = ad7417 }, + { .name = "ad7418", .driver_data = ad7418 }, { } }; MODULE_DEVICE_TABLE(i2c, ad7418_id); diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index 5e805d4ee76a..e45adc0b84d1 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -480,7 +480,7 @@ static int adc128_probe(struct i2c_client *client) } static const struct i2c_device_id adc128_id[] = { - { "adc128d818" }, + { .name = "adc128d818" }, { } }; MODULE_DEVICE_TABLE(i2c, adc128_id); diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 389382d54752..ccac7ba601e9 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -548,8 +548,8 @@ static int adm1025_probe(struct i2c_client *client) } static const struct i2c_device_id adm1025_id[] = { - { "adm1025", adm1025 }, - { "ne1619", ne1619 }, + { .name = "adm1025", .driver_data = adm1025 }, + { .name = "ne1619", .driver_data = ne1619 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1025_id); diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index c38c932e5d2a..3ad82c01f81d 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -1857,7 +1857,7 @@ static int adm1026_probe(struct i2c_client *client) } static const struct i2c_device_id adm1026_id[] = { - { "adm1026" }, + { .name = "adm1026" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1026_id); diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 71eea8ae51b9..6cb0a238e059 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -382,7 +382,7 @@ static int adm1029_probe(struct i2c_client *client) } static const struct i2c_device_id adm1029_id[] = { - { "adm1029" }, + { .name = "adm1029" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1029_id); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 343118532cdb..24d0d4146c87 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -1055,8 +1055,8 @@ static int adm1031_probe(struct i2c_client *client) } static const struct i2c_device_id adm1031_id[] = { - { "adm1030", adm1030 }, - { "adm1031", adm1031 }, + { .name = "adm1030", .driver_data = adm1030 }, + { .name = "adm1031", .driver_data = adm1031 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1031_id); diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c index 7888afe8dafd..dc4d8a214d1b 100644 --- a/drivers/hwmon/adm1177.c +++ b/drivers/hwmon/adm1177.c @@ -246,8 +246,8 @@ static int adm1177_probe(struct i2c_client *client) } static const struct i2c_device_id adm1177_id[] = { - {"adm1177"}, - {} + { .name = "adm1177" }, + { } }; MODULE_DEVICE_TABLE(i2c, adm1177_id); diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 86f6044b5bd0..586650e8d043 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -794,9 +794,9 @@ static int adm9240_probe(struct i2c_client *client) } static const struct i2c_device_id adm9240_id[] = { - { "adm9240", adm9240 }, - { "ds1780", ds1780 }, - { "lm81", lm81 }, + { .name = "adm9240", .driver_data = adm9240 }, + { .name = "ds1780", .driver_data = ds1780 }, + { .name = "lm81", .driver_data = lm81 }, { } }; MODULE_DEVICE_TABLE(i2c, adm9240_id); diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 7f43565ca284..149cfcec78dc 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -176,8 +176,8 @@ static int ads7828_probe(struct i2c_client *client) } static const struct i2c_device_id ads7828_device_ids[] = { - { "ads7828", ads7828 }, - { "ads7830", ads7830 }, + { .name = "ads7828", .driver_data = ads7828 }, + { .name = "ads7830", .driver_data = ads7830 }, { } }; MODULE_DEVICE_TABLE(i2c, ads7828_device_ids); diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c index 9ee3ce01f130..d1cbff0e8572 100644 --- a/drivers/hwmon/ads7871.c +++ b/drivers/hwmon/ads7871.c @@ -56,7 +56,6 @@ #include <linux/init.h> #include <linux/spi/spi.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/delay.h> @@ -64,8 +63,19 @@ struct ads7871_data { struct spi_device *spi; + u8 tx_buf[2] ____cacheline_aligned; }; +static umode_t ads7871_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_in && attr == hwmon_in_input) + return 0444; + + return 0; +} + static int ads7871_read_reg8(struct spi_device *spi, int reg) { int ret; @@ -86,29 +96,31 @@ static int ads7871_read_reg16(struct spi_device *spi, int reg) return le16_to_cpu((__force __le16)ret); } -static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val) +static int ads7871_write_reg8(struct ads7871_data *pdata, int reg, u8 val) { - u8 tmp[2] = {reg, val}; - return spi_write(spi, tmp, sizeof(tmp)); + pdata->tx_buf[0] = reg; + pdata->tx_buf[1] = val; + + return spi_write(pdata->spi, pdata->tx_buf, 2); } -static ssize_t voltage_show(struct device *dev, struct device_attribute *da, - char *buf) +static int ads7871_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { struct ads7871_data *pdata = dev_get_drvdata(dev); struct spi_device *spi = pdata->spi; - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - int ret, val, i = 0; - uint8_t channel, mux_cnv; + int ret, raw_val, i = 0; + u8 mux_cnv; - channel = attr->index; + if (type != hwmon_in || attr != hwmon_in_input) + return -EOPNOTSUPP; /* * TODO: add support for conversions * other than single ended with a gain of 1 */ /*MUX_M3_BM forces single ended*/ /*This is also where the gain of the PGA would be set*/ - ret = ads7871_write_reg8(spi, REG_GAIN_MUX, + ret = ads7871_write_reg8(pdata, REG_GAIN_MUX, (MUX_CNV_BM | MUX_M3_BM | channel)); if (ret < 0) return ret; @@ -116,6 +128,7 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da, ret = ads7871_read_reg8(spi, REG_GAIN_MUX); if (ret < 0) return ret; + mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV); /* * on 400MHz arm9 platform the conversion @@ -131,39 +144,37 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da, } if (mux_cnv == 0) { - val = ads7871_read_reg16(spi, REG_LS_BYTE); - if (val < 0) - return val; - /*result in volts*10000 = (val/8192)*2.5*10000*/ - val = ((val >> 2) * 25000) / 8192; - return sysfs_emit(buf, "%d\n", val); + raw_val = ads7871_read_reg16(spi, REG_LS_BYTE); + if (raw_val < 0) + return raw_val; + + /* + * Use (s16) to ensure the sign bit is preserved during the shift. + * Report millivolts (2.5V = 2500mV). + */ + *val = ((s16)raw_val >> 2) * 2500 / 8192; + return 0; } return -ETIMEDOUT; } -static SENSOR_DEVICE_ATTR_RO(in0_input, voltage, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, voltage, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, voltage, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, voltage, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, voltage, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, voltage, 5); -static SENSOR_DEVICE_ATTR_RO(in6_input, voltage, 6); -static SENSOR_DEVICE_ATTR_RO(in7_input, voltage, 7); - -static struct attribute *ads7871_attrs[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, +static const struct hwmon_channel_info * const ads7871_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, + HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT), NULL }; -ATTRIBUTE_GROUPS(ads7871); +static const struct hwmon_ops ads7871_hwmon_ops = { + .is_visible = ads7871_is_visible, + .read = ads7871_read, +}; + +static const struct hwmon_chip_info ads7871_chip_info = { + .ops = &ads7871_hwmon_ops, + .info = ads7871_info, +}; static int ads7871_probe(struct spi_device *spi) { @@ -178,11 +189,17 @@ static int ads7871_probe(struct spi_device *spi) spi->bits_per_word = 8; spi_setup(spi); - ads7871_write_reg8(spi, REG_SER_CONTROL, 0); - ads7871_write_reg8(spi, REG_AD_CONTROL, 0); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->spi = spi; + + ads7871_write_reg8(pdata, REG_SER_CONTROL, 0); + ads7871_write_reg8(pdata, REG_AD_CONTROL, 0); val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM); - ads7871_write_reg8(spi, REG_OSC_CONTROL, val); + ads7871_write_reg8(pdata, REG_OSC_CONTROL, val); ret = ads7871_read_reg8(spi, REG_OSC_CONTROL); dev_dbg(dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret); @@ -193,15 +210,10 @@ static int ads7871_probe(struct spi_device *spi) if (val != ret) return -ENODEV; - pdata = devm_kzalloc(dev, sizeof(struct ads7871_data), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdata->spi = spi; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, spi->modalias, - pdata, - ads7871_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, spi->modalias, + pdata, + &ads7871_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index 73b196a78f3a..0aa7ce0a04be 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -89,10 +89,10 @@ static int adt7410_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adt7410_ids[] = { - { "adt7410" }, - { "adt7420" }, - { "adt7422" }, - {} + { .name = "adt7410" }, + { .name = "adt7420" }, + { .name = "adt7422" }, + { } }; MODULE_DEVICE_TABLE(i2c, adt7410_ids); diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index b9991a69e6c6..598f31647544 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -169,11 +169,11 @@ static ssize_t adt7411_set_bit(struct device *dev, if (ret || flag > 1) return -EINVAL; - hwmon_lock(dev); - ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); - /* force update */ - data->next_update = jiffies; - hwmon_unlock(dev); + scoped_guard(hwmon_lock, dev) { + ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); + /* force update */ + data->next_update = jiffies; + } return ret < 0 ? ret : count; } @@ -670,7 +670,7 @@ static int adt7411_probe(struct i2c_client *client) } static const struct i2c_device_id adt7411_id[] = { - { "adt7411" }, + { .name = "adt7411" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7411_id); diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 174dfee47f7a..31cf9e3bb04f 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -12,6 +12,7 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> +#include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/log2.h> #include <linux/slab.h> @@ -1809,15 +1810,22 @@ static int adt7462_probe(struct i2c_client *client) } static const struct i2c_device_id adt7462_id[] = { - { "adt7462" }, + { .name = "adt7462" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7462_id); +static const struct of_device_id adt7462_of_match[] = { + { .compatible = "onnn,adt7462" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adt7462_of_match); + static struct i2c_driver adt7462_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7462", + .of_match_table = adt7462_of_match, }, .probe = adt7462_probe, .id_table = adt7462_id, diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index dbee6926fa05..664349756dc2 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1296,7 +1296,7 @@ static void adt7470_remove(struct i2c_client *client) } static const struct i2c_device_id adt7470_id[] = { - { "adt7470" }, + { .name = "adt7470" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7470_id); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 8cefa14e1633..7241fc73d21a 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -19,6 +19,8 @@ #include <linux/err.h> #include <linux/jiffies.h> #include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/util_macros.h> #include <dt-bindings/pwm/pwm.h> @@ -165,15 +167,15 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { adt7473, adt7475, adt7476, adt7490 }; static const struct i2c_device_id adt7475_id[] = { - { "adt7473", adt7473 }, - { "adt7475", adt7475 }, - { "adt7476", adt7476 }, - { "adt7490", adt7490 }, + { .name = "adt7473", .driver_data = adt7473 }, + { .name = "adt7475", .driver_data = adt7475 }, + { .name = "adt7476", .driver_data = adt7476 }, + { .name = "adt7490", .driver_data = adt7490 }, { } }; MODULE_DEVICE_TABLE(i2c, adt7475_id); -static const struct of_device_id __maybe_unused adt7475_of_match[] = { +static const struct of_device_id adt7475_of_match[] = { { .compatible = "adi,adt7473", .data = (void *)adt7473 @@ -1995,7 +1997,7 @@ static struct i2c_driver adt7475_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7475", - .of_match_table = of_match_ptr(adt7475_of_match), + .of_match_table = adt7475_of_match, }, .probe = adt7475_probe, .id_table = adt7475_id, diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index 66955395d058..6c263cb57766 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -55,10 +55,10 @@ enum aht10_variant { aht10, aht20, dht20}; static const struct i2c_device_id aht10_id[] = { - { "aht10", aht10 }, - { "aht20", aht20 }, - { "dht20", dht20 }, - { }, + { .name = "aht10", .driver_data = aht10 }, + { .name = "aht20", .driver_data = aht20 }, + { .name = "dht20", .driver_data = dht20 }, + { } }; MODULE_DEVICE_TABLE(i2c, aht10_id); diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index d5f864b360b0..5e6611d6e1bd 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -1076,14 +1076,14 @@ static int amc6821_probe(struct i2c_client *client) "Failed to initialize hwmon\n"); if (IS_ENABLED(CONFIG_THERMAL) && fan_np && data->fan_cooling_levels) - return PTR_ERR_OR_ZERO(devm_thermal_of_cooling_device_register(dev, + return PTR_ERR_OR_ZERO(devm_thermal_of_child_cooling_device_register(dev, fan_np, client->name, data, &amc6821_cooling_ops)); return 0; } static const struct i2c_device_id amc6821_id[] = { - { "amc6821" }, + { .name = "amc6821" }, { } }; diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 24f3e86d0ebf..90a14a7f2c4c 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1305,6 +1305,7 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = { }, { .ident = NULL } }; +MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); static int __init applesmc_init(void) { @@ -1416,4 +1417,3 @@ module_exit(applesmc_exit); MODULE_AUTHOR("Nicolas Boichat"); MODULE_DESCRIPTION("Apple SMC"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); diff --git a/drivers/hwmon/arctic_fan_controller.c b/drivers/hwmon/arctic_fan_controller.c new file mode 100644 index 000000000000..dbe84cd93c08 --- /dev/null +++ b/drivers/hwmon/arctic_fan_controller.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux hwmon driver for ARCTIC Fan Controller + * + * USB Custom HID device with 10 fan channels. + * Exposes fan RPM (input) and PWM (0-255) via hwmon. Device pushes IN reports + * at ~1 Hz; no GET_REPORT. OUT reports set PWM duty (bytes 1-10, 0-100%). + * PWM is manual-only: the device does not change duty autonomously, only + * when it receives an OUT report from the host. + */ + +#include <linux/completion.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/unaligned.h> + +#define ARCTIC_VID 0x3904 +#define ARCTIC_PID 0xF001 +#define ARCTIC_NUM_FANS 10 +#define ARCTIC_OUTPUT_REPORT_ID 0x01 +#define ARCTIC_REPORT_LEN 32 +#define ARCTIC_RPM_OFFSET 11 /* bytes 11-30: 10 x uint16 LE */ +/* ACK report: device sends Report ID 0x02, 2 bytes (ID + status) after applying OUT report */ +#define ARCTIC_ACK_REPORT_ID 0x02 +#define ARCTIC_ACK_REPORT_LEN 2 +/* + * Time to wait for ACK report after send. + * Measured over 500 iterations: max ~563 ms. Keep 1 s as margin. + */ +#define ARCTIC_ACK_TIMEOUT_MS 1000 + +struct arctic_fan_data { + struct hid_device *hdev; + struct device *hwmon_dev; /* stored for explicit unregister in remove() */ + spinlock_t in_report_lock; /* protects fan_rpm, ack_status, write_pending, pwm_duty */ + struct completion in_report_received; /* ACK (ID 0x02) received in raw_event */ + int ack_status; /* 0 = OK, negative errno on device error */ + bool write_pending; /* true while an OUT report ACK is in flight */ + u32 fan_rpm[ARCTIC_NUM_FANS]; + u8 pwm_duty[ARCTIC_NUM_FANS]; /* 0-255 matching sysfs range; converted to 0-100 on send */ + /* + * OUT report buffer passed to hid_hw_output_report(). Embedded in the + * devm_kzalloc'd struct so it is heap-allocated and passes + * usb_hcd_map_urb_for_dma(). Exclusively accessed by write(), which + * the hwmon core serializes. + */ + __dma_from_device_group_begin(); + u8 buf[ARCTIC_REPORT_LEN]; + __dma_from_device_group_end(); +}; + +/* + * Parse RPM values from the periodic status report (10 x uint16 LE at rpm_off). + * pwm_duty is not updated from the report: the device is manual-only, so the + * host cache is the authoritative source for PWM. + * Called from raw_event which may run in IRQ context; must not sleep. + */ +static void arctic_fan_parse_report(struct arctic_fan_data *priv, u8 *buf, + int len, int rpm_off) +{ + unsigned long flags; + int i; + + if (len < rpm_off + 20) + return; + + spin_lock_irqsave(&priv->in_report_lock, flags); + for (i = 0; i < ARCTIC_NUM_FANS; i++) + priv->fan_rpm[i] = get_unaligned_le16(&buf[rpm_off + i * 2]); + spin_unlock_irqrestore(&priv->in_report_lock, flags); +} + +/* + * raw_event: IN reports. + * + * Status report: Report ID 0x01, 32 bytes: + * byte 0 = report ID, bytes 1-10 = PWM 0-100%, bytes 11-30 = 10 x RPM uint16 LE. + * Device pushes these at ~1 Hz; no GET_REPORT. + * + * ACK report: Report ID 0x02, 2 bytes: + * byte 0 = 0x02, byte 1 = status (0x00 = OK, 0x01 = ERROR). + * Sent once after accepting and applying an OUT report (ID 0x01). + */ +static int arctic_fan_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + unsigned long flags; + + hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d\n", report->id, size); + + if (report->id == ARCTIC_ACK_REPORT_ID && size == ARCTIC_ACK_REPORT_LEN) { + spin_lock_irqsave(&priv->in_report_lock, flags); + /* + * Only deliver if a write is in flight. This prevents a + * late-arriving ACK from a timed-out write from erroneously + * satisfying a subsequent write's completion wait. + */ + if (priv->write_pending) { + priv->ack_status = data[1] == 0x00 ? 0 : -EIO; + complete(&priv->in_report_received); + } + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + + if (report->id != ARCTIC_OUTPUT_REPORT_ID || size != ARCTIC_REPORT_LEN) { + hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d ignored\n", + report->id, size); + return 0; + } + + arctic_fan_parse_report(priv, data, size, ARCTIC_RPM_OFFSET); + return 0; +} + +static umode_t arctic_fan_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_fan && attr == hwmon_fan_input) + return 0444; + if (type == hwmon_pwm && attr == hwmon_pwm_input) + return 0644; + return 0; +} + +static int arctic_fan_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct arctic_fan_data *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (type == hwmon_fan && attr == hwmon_fan_input) { + spin_lock_irqsave(&priv->in_report_lock, flags); + *val = priv->fan_rpm[channel]; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + if (type == hwmon_pwm && attr == hwmon_pwm_input) { + spin_lock_irqsave(&priv->in_report_lock, flags); + *val = priv->pwm_duty[channel]; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + return -EINVAL; +} + +static int arctic_fan_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct arctic_fan_data *priv = dev_get_drvdata(dev); + u8 new_duty = (u8)clamp_val(val, 0, 255); + unsigned long flags; + unsigned long t; + int i, ret; + + /* + * Build the buffer and arm write_pending under in_report_lock so that + * reset_resume() cannot clear pwm_duty[] between the pwm_duty[] read + * and the buffer write, and raw_event() cannot deliver a stale ACK + * from a previous write into this write's completion. + * + * priv->buf is heap-allocated (embedded in the devm_kzalloc'd struct), + * satisfying usb_hcd_map_urb_for_dma(). Exclusively accessed by + * write() which the hwmon core serializes. + * + * pwm_duty[channel] is committed only after a positive device ACK so a + * failed or timed-out write does not corrupt the cached state. + * + * Residual theoretical race: if write A times out (write_pending + * cleared), write B sets write_pending = true, and a late ACK from + * write A—delayed beyond ARCTIC_ACK_TIMEOUT_MS—arrives during write + * B's pending window, it would falsely satisfy write B's completion. + * This cannot be prevented in driver code without protocol support + * (for example, a correlation ID echoed in the device ACK report). + * In testing, observed ACK latency stayed below the 1 s timeout + * (maximum ~563 ms over 500 iterations). + * + * The wait is non-interruptible so that a signal cannot cause write() + * to return early while the OUT report is already in flight; an + * interruptible early return would create the same late-ACK window + * without even the timeout guard. + * Serialized by the hwmon core: only one arctic_fan_write() at a time. + * Use irqsave to match the IRQ context in which raw_event may run. + */ + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->buf[0] = ARCTIC_OUTPUT_REPORT_ID; + for (i = 0; i < ARCTIC_NUM_FANS; i++) { + u8 d = i == channel ? new_duty : priv->pwm_duty[i]; + + priv->buf[1 + i] = DIV_ROUND_CLOSEST((unsigned int)d * 100, 255); + } + priv->ack_status = -ETIMEDOUT; + priv->write_pending = true; + reinit_completion(&priv->in_report_received); + spin_unlock_irqrestore(&priv->in_report_lock, flags); + + ret = hid_hw_output_report(priv->hdev, priv->buf, ARCTIC_REPORT_LEN); + if (ret < 0) { + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->write_pending = false; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return ret; + } + + t = wait_for_completion_timeout(&priv->in_report_received, + msecs_to_jiffies(ARCTIC_ACK_TIMEOUT_MS)); + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->write_pending = false; + /* Commit inside the lock so reset_resume() cannot race with this write */ + if (t && priv->ack_status == 0) + priv->pwm_duty[channel] = new_duty; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + + if (!t) + return -ETIMEDOUT; + return priv->ack_status; /* 0=OK, -EIO=device error */ +} + +static const struct hwmon_ops arctic_fan_ops = { + .is_visible = arctic_fan_is_visible, + .read = arctic_fan_read, + .write = arctic_fan_write, +}; + +static const struct hwmon_channel_info *arctic_fan_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_chip_info arctic_fan_chip_info = { + .ops = &arctic_fan_ops, + .info = arctic_fan_info, +}; + +static int arctic_fan_reset_resume(struct hid_device *hdev) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + unsigned long flags; + + /* + * The device resets its PWM channels to hardware defaults on power + * loss during suspend. Clear the cached duty values so they reflect + * the unknown hardware state, consistent with probe-time behaviour + * (the device has no GET_REPORT support). Hold in_report_lock so + * this does not race with a concurrent pwm read or write callback. + */ + spin_lock_irqsave(&priv->in_report_lock, flags); + memset(priv->pwm_duty, 0, sizeof(priv->pwm_duty)); + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; +} + +static int arctic_fan_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct arctic_fan_data *priv; + int ret; + + if (!hid_is_usb(hdev)) + return -ENODEV; + + ret = hid_parse(hdev); + if (ret) + return ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hdev = hdev; + spin_lock_init(&priv->in_report_lock); + init_completion(&priv->in_report_received); + hid_set_drvdata(hdev, priv); + + ret = hid_hw_start(hdev, HID_CONNECT_DRIVER); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto out_stop; + + /* + * Start IO before registering with hwmon. If IO were started after + * hwmon registration, a sysfs write arriving in that narrow window + * would send an OUT report but the ACK could not be delivered (the HID + * core discards events until io_started), causing a spurious timeout. + */ + hid_device_io_start(hdev); + + /* + * Use the non-devm variant and store the pointer so remove() can + * call hwmon_device_unregister() before tearing down the HID + * transport. devm_hwmon_device_register_with_info() would defer + * unregistration until after remove() returns, leaving a window + * where a concurrent sysfs write could call hid_hw_output_report() + * on an already-stopped device (use-after-free). + */ + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "arctic_fan", + priv, &arctic_fan_chip_info, + NULL); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + goto out_close; + } + + return 0; + +out_close: + hid_device_io_stop(hdev); + hid_hw_close(hdev); +out_stop: + hid_hw_stop(hdev); + return ret; +} + +static void arctic_fan_remove(struct hid_device *hdev) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + + /* + * Unregister hwmon before stopping the HID transport. This removes + * the sysfs files and waits for any in-progress write() callback to + * return, so no hwmon op can call hid_hw_output_report() after + * hid_hw_stop() frees the underlying USB resources. + * Matches the pattern used by nzxt-smart2 and aquacomputer_d5next. + * + * The HID core clears hdev->io_started before invoking ->remove(), + * so hid_device_io_stop() is not called here; doing so would emit + * a spurious "io already stopped" warning. + */ + hwmon_device_unregister(priv->hwmon_dev); + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id arctic_fan_id_table[] = { + { HID_USB_DEVICE(ARCTIC_VID, ARCTIC_PID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, arctic_fan_id_table); + +static struct hid_driver arctic_fan_driver = { + .name = "arctic_fan", + .id_table = arctic_fan_id_table, + .probe = arctic_fan_probe, + .remove = arctic_fan_remove, + .raw_event = arctic_fan_raw_event, + .reset_resume = arctic_fan_reset_resume, +}; + +module_hid_driver(arctic_fan_driver); + +MODULE_AUTHOR("Aureo Serrano de Souza <aureo.serrano@arctic.de>"); +MODULE_DESCRIPTION("HID hwmon driver for ARCTIC Fan Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 14e7737866c2..5f5c94505166 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -213,7 +213,7 @@ static struct asb100_data *asb100_update_device(struct device *dev); static void asb100_init_client(struct i2c_client *client); static const struct i2c_device_id asb100_id[] = { - { "asb100" }, + { .name = "asb100" }, { } }; MODULE_DEVICE_TABLE(i2c, asb100_id); diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 87e186700849..e8145bb13ab3 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -1179,9 +1179,9 @@ static void asc7621_remove(struct i2c_client *client) } static const struct i2c_device_id asc7621_id[] = { - {"asc7621", asc7621}, - {"asc7621a", asc7621a}, - {}, + { .name = "asc7621", .driver_data = asc7621 }, + { .name = "asc7621a", .driver_data = asc7621a }, + { } }; MODULE_DEVICE_TABLE(i2c, asc7621_id); diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index aa159bf158a3..1c5945d4ba37 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -841,8 +841,9 @@ static int aspeed_create_pwm_cooling(struct device *dev, } snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port); - cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, - cdev->name, cdev, &aspeed_pwm_cool_ops); + cdev->tcdev = devm_thermal_of_child_cooling_device_register(dev, child, + cdev->name, cdev, + &aspeed_pwm_cool_ops); if (IS_ERR(cdev->tcdev)) return PTR_ERR(cdev->tcdev); diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index b5d97a27f80d..29a23484cbe7 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -277,7 +277,7 @@ static const struct ec_sensor_info sensors_family_amd_600[] = { [ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31), [ec_sensor_temp_mb] = - EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32), + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32), [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), [ec_sensor_temp_t_sensor] = @@ -404,6 +404,12 @@ static const struct ec_sensor_info sensors_family_intel_700[] = { [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), [ec_sensor_fan_cpu_opt] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_temp_water_in] = + EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), + [ec_sensor_temp_water_out] = + EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), }; /* Shortcuts for common combinations */ @@ -514,6 +520,13 @@ static const struct ec_board_info board_info_maximus_z690_formula = { .family = family_intel_600_series, }; +static const struct ec_board_info board_info_maximus_z790_extreme = { + .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW, + .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, + .family = family_intel_700_series, +}; + static const struct ec_board_info board_info_prime_x470_pro = { .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | @@ -621,6 +634,14 @@ static const struct ec_board_info board_info_strix_b550_i_gaming = { .family = family_amd_500_series, }; +static const struct ec_board_info board_info_strix_b650e_e_gaming = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_600_series, +}; + static const struct ec_board_info board_info_strix_b650e_i_gaming = { .sensors = SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_IN_CPU_CORE, @@ -628,6 +649,14 @@ static const struct ec_board_info board_info_strix_b650e_i_gaming = { .family = family_amd_600_series, }; +static const struct ec_board_info board_info_strix_b850_e_gaming_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_800_series, +}; + static const struct ec_board_info board_info_strix_b850_i_gaming_wifi = { .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_MB | SENSOR_TEMP_VRM, @@ -862,12 +891,18 @@ static const struct dmi_system_id dmi_table[] = { &board_info_maximus_x_hero), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z690 FORMULA", &board_info_maximus_z690_formula), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 EXTREME", + &board_info_maximus_z790_extreme), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", &board_info_strix_b550_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", &board_info_strix_b550_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-E GAMING WIFI", + &board_info_strix_b650e_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-I GAMING WIFI", &board_info_strix_b650e_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-E GAMING WIFI", + &board_info_strix_b850_e_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-I GAMING WIFI", &board_info_strix_b850_i_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING", diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index 1c7e9a98b757..f5de4894f0a2 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -278,7 +278,7 @@ static int atxp1_probe(struct i2c_client *client) }; static const struct i2c_device_id atxp1_id[] = { - { "atxp1" }, + { .name = "atxp1" }, { } }; MODULE_DEVICE_TABLE(i2c, atxp1_id); diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c index 645b8c2e704e..4aecf463180f 100644 --- a/drivers/hwmon/chipcap2.c +++ b/drivers/hwmon/chipcap2.c @@ -736,14 +736,14 @@ static void cc2_remove(struct i2c_client *client) } static const struct i2c_device_id cc2_id[] = { - { "cc2d23" }, - { "cc2d23s" }, - { "cc2d25" }, - { "cc2d25s" }, - { "cc2d33" }, - { "cc2d33s" }, - { "cc2d35" }, - { "cc2d35s" }, + { .name = "cc2d23" }, + { .name = "cc2d23s" }, + { .name = "cc2d25" }, + { .name = "cc2d25s" }, + { .name = "cc2d33" }, + { .name = "cc2d33s" }, + { .name = "cc2d35" }, + { .name = "cc2d35s" }, { } }; MODULE_DEVICE_TABLE(i2c, cc2_id); diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 6a0d94711ead..6215ea49faaa 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -25,7 +25,7 @@ #include <linux/moduleparam.h> #include <linux/pci.h> #include <asm/msr.h> -#include <asm/processor.h> +#include <linux/processor.h> #include <asm/cpu_device_id.h> #include <linux/sched/isolation.h> @@ -39,7 +39,6 @@ static int force_tjmax; module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); -#define NUM_REAL_CORES 512 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */ enum coretemp_attr_index { @@ -136,14 +135,14 @@ static const struct tjmax_model tjmax_model_table[] = { * which are covered by tjmax_table */ { INTEL_ATOM_BONNELL_MID, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx) - * Note: TjMax for E6xxT is 110C, but CPU type - * is undetectable by software + * Note: TjMax for E6xxT is 110C, but CPU + * type is undetectable by software */ { INTEL_ATOM_SALTWELL_MID, ANY, 90000 }, /* Atom Medfield (Z2460) */ { INTEL_ATOM_SALTWELL_TABLET, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */ { INTEL_ATOM_SALTWELL, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) - * Also matches S12x0 (stepping 9), covered by - * PCI table + * Also matches S12x0 (stepping 9), covered + * by PCI table */ { INTEL_ATOM_SILVERMONT, 9, 110000 }, /* Atom Bay Trail E38xx (embedded) */ { INTEL_ATOM_SILVERMONT, ANY, 90000 }, /* Atom Bay Trail Z37xx (tablet) */ @@ -169,7 +168,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) int tjmax_ee = 85000; int usemsr_ee = 1; int err; - u32 eax, edx; + u64 val; int i; u16 devfn = PCI_DEVFN(0, 0); struct pci_dev *host_bridge = pci_get_domain_bus_and_slot(0, 0, devfn); @@ -201,6 +200,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) { const struct tjmax_model *tm = &tjmax_model_table[i]; + if (c->x86_vfm == tm->vfm && (tm->stepping_mask == ANY || tm->stepping_mask == c->x86_stepping)) @@ -220,14 +220,13 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) * http://softwarecommunity.intel.com/Wiki/Mobility/720.htm * For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU */ - err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx); + err = rdmsrq_safe_on_cpu(id, 0x17, &val); if (err) { dev_warn(dev, - "Unable to access MSR 0x17, assuming desktop" - " CPU\n"); + "Unable to access MSR 0x17, assuming desktop CPU\n"); usemsr_ee = 0; } else if (c->x86_vfm < INTEL_CORE2_PENRYN && - !(eax & 0x10000000)) { + !(val & 0x10000000)) { /* * Trust bit 28 up to Penryn, I could not find any * documentation on that; if you happen to know @@ -235,8 +234,8 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) */ usemsr_ee = 0; } else { - /* Platform ID bits 52:50 (EDX starts at bit 32) */ - platform_id = (edx >> 18) & 0x7; + /* Platform ID bits 52:50 */ + platform_id = (val >> 50) & 0x7; /* * Mobile Penryn CPU seems to be platform ID 7 or 5 @@ -255,12 +254,11 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) } if (usemsr_ee) { - err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); + err = rdmsrq_safe_on_cpu(id, 0xee, &val); if (err) { dev_warn(dev, - "Unable to access MSR 0xEE, for Tjmax, left" - " at default\n"); - } else if (eax & 0x40000000) { + "Unable to access MSR 0xEE, for Tjmax, left at default\n"); + } else if (val & 0x40000000) { tjmax = tjmax_ee; } } else if (tjmax == 100000) { @@ -278,7 +276,7 @@ static int get_tjmax(struct temp_data *tdata, struct device *dev) { struct cpuinfo_x86 *c = &cpu_data(tdata->cpu); int err; - u32 eax, edx; + u64 msrval; u32 val; /* use static tjmax once it is set */ @@ -289,11 +287,11 @@ static int get_tjmax(struct temp_data *tdata, struct device *dev) * A new feature of current Intel(R) processors, the * IA32_TEMPERATURE_TARGET contains the TjMax value */ - err = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + err = rdmsrq_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &msrval); if (err) { dev_warn_once(dev, "Unable to read TjMax from CPU %u\n", tdata->cpu); } else { - val = (eax >> 16) & 0xff; + val = (msrval >> 16) & 0xff; if (val) return val * 1000; } @@ -314,7 +312,7 @@ static int get_tjmax(struct temp_data *tdata, struct device *dev) static int get_ttarget(struct temp_data *tdata, struct device *dev) { - u32 eax, edx; + u64 val; int tjmax, ttarget_offset, ret; /* @@ -324,14 +322,14 @@ static int get_ttarget(struct temp_data *tdata, struct device *dev) if (tdata->tjmax) return -ENODEV; - ret = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + ret = rdmsrq_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &val); if (ret) return ret; - tjmax = (eax >> 16) & 0xff; + tjmax = (val >> 16) & 0xff; /* Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. */ - ttarget_offset = (eax >> 8) & 0xff; + ttarget_offset = (val >> 8) & 0xff; return (tjmax - ttarget_offset) * 1000; } @@ -342,7 +340,7 @@ static int max_zones __read_mostly; static struct platform_device **zone_devices; static ssize_t show_label(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct platform_data *pdata = dev_get_drvdata(dev); struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_LABEL]); @@ -354,21 +352,21 @@ static ssize_t show_label(struct device *dev, } static ssize_t show_crit_alarm(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { - u32 eax, edx; + struct msr val; struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_CRIT_ALARM]); mutex_lock(&tdata->update_lock); - rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + rdmsrq_on_cpu(tdata->cpu, tdata->status_reg, &val.q); mutex_unlock(&tdata->update_lock); - return sprintf(buf, "%d\n", (eax >> 5) & 1); + return sprintf(buf, "%d\n", (val.l >> 5) & 1); } static ssize_t show_tjmax(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TJMAX]); int tjmax; @@ -381,7 +379,7 @@ static ssize_t show_tjmax(struct device *dev, } static ssize_t show_ttarget(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TTARGET]); int ttarget; @@ -396,9 +394,9 @@ static ssize_t show_ttarget(struct device *dev, } static ssize_t show_temp(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { - u32 eax, edx; + struct msr val; struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TEMP]); int tjmax; @@ -407,14 +405,14 @@ static ssize_t show_temp(struct device *dev, tjmax = get_tjmax(tdata, dev); /* Check whether the time interval has elapsed */ if (time_after(jiffies, tdata->last_updated + HZ)) { - rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + rdmsrq_on_cpu(tdata->cpu, tdata->status_reg, &val.q); /* * Ignore the valid bit. In all observed cases the register * value is either low or zero if the valid bit is 0. * Return it instead of reporting an error which doesn't * really help at all. */ - tdata->temp = tjmax - ((eax >> 16) & 0xff) * 1000; + tdata->temp = tjmax - ((val.l >> 16) & 0xff) * 1000; tdata->last_updated = jiffies; } @@ -424,7 +422,6 @@ static ssize_t show_temp(struct device *dev, static int create_core_attrs(struct temp_data *tdata, struct device *dev) { - int i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, @@ -432,6 +429,7 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev) static const char *const suffixes[TOTAL_ATTRS] = { "label", "crit_alarm", "input", "crit", "max" }; + int i; for (i = 0; i < tdata->attr_size; i++) { /* @@ -453,7 +451,6 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev) return sysfs_create_group(&dev->kobj, &tdata->attr_group); } - static int chk_ucode_version(unsigned int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); @@ -485,13 +482,7 @@ init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag) struct temp_data *tdata; if (!pdata->core_data) { - /* - * TODO: - * The information of actual possible cores in a package is broken for now. - * Will replace hardcoded NUM_REAL_CORES with actual per package core count - * when this information becomes available. - */ - pdata->nr_cores = NUM_REAL_CORES; + pdata->nr_cores = topology_num_cores_per_package(); pdata->core_data = kzalloc_objs(struct temp_data *, pdata->nr_cores); if (!pdata->core_data) @@ -560,7 +551,7 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, struct temp_data *tdata; struct platform_data *pdata = platform_get_drvdata(pdev); struct cpuinfo_x86 *c = &cpu_data(cpu); - u32 eax, edx; + u64 val; int err; if (!housekeeping_cpu(cpu, HK_TYPE_MISC)) @@ -571,7 +562,7 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, return -ENOMEM; /* Test if we can access the status register */ - err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); + err = rdmsrq_safe_on_cpu(cpu, tdata->status_reg, &val); if (err) goto err; @@ -781,7 +772,8 @@ static int coretemp_cpu_offline(unsigned int cpu) } return 0; } -static const struct x86_cpu_id __initconst coretemp_ids[] = { + +static const struct x86_cpu_id coretemp_ids[] __initconst = { X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL), {} }; diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c index 6cf5ab0f4b73..ea24056ae646 100644 --- a/drivers/hwmon/cros_ec_hwmon.c +++ b/drivers/hwmon/cros_ec_hwmon.c @@ -532,8 +532,8 @@ static void cros_ec_hwmon_register_fan_cooling_devices(struct device *dev, cpriv->hwmon_priv = priv; cpriv->index = i; - cdev = devm_thermal_of_cooling_device_register(dev, NULL, type, cpriv, - &cros_ec_thermal_cooling_ops); + cdev = devm_thermal_cooling_device_register(dev, type, cpriv, + &cros_ec_thermal_cooling_ops); if (IS_ERR(cdev)) { dev_warn(dev, "failed to register fan %zu as a cooling device: %pe\n", i, cdev); @@ -654,9 +654,10 @@ static int cros_ec_hwmon_resume(struct platform_device *pdev) } static const struct platform_device_id cros_ec_hwmon_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; +MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id); static struct platform_driver cros_ec_hwmon_driver = { .driver.name = DRV_NAME, @@ -667,7 +668,6 @@ static struct platform_driver cros_ec_hwmon_driver = { }; module_platform_driver(cros_ec_hwmon_driver); -MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id); MODULE_DESCRIPTION("ChromeOS EC Hardware Monitoring Driver"); MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 038edffc1ac7..6bb8b8d759c7 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1161,8 +1161,8 @@ static int dell_smm_init_cdev(struct device *dev, u8 fan_num) if (cdata) { cdata->fan_num = fan_num; cdata->data = data; - cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata, - &dell_smm_cooling_ops); + cdev = devm_thermal_cooling_device_register(dev, name, cdata, + &dell_smm_cooling_ops); if (IS_ERR(cdev)) { devm_kfree(dev, cdata); ret = PTR_ERR(cdev); @@ -1575,6 +1575,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], }, { + .ident = "Dell Latitude 7530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7530"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], + }, + { .ident = "Dell Latitude E6440", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 3d4057309950..7e839308e58f 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2515,8 +2515,8 @@ static void dme1737_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id dme1737_id[] = { - { "dme1737", dme1737 }, - { "sch5027", sch5027 }, + { .name = "dme1737", .driver_data = dme1737 }, + { .name = "sch5027", .driver_data = sch5027 }, { } }; MODULE_DEVICE_TABLE(i2c, dme1737_id); diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 42ec34cb8a5f..0618f6de9679 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -367,11 +367,11 @@ static int ds1621_probe(struct i2c_client *client) } static const struct i2c_device_id ds1621_id[] = { - { "ds1621", ds1621 }, - { "ds1625", ds1625 }, - { "ds1631", ds1631 }, - { "ds1721", ds1721 }, - { "ds1731", ds1731 }, + { .name = "ds1621", .driver_data = ds1621 }, + { .name = "ds1625", .driver_data = ds1625 }, + { .name = "ds1631", .driver_data = ds1631 }, + { .name = "ds1721", .driver_data = ds1721 }, + { .name = "ds1731", .driver_data = ds1731 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1621_id); diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index ce397042d90b..25287f0fa4c2 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -233,8 +233,8 @@ static int ds620_probe(struct i2c_client *client) } static const struct i2c_device_id ds620_id[] = { - {"ds620"}, - {} + { .name = "ds620" }, + { } }; MODULE_DEVICE_TABLE(i2c, ds620_id); diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index 964a8cb278f1..cd753b38709f 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -639,18 +639,18 @@ static const struct hwmon_chip_info emc1403_chip_info = { /* Last digit of chip name indicates number of channels */ static const struct i2c_device_id emc1403_idtable[] = { - { "emc1402", emc1402 }, - { "emc1403", emc1403 }, - { "emc1404", emc1404 }, - { "emc1412", emc1402 }, - { "emc1413", emc1403 }, - { "emc1414", emc1404 }, - { "emc1422", emc1402 }, - { "emc1423", emc1403 }, - { "emc1424", emc1404 }, - { "emc1428", emc1428 }, - { "emc1438", emc1428 }, - { "emc1442", emc1402 }, + { .name = "emc1402", .driver_data = emc1402 }, + { .name = "emc1403", .driver_data = emc1403 }, + { .name = "emc1404", .driver_data = emc1404 }, + { .name = "emc1412", .driver_data = emc1402 }, + { .name = "emc1413", .driver_data = emc1403 }, + { .name = "emc1414", .driver_data = emc1404 }, + { .name = "emc1422", .driver_data = emc1402 }, + { .name = "emc1423", .driver_data = emc1403 }, + { .name = "emc1424", .driver_data = emc1404 }, + { .name = "emc1428", .driver_data = emc1428 }, + { .name = "emc1438", .driver_data = emc1428 }, + { .name = "emc1442", .driver_data = emc1402 }, { } }; MODULE_DEVICE_TABLE(i2c, emc1403_idtable); diff --git a/drivers/hwmon/emc1812.c b/drivers/hwmon/emc1812.c new file mode 100644 index 000000000000..68575c27d090 --- /dev/null +++ b/drivers/hwmon/emc1812.c @@ -0,0 +1,965 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HWMON driver for Microchip EMC1812/13/14/15/33 Multichannel high-accuracy + * 2-wire low-voltage remote diode temperature monitor family. + * + * Copyright (C) 2026 Microchip Technology Inc. and its subsidiaries + * + * Author: Marius Cristea <marius.cristea@microchip.com> + * + * Datasheet can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/string.h> +#include <linux/units.h> +#include <linux/util_macros.h> + +/* EMC1812 Registers Addresses */ +#define EMC1812_STATUS_ADDR 0x02 +#define EMC1812_CONFIG_LO_ADDR 0x03 + +#define EMC1812_CFG_ADDR 0x09 +#define EMC1812_CONV_ADDR 0x0A +#define EMC1812_INT_DIODE_HIGH_LIMIT_ADDR 0x0B +#define EMC1812_INT_DIODE_LOW_LIMIT_ADDR 0x0C +#define EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR 0x0D +#define EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR 0x0E +#define EMC1812_ONE_SHOT_ADDR 0x0F + +#define EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR 0x13 +#define EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR 0x14 +#define EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR 0x15 +#define EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR 0x16 +#define EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR 0x17 +#define EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR 0x18 +#define EMC1812_EXT1_THERM_LIMIT_ADDR 0x19 +#define EMC1812_EXT2_THERM_LIMIT_ADDR 0x1A +#define EMC1812_EXT_DIODE_FAULT_STATUS_ADDR 0x1B + +#define EMC1812_DIODE_FAULT_MASK_ADDR 0x1F +#define EMC1812_INT_DIODE_THERM_LIMIT_ADDR 0x20 +#define EMC1812_THRM_HYS_ADDR 0x21 +#define EMC1812_CONSEC_ALERT_ADDR 0x22 + +#define EMC1812_EXT1_BETA_CONFIG_ADDR 0x25 +#define EMC1812_EXT2_BETA_CONFIG_ADDR 0x26 +#define EMC1812_EXT1_IDEALITY_FACTOR_ADDR 0x27 +#define EMC1812_EXT2_IDEALITY_FACTOR_ADDR 0x28 + +#define EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR 0x2C +#define EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR 0x2D +#define EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR 0x2E +#define EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR 0x2F +#define EMC1812_EXT3_THERM_LIMIT_ADDR 0x30 +#define EMC1812_EXT3_IDEALITY_FACTOR_ADDR 0x31 + +#define EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR 0x34 +#define EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR 0x35 +#define EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR 0x36 +#define EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR 0x37 +#define EMC1812_EXT4_THERM_LIMIT_ADDR 0x38 +#define EMC1812_EXT4_IDEALITY_FACTOR_ADDR 0x39 +#define EMC1812_HIGH_LIMIT_STATUS_ADDR 0x3A +#define EMC1812_LOW_LIMIT_STATUS_ADDR 0x3B +#define EMC1812_THERM_LIMIT_STATUS_ADDR 0x3C +#define EMC1812_ROC_GAIN_ADDR 0x3D +#define EMC1812_ROC_CONFIG_ADDR 0x3E +#define EMC1812_ROC_STATUS_ADDR 0x3F +#define EMC1812_R1_RESH_ADDR 0x40 +#define EMC1812_R1_LIMH_ADDR 0x41 +#define EMC1812_R1_LIML_ADDR 0x42 +#define EMC1812_R1_SMPL_ADDR 0x43 +#define EMC1812_R2_RESH_ADDR 0x44 +#define EMC1812_R2_3_RESL_ADDR 0x45 +#define EMC1812_R2_LIMH_ADDR 0x46 +#define EMC1812_R2_LIML_ADDR 0x47 +#define EMC1812_R2_SMPL_ADDR 0x48 +#define EMC1812_PER_MAXTH_1_ADDR 0x49 +#define EMC1812_PER_MAXT1L_ADDR 0x4A +#define EMC1812_PER_MAXTH_2_ADDR 0x4B +#define EMC1812_PER_MAXT2_3L_ADDR 0x4C +#define EMC1812_GBL_MAXT1H_ADDR 0x4D +#define EMC1812_GBL_MAXT1L_ADDR 0x4E +#define EMC1812_GBL_MAXT2H_ADDR 0x4F +#define EMC1812_GBL_MAXT2L_ADDR 0x50 +#define EMC1812_FILTER_SEL_ADDR 0x51 + +#define EMC1812_INT_HIGH_BYTE_ADDR 0x60 +#define EMC1812_INT_LOW_BYTE_ADDR 0x61 +#define EMC1812_EXT1_HIGH_BYTE_ADDR 0x62 +#define EMC1812_EXT1_LOW_BYTE_ADDR 0x63 +#define EMC1812_EXT2_HIGH_BYTE_ADDR 0x64 +#define EMC1812_EXT2_LOW_BYTE_ADDR 0x65 +#define EMC1812_EXT3_HIGH_BYTE_ADDR 0x66 +#define EMC1812_EXT3_LOW_BYTE_ADDR 0x67 +#define EMC1812_EXT4_HIGH_BYTE_ADDR 0x68 +#define EMC1812_EXT4_LOW_BYTE_ADDR 0x69 +#define EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR 0x6A +#define EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR 0x6B +#define EMC1812_HOTTEST_STATUS_ADDR 0x6C +#define EMC1812_HOTTEST_CFG_ADDR 0x6D + +#define EMC1812_PRODUCT_ID_ADDR 0xFD +#define EMC1812_MANUFACTURER_ID_ADDR 0xFE +#define EMC1812_REVISION_ADDR 0xFF + +/* EMC1812 Config Bits */ +#define EMC1812_CFG_MSKAL BIT(7) +#define EMC1812_CFG_RS BIT(6) +#define EMC1812_CFG_ATTHM BIT(5) +#define EMC1812_CFG_RECD12 BIT(4) +#define EMC1812_CFG_RECD34 BIT(3) +#define EMC1812_CFG_RANGE BIT(2) +#define EMC1812_CFG_DA_ENA BIT(1) +#define EMC1812_CFG_APDD BIT(0) + +/* EMC1812 Status Bits */ +#define EMC1812_STATUS_ROCF BIT(7) +#define EMC1812_STATUS_HOTCHG BIT(6) +#define EMC1812_STATUS_BUSY BIT(5) +#define EMC1812_STATUS_HIGH BIT(4) +#define EMC1812_STATUS_LOW BIT(3) +#define EMC1812_STATUS_FAULT BIT(2) +#define EMC1812_STATUS_ETHRM BIT(1) +#define EMC1812_STATUS_ITHRM BIT(0) + +#define EMC1812_BETA_LOCK_VAL 0x0F + +#define EMC1812_TEMP_CH_ADDR(index) (EMC1812_INT_HIGH_BYTE_ADDR + 2 * (index)) + +#define EMC1812_FILTER_MASK_LEN 2 + +#define EMC1812_PID 0x81 +#define EMC1813_PID 0x87 +#define EMC1814_PID 0x84 +#define EMC1815_PID 0x85 +#define EMC1833_PID 0x83 + +/* The maximum number of channels a member of the family can have */ +#define EMC1812_MAX_NUM_CHANNELS 5 +#define EMC1812_TEMP_OFFSET 64 + +#define EMC1812_DEFAULT_IDEALITY_FACTOR 0x12 + +/* Constants and default values */ +#define EMC1812_HIGH_LIMIT_DEFAULT (85 + EMC1812_TEMP_OFFSET) + +#define EMC1812_TEMP_MASK (HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | \ + HWMON_T_CRIT | HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST | \ + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | \ + HWMON_T_CRIT_ALARM | HWMON_T_LABEL) + +static const struct hwmon_channel_info * const emc1812_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + EMC1812_TEMP_MASK, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT), + NULL +}; + +/** + * struct emc1812_features - features of a emc1812 instance + * @name: chip's name + * @phys_channels: number of physical channels supported by the chip + * @has_ext2_beta_reg: the EXT2_BETA register is available on the chip + */ +struct emc1812_features { + const char *name; + u8 phys_channels; + bool has_ext2_beta_reg; +}; + +static const struct emc1812_features emc1833_chip_config = { + .name = "emc1833", + .phys_channels = 3, + .has_ext2_beta_reg = true, +}; + +static const struct emc1812_features emc1812_chip_config = { + .name = "emc1812", + .phys_channels = 2, + .has_ext2_beta_reg = false, +}; + +static const struct emc1812_features emc1813_chip_config = { + .name = "emc1813", + .phys_channels = 3, + .has_ext2_beta_reg = true, +}; + +static const struct emc1812_features emc1814_chip_config = { + .name = "emc1814", + .phys_channels = 4, + .has_ext2_beta_reg = false, +}; + +static const struct emc1812_features emc1815_chip_config = { + .name = "emc1815", + .phys_channels = 5, + .has_ext2_beta_reg = false, +}; + +enum emc1812_limit_type {temp_min, temp_max}; + +static const u8 emc1812_temp_map[] = { + [hwmon_temp_min] = temp_min, + [hwmon_temp_max] = temp_max, +}; + +static const u8 emc1812_ideality_regs[] = { + [0] = 0xff, + [1] = EMC1812_EXT1_IDEALITY_FACTOR_ADDR, + [2] = EMC1812_EXT2_IDEALITY_FACTOR_ADDR, + [3] = EMC1812_EXT3_IDEALITY_FACTOR_ADDR, + [4] = EMC1812_EXT4_IDEALITY_FACTOR_ADDR, +}; + +static const u8 emc1812_temp_crit_regs[] = { + [0] = EMC1812_INT_DIODE_THERM_LIMIT_ADDR, + [1] = EMC1812_EXT1_THERM_LIMIT_ADDR, + [2] = EMC1812_EXT2_THERM_LIMIT_ADDR, + [3] = EMC1812_EXT3_THERM_LIMIT_ADDR, + [4] = EMC1812_EXT4_THERM_LIMIT_ADDR, +}; + +static const u8 emc1812_limit_regs[][2] = { + [0] = { + [temp_min] = EMC1812_INT_DIODE_LOW_LIMIT_ADDR, + [temp_max] = EMC1812_INT_DIODE_HIGH_LIMIT_ADDR, + }, + [1] = { + [temp_min] = EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [2] = { + [temp_min] = EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [3] = { + [temp_min] = EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [4] = { + [temp_min] = EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, +}; + +static const u8 emc1812_limit_regs_low[][2] = { + [0] = { + [temp_min] = 0xff, + [temp_max] = 0xff, + }, + [1] = { + [temp_min] = EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [2] = { + [temp_min] = EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [3] = { + [temp_min] = EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [4] = { + [temp_min] = EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR, + }, +}; + +/* Lookup table for temperature conversion times in msec */ +static const u16 emc1812_conv_time[] = { + 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 62, 31, 16 +}; + +/** + * struct emc1812_data - information about chip parameters + * @labels: labels of the channels + * @active_ch_mask: active channels + * @chip: pointer to structure holding chip features + * @regmap: device register map + * @recd34_en: state of Resistance Error Correction (REC) on channels 3 and 4 + * @recd12_en: state of Resistance Error Correction (REC) on channels 1 and 2 + * @apdd_en: state of anti-parallel diode mode + */ +struct emc1812_data { + const char *labels[EMC1812_MAX_NUM_CHANNELS]; + unsigned long active_ch_mask; + const struct emc1812_features *chip; + struct regmap *regmap; + bool recd34_en; + bool recd12_en; + bool apdd_en; +}; + +/* emc1812 regmap configuration */ +static const struct regmap_range emc1812_regmap_writable_ranges[] = { + regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR), + regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, EMC1812_EXT2_THERM_LIMIT_ADDR), + regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR), + regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_EXT4_IDEALITY_FACTOR_ADDR), + regmap_reg_range(EMC1812_ROC_GAIN_ADDR, EMC1812_ROC_CONFIG_ADDR), + regmap_reg_range(EMC1812_R1_LIMH_ADDR, EMC1812_R1_SMPL_ADDR), + regmap_reg_range(EMC1812_R2_LIMH_ADDR, EMC1812_R2_SMPL_ADDR), + regmap_reg_range(EMC1812_FILTER_SEL_ADDR, EMC1812_FILTER_SEL_ADDR), + regmap_reg_range(EMC1812_HOTTEST_CFG_ADDR, EMC1812_HOTTEST_CFG_ADDR), +}; + +static const struct regmap_access_table emc1812_regmap_wr_table = { + .yes_ranges = emc1812_regmap_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(emc1812_regmap_writable_ranges), +}; + +static const struct regmap_range emc1812_regmap_rd_ranges[] = { + regmap_reg_range(EMC1812_STATUS_ADDR, EMC1812_CONFIG_LO_ADDR), + regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR), + regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, + EMC1812_EXT_DIODE_FAULT_STATUS_ADDR), + regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR), + regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_FILTER_SEL_ADDR), + regmap_reg_range(EMC1812_INT_HIGH_BYTE_ADDR, EMC1812_HOTTEST_CFG_ADDR), + regmap_reg_range(EMC1812_PRODUCT_ID_ADDR, EMC1812_REVISION_ADDR), +}; + +static const struct regmap_access_table emc1812_regmap_rd_table = { + .yes_ranges = emc1812_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(emc1812_regmap_rd_ranges), +}; + +static bool emc1812_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case EMC1812_STATUS_ADDR: + case EMC1812_EXT_DIODE_FAULT_STATUS_ADDR: + case EMC1812_DIODE_FAULT_MASK_ADDR: + case EMC1812_EXT1_BETA_CONFIG_ADDR: + case EMC1812_EXT2_BETA_CONFIG_ADDR: + case EMC1812_HIGH_LIMIT_STATUS_ADDR: + case EMC1812_LOW_LIMIT_STATUS_ADDR: + case EMC1812_THERM_LIMIT_STATUS_ADDR: + case EMC1812_ROC_STATUS_ADDR: + case EMC1812_PER_MAXTH_1_ADDR: + case EMC1812_PER_MAXT1L_ADDR: + case EMC1812_PER_MAXTH_2_ADDR: + case EMC1812_PER_MAXT2_3L_ADDR: + case EMC1812_GBL_MAXT1H_ADDR: + case EMC1812_GBL_MAXT1L_ADDR: + case EMC1812_GBL_MAXT2H_ADDR: + case EMC1812_GBL_MAXT2L_ADDR: + case EMC1812_INT_HIGH_BYTE_ADDR: + case EMC1812_INT_LOW_BYTE_ADDR: + case EMC1812_EXT1_HIGH_BYTE_ADDR: + case EMC1812_EXT1_LOW_BYTE_ADDR: + case EMC1812_EXT2_HIGH_BYTE_ADDR: + case EMC1812_EXT2_LOW_BYTE_ADDR: + case EMC1812_EXT3_HIGH_BYTE_ADDR: + case EMC1812_EXT3_LOW_BYTE_ADDR: + case EMC1812_EXT4_HIGH_BYTE_ADDR: + case EMC1812_EXT4_LOW_BYTE_ADDR: + case EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR: + case EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR: + case EMC1812_HOTTEST_STATUS_ADDR: + return true; + default: + return false; + } +} + +static const struct regmap_config emc1812_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &emc1812_regmap_rd_table, + .wr_table = &emc1812_regmap_wr_table, + .volatile_reg = emc1812_is_volatile_reg, + .max_register = EMC1812_REVISION_ADDR, + .cache_type = REGCACHE_MAPLE, +}; + +static umode_t emc1812_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct emc1812_data *data = _data; + + switch (type) { + case hwmon_temp: + /* Don't show channels which are not enabled */ + if (!(data->active_ch_mask & BIT(channel))) + return 0; + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + return 0644; + case hwmon_temp_crit_alarm: + case hwmon_temp_input: + case hwmon_temp_fault: + case hwmon_temp_max_alarm: + case hwmon_temp_max_hyst: + case hwmon_temp_min_alarm: + return 0444; + case hwmon_temp_label: + if (data->labels[channel]) + return 0444; + return 0; + default: + return 0; + } + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + default: + return 0; + } + default: + return 0; + } +}; + +static int emc1812_get_temp(struct emc1812_data *data, int channel, long *val) +{ + __be16 tmp_be16; + int ret; + + ret = regmap_bulk_read(data->regmap, EMC1812_TEMP_CH_ADDR(channel), + &tmp_be16, sizeof(tmp_be16)); + if (ret) + return ret; + + /* Range is always -64 to 191.875°C */ + *val = ((be16_to_cpu(tmp_be16) >> 5) - (EMC1812_TEMP_OFFSET << 3)) * 125; + + return 0; +} + +static int emc1812_get_crit_limit_temp(struct emc1812_data *data, int channel, long *val) +{ + unsigned int tmp; + int ret; + + /* Critical register is 8bits long and keeps only integer part of temperature */ + ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &tmp); + if (ret) + return ret; + + *val = tmp; + /* Range is always -64 to 191°C */ + *val = (*val - EMC1812_TEMP_OFFSET) * 1000; + + return 0; +} + +static int emc1812_get_limit_temp(struct emc1812_data *data, int ch, + enum emc1812_limit_type type, long *val) +{ + unsigned int regvalh; + unsigned int regvall = 0; + int ret; + + ret = regmap_read(data->regmap, emc1812_limit_regs[ch][type], ®valh); + if (ret < 0) + return ret; + + if (ch) { + ret = regmap_read(data->regmap, emc1812_limit_regs_low[ch][type], ®vall); + if (ret < 0) + return ret; + } + + /* Range is always -64 to 191.875°C */ + *val = ((regvalh << 3) | (regvall >> 5)); + *val = (*val - (EMC1812_TEMP_OFFSET << 3)) * 125; + + return 0; +} + +static int emc1812_read_reg(struct device *dev, struct emc1812_data *data, u32 attr, + int channel, long *val) +{ + unsigned int hyst; + int ret; + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + return emc1812_get_limit_temp(data, channel, emc1812_temp_map[attr], val); + case hwmon_temp_crit: + return emc1812_get_crit_limit_temp(data, channel, val); + case hwmon_temp_input: + return emc1812_get_temp(data, channel, val); + case hwmon_temp_max_hyst: + ret = emc1812_get_limit_temp(data, channel, temp_max, val); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst); + if (ret < 0) + return ret; + + *val -= (long)hyst * 1000; + + return 0; + case hwmon_temp_crit_hyst: + ret = emc1812_get_crit_limit_temp(data, channel, val); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst); + if (ret < 0) + return ret; + + *val -= (long)hyst * 1000; + + return 0; + case hwmon_temp_min_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_LOW_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_max_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_HIGH_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_crit_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_THERM_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_fault: + *val = regmap_test_bits(data->regmap, EMC1812_EXT_DIODE_FAULT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + unsigned int convrate; + int ret; + + switch (type) { + case hwmon_temp: + return emc1812_read_reg(dev, data, attr, channel, val); + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(data->regmap, EMC1812_CONV_ADDR, &convrate); + if (ret < 0) + return ret; + + if (convrate > 10) + convrate = 4; + + *val = DIV_ROUND_CLOSEST(16000, 1 << convrate); + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + + if (channel >= data->chip->phys_channels) + return -EOPNOTSUPP; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = data->labels[channel]; + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_set_hyst(struct emc1812_data *data, int channel, int val) +{ + unsigned int limit; + int hyst, ret; + + /* Critical register is 8bits long and keeps only integer part of temperature */ + ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &limit); + if (ret) + return ret; + + hyst = clamp_val((int)limit - val, 0, 255); + + ret = regmap_write(data->regmap, EMC1812_THRM_HYS_ADDR, hyst); + + return ret; +} + +static int emc1812_set_temp(struct emc1812_data *data, int channel, + enum emc1812_limit_type map, int val) +{ + unsigned int valh, vall; + u8 regh, regl; + int ret; + + regh = emc1812_limit_regs[channel][map]; + regl = emc1812_limit_regs_low[channel][map]; + + if (channel) { + val = DIV_ROUND_CLOSEST(val, 125); + valh = (val >> 3) & 0xff; + vall = (val & 0x07) << 5; + } else { + /* Temperature limit for internal channel is stored on 8bits */ + valh = DIV_ROUND_CLOSEST(val, 1000); + valh = clamp_val(valh, 0, 255); + } + + ret = regmap_write(data->regmap, regh, valh); + if (ret < 0) + return ret; + + if (channel) + ret = regmap_write(data->regmap, regl, vall); + + return ret; +} + +static int emc1812_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + unsigned int interval, tmp; + + switch (type) { + case hwmon_temp: + /* Range should be -64000 to 191875°C + (EMC1812_TEMP_OFFSET * 1000) */ + val = clamp_val(val, -64000, 191875); + val = val + (EMC1812_TEMP_OFFSET * 1000); + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + return emc1812_set_temp(data, channel, emc1812_temp_map[attr], val); + case hwmon_temp_crit: + /* Critical temperature limit is stored on 8bits */ + val = DIV_ROUND_CLOSEST(val, 1000); + tmp = clamp_val(val, 0, 255); + return regmap_write(data->regmap, emc1812_temp_crit_regs[channel], tmp); + case hwmon_temp_crit_hyst: + /* Critical temperature hysteresis is stored on 8bits */ + val = DIV_ROUND_CLOSEST(val, 1000); + tmp = clamp_val(val, 0, 255); + return emc1812_set_hyst(data, channel, tmp); + default: + return -EOPNOTSUPP; + } + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + interval = clamp_val(val, 0, 16000); + tmp = find_closest_descending(interval, emc1812_conv_time, + ARRAY_SIZE(emc1812_conv_time)); + return regmap_write(data->regmap, EMC1812_CONV_ADDR, tmp); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_init(struct emc1812_data *priv) +{ + int i, ret; + u8 val; + + ret = regmap_write(priv->regmap, EMC1812_THRM_HYS_ADDR, 0x0A); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_CONSEC_ALERT_ADDR, 0x70); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_FILTER_SEL_ADDR, 0); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_HOTTEST_CFG_ADDR, 0); + if (ret) + return ret; + + /* Enables the beta compensation factor auto-detection function for beta1 and beta2 */ + ret = regmap_write(priv->regmap, EMC1812_EXT1_BETA_CONFIG_ADDR, + EMC1812_BETA_LOCK_VAL); + if (ret) + return ret; + + if (priv->chip->has_ext2_beta_reg) { + ret = regmap_write(priv->regmap, EMC1812_EXT2_BETA_CONFIG_ADDR, + EMC1812_BETA_LOCK_VAL); + if (ret) + return ret; + } + + for (i = 0; i < priv->chip->phys_channels; i++) { + if (!test_bit(i, &priv->active_ch_mask)) + continue; + + /* Update the max temperature limit for extended temperature range. */ + ret = emc1812_set_temp(priv, i, emc1812_temp_map[hwmon_temp_max], + EMC1812_HIGH_LIMIT_DEFAULT * 1000); + if (ret) + return ret; + + /* Update the critical temperature limit for extended temperature range. */ + ret = regmap_write(priv->regmap, emc1812_temp_crit_regs[i], + EMC1812_HIGH_LIMIT_DEFAULT); + if (ret) + return ret; + + /* Set the ideality factor */ + if (i > 0) { + ret = regmap_write(priv->regmap, emc1812_ideality_regs[i], + EMC1812_DEFAULT_IDEALITY_FACTOR); + if (ret) + return ret; + } + } + + /* + * Set default values in registers. APDD, RECD12 and RECD34 are active on 0. + * Set the device to be in Run (Active) state and converting on all + * channels. + * Don't change conversion rate. After reset, default is 4 conversions/seconds. + * The temperature measurement range is -64°C to +191.875°C. + * Set ALERT/THERM2 pin to be in comparator mode (When the ALERT/THERM2 pin is + * asserted in comparator mode, the corresponding High Limit Status bits are set. + * Reading these bits does not clear them until the ALERT/THERM2 pin is deasserted. + * Once the ALERT/THERM2 pin is deasserted, the status bits are automatically + * cleared.). + */ + val = FIELD_PREP(EMC1812_CFG_MSKAL, 0) | + FIELD_PREP(EMC1812_CFG_RS, 0) | + FIELD_PREP(EMC1812_CFG_ATTHM, 1) | + FIELD_PREP(EMC1812_CFG_RECD12, !priv->recd12_en) | + FIELD_PREP(EMC1812_CFG_RECD34, !priv->recd34_en) | + FIELD_PREP(EMC1812_CFG_RANGE, 1) | + FIELD_PREP(EMC1812_CFG_DA_ENA, 0) | + FIELD_PREP(EMC1812_CFG_APDD, !priv->apdd_en); + + return regmap_write(priv->regmap, EMC1812_CFG_ADDR, val); +} + +static int emc1812_parse_fw_config(struct emc1812_data *data, struct device *dev) +{ + unsigned int reg_nr = 0; + int ret; + + /* To be able to load the driver in case we don't have device tree */ + if (!dev_fwnode(dev)) { + data->active_ch_mask = BIT(data->chip->phys_channels) - 1; + return 0; + } + + data->apdd_en = device_property_read_bool(dev, "microchip,enable-anti-parallel"); + data->recd12_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel1-2"); + data->recd34_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel3-4"); + + /* Internal temperature channel is always active */ + data->labels[reg_nr] = "internal_diode"; + set_bit(reg_nr, &data->active_ch_mask); + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", ®_nr); + if (ret || reg_nr >= data->chip->phys_channels) + return dev_err_probe(dev, -EINVAL, + "The index is higher then the chip supports\n"); + /* Mark channel as active */ + set_bit(reg_nr, &data->active_ch_mask); + + fwnode_property_read_string(child, "label", &data->labels[reg_nr]); + } + + return 0; +} + +static int emc1812_chip_identify(struct emc1812_data *data, struct i2c_client *client) +{ + const struct emc1812_features *chip; + struct device *dev = &client->dev; + unsigned int tmp; + int ret; + + ret = regmap_read(data->regmap, EMC1812_PRODUCT_ID_ADDR, &tmp); + if (ret) + return ret; + + switch (tmp) { + case EMC1812_PID: + data->chip = &emc1812_chip_config; + break; + case EMC1813_PID: + data->chip = &emc1813_chip_config; + break; + case EMC1814_PID: + data->chip = &emc1814_chip_config; + break; + case EMC1815_PID: + data->chip = &emc1815_chip_config; + break; + case EMC1833_PID: + data->chip = &emc1833_chip_config; + break; + default: + /* + * If failed to identify the hardware based on internal registers, + * try using fallback compatible in device tree to deal with some + * newer part number. + */ + chip = i2c_get_match_data(client); + if (!chip) + return -ENODEV; + + dev_warn(dev, "Unrecognized hardware ID 0x%x, using %s from devicetree data\n", + tmp, chip->name); + + data->chip = chip; + + return 0; + } + + return 0; +} + +static const struct hwmon_ops emc1812_ops = { + .is_visible = emc1812_is_visible, + .read = emc1812_read, + .read_string = emc1812_read_string, + .write = emc1812_write, +}; + +static const struct hwmon_chip_info emc1812_chip_info = { + .ops = &emc1812_ops, + .info = emc1812_info, +}; + +static int emc1812_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct emc1812_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &emc1812_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Cannot initialize register map\n"); + + ret = emc1812_chip_identify(data, client); + if (ret) + return dev_err_probe(dev, ret, "Chip identification fails\n"); + + ret = emc1812_parse_fw_config(data, dev); + if (ret) + return ret; + + ret = emc1812_init(data); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize device\n"); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &emc1812_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id emc1812_id[] = { + { .name = "emc1812", .driver_data = (kernel_ulong_t)&emc1812_chip_config }, + { .name = "emc1813", .driver_data = (kernel_ulong_t)&emc1813_chip_config }, + { .name = "emc1814", .driver_data = (kernel_ulong_t)&emc1814_chip_config }, + { .name = "emc1815", .driver_data = (kernel_ulong_t)&emc1815_chip_config }, + { .name = "emc1833", .driver_data = (kernel_ulong_t)&emc1833_chip_config }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc1812_id); + +static const struct of_device_id emc1812_of_match[] = { + { + .compatible = "microchip,emc1812", + .data = &emc1812_chip_config + }, + { + .compatible = "microchip,emc1813", + .data = &emc1813_chip_config + }, + { + .compatible = "microchip,emc1814", + .data = &emc1814_chip_config + }, + { + .compatible = "microchip,emc1815", + .data = &emc1815_chip_config + }, + { + .compatible = "microchip,emc1833", + .data = &emc1833_chip_config + }, + { } +}; +MODULE_DEVICE_TABLE(of, emc1812_of_match); + +static struct i2c_driver emc1812_driver = { + .driver = { + .name = "emc1812", + .of_match_table = emc1812_of_match, + }, + .probe = emc1812_probe, + .id_table = emc1812_id, +}; +module_i2c_driver(emc1812_driver); + +MODULE_AUTHOR("Marius Cristea <marius.cristea@microchip.com>"); +MODULE_DESCRIPTION("EMC1812/13/14/15/33 high-accuracy remote diode temperature monitor Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 9b8e925af030..27dc149a3ed9 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -624,7 +624,7 @@ emc2103_probe(struct i2c_client *client) } static const struct i2c_device_id emc2103_ids[] = { - { "emc2103" }, + { .name = "emc2103" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, emc2103_ids); diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 64b213e1451e..eef3b021671b 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -32,6 +32,7 @@ #define EMC2305_REG_DRIVE_PWM_OUT 0x2b #define EMC2305_OPEN_DRAIN 0x0 #define EMC2305_PUSH_PULL 0x1 +#define EMC2305_PWM_SHUTDOWN_UNSET -1 #define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) @@ -59,10 +60,10 @@ enum emc230x_product_id { }; static const struct i2c_device_id emc2305_ids[] = { - { "emc2305" }, - { "emc2303" }, - { "emc2302" }, - { "emc2301" }, + { .name = "emc2305" }, + { .name = "emc2303" }, + { .name = "emc2302" }, + { .name = "emc2301" }, { } }; MODULE_DEVICE_TABLE(i2c, emc2305_ids); @@ -104,6 +105,7 @@ struct emc2305_cdev_data { * @pwm_output_mask: PWM output mask * @pwm_polarity_mask: PWM polarity mask * @pwm_separate: separate PWM settings for every channel + * @pwm_shutdown: Set shutdown PWM. * @pwm_min: array of minimum PWM per channel * @pwm_freq: array of PWM frequency per channel * @cdev_data: array of cooling devices data @@ -116,6 +118,7 @@ struct emc2305_data { u8 pwm_output_mask; u8 pwm_polarity_mask; bool pwm_separate; + s16 pwm_shutdown[EMC2305_PWM_MAX]; u8 pwm_min[EMC2305_PWM_MAX]; u16 pwm_freq[EMC2305_PWM_MAX]; struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; @@ -309,9 +312,9 @@ static int emc2305_set_single_tz(struct device *dev, struct device_node *fan_nod pwm = data->pwm_min[cdev_idx]; data->cdev_data[cdev_idx].cdev = - devm_thermal_of_cooling_device_register(dev, fan_node, - emc2305_fan_name[idx], data, - &emc2305_cooling_ops); + devm_thermal_of_child_cooling_device_register(dev, fan_node, + emc2305_fan_name[idx], data, + &emc2305_cooling_ops); if (IS_ERR(data->cdev_data[cdev_idx].cdev)) { dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); @@ -539,6 +542,7 @@ static int emc2305_of_parse_pwm_child(struct device *dev, struct device_node *child, struct emc2305_data *data) { u32 ch; + u32 pwm_shutdown_percent; int ret; struct of_phandle_args args; @@ -548,6 +552,12 @@ static int emc2305_of_parse_pwm_child(struct device *dev, return ret; } + if (ch >= data->pwm_num) { + dev_err(dev, "invalid reg %u for node %pOF (valid range 0-%u)\n", ch, child, + data->pwm_num - 1); + return -EINVAL; + } + ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells", 0, &args); if (ret) @@ -579,6 +589,16 @@ static int emc2305_of_parse_pwm_child(struct device *dev, } of_node_put(args.np); + + ret = of_property_read_u32(child, "fan-shutdown-percent", + &pwm_shutdown_percent); + + if (!ret) { + pwm_shutdown_percent = clamp(pwm_shutdown_percent, 0, 100); + data->pwm_shutdown[ch] = + DIV_ROUND_CLOSEST(pwm_shutdown_percent * EMC2305_FAN_MAX, 100); + } + return 0; } @@ -612,6 +632,7 @@ static int emc2305_probe(struct i2c_client *client) int ret; int i; int pwm_childs; + u32 ch; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -631,6 +652,9 @@ static int emc2305_probe(struct i2c_client *client) if (ret) return ret; + for (i = 0; i < EMC2305_PWM_MAX; i++) + data->pwm_shutdown[i] = EMC2305_PWM_SHUTDOWN_UNSET; + pwm_childs = emc2305_probe_childs_from_dt(dev); pdata = dev_get_platdata(&client->dev); @@ -680,12 +704,18 @@ static int emc2305_probe(struct i2c_client *client) if (IS_REACHABLE(CONFIG_THERMAL)) { /* Parse and check for the available PWM child nodes */ if (pwm_childs > 0) { - i = 0; for_each_child_of_node_scoped(dev->of_node, child) { - ret = emc2305_set_single_tz(dev, child, i); + ret = of_property_read_u32(child, "reg", &ch); + if (ret || ch >= data->pwm_num) + continue; + + /* + * emc2305_set_single_tz() uses 0 for the common cooling + * device and 1..pwm_num for individual fan channels. + */ + ret = emc2305_set_single_tz(dev, child, ch + 1); if (ret != 0) return ret; - i++; } } else { ret = emc2305_set_tz(dev); @@ -714,6 +744,23 @@ static int emc2305_probe(struct i2c_client *client) return 0; } +static void emc2305_shutdown(struct i2c_client *client) +{ + int i; + int ret; + struct emc2305_data *data = i2c_get_clientdata(client); + + for (i = 0; i < data->pwm_num; i++) { + if (data->pwm_shutdown[i] != EMC2305_PWM_SHUTDOWN_UNSET) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i), + data->pwm_shutdown[i]); + if (ret < 0) + dev_warn(&client->dev, + "Failed to set shutdown PWM for ch %d\n", i); + } + } +} + static const struct of_device_id of_emc2305_match_table[] = { { .compatible = "microchip,emc2305", }, {}, @@ -726,6 +773,7 @@ static struct i2c_driver emc2305_driver = { .of_match_table = of_emc2305_match_table, }, .probe = emc2305_probe, + .shutdown = emc2305_shutdown, .id_table = emc2305_ids, }; diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c index 1100c6e5daa7..c13ea8748683 100644 --- a/drivers/hwmon/emc6w201.c +++ b/drivers/hwmon/emc6w201.c @@ -464,7 +464,7 @@ static int emc6w201_probe(struct i2c_client *client) } static const struct i2c_device_id emc6w201_id[] = { - { "emc6w201" }, + { .name = "emc6w201" }, { } }; MODULE_DEVICE_TABLE(i2c, emc6w201_id); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 7e867f132420..cc3a9612f2c0 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -877,9 +877,9 @@ static int f75375_detect(struct i2c_client *client, } static const struct i2c_device_id f75375_id[] = { - { "f75373", f75373 }, - { "f75375", f75375 }, - { "f75387", f75387 }, + { .name = "f75373", .driver_data = f75373 }, + { .name = "f75375", .driver_data = f75375 }, + { .name = "f75387", .driver_data = f75387 }, { } }; MODULE_DEVICE_TABLE(i2c, f75375_id); diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index efcbea2d070e..ad4ed4162b57 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -19,6 +19,7 @@ #include <linux/sched.h> #include <linux/topology.h> #include <asm/processor.h> +#include <asm/cpuid/api.h> #include <asm/msr.h> MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor"); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 1211fa2259e5..019fc32bf318 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -225,13 +225,13 @@ static struct fschmd_data *fschmd_update_device(struct device *dev); */ static const struct i2c_device_id fschmd_id[] = { - { "fscpos", fscpos }, - { "fscher", fscher }, - { "fscscy", fscscy }, - { "fschrc", fschrc }, - { "fschmd", fschmd }, - { "fschds", fschds }, - { "fscsyl", fscsyl }, + { .name = "fscpos", .driver_data = fscpos }, + { .name = "fscher", .driver_data = fscher }, + { .name = "fscscy", .driver_data = fscscy }, + { .name = "fschrc", .driver_data = fschrc }, + { .name = "fschmd", .driver_data = fschmd }, + { .name = "fschds", .driver_data = fschds }, + { .name = "fscsyl", .driver_data = fscsyl }, { } }; MODULE_DEVICE_TABLE(i2c, fschmd_id); diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 08dcc6a7fb62..06be120a349d 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -49,7 +49,7 @@ static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; static const struct i2c_device_id fts_id[] = { - { "ftsteutates" }, + { .name = "ftsteutates" }, { } }; MODULE_DEVICE_TABLE(i2c, fts_id); diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c index 39ae8f826417..e2166ee76286 100644 --- a/drivers/hwmon/g760a.c +++ b/drivers/hwmon/g760a.c @@ -197,7 +197,7 @@ static int g760a_probe(struct i2c_client *client) } static const struct i2c_device_id g760a_id[] = { - { "g760a" }, + { .name = "g760a" }, { } }; MODULE_DEVICE_TABLE(i2c, g760a_id); diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 4fa3aa1271da..407cf7af4fdd 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -44,9 +44,9 @@ #define DRVNAME "g762" static const struct i2c_device_id g762_id[] = { - { "g761" }, - { "g762" }, - { "g763" }, + { .name = "g761" }, + { .name = "g762" }, + { .name = "g763" }, { } }; MODULE_DEVICE_TABLE(i2c, g762_id); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index 9c68bc013950..742c130cec7f 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -642,7 +642,7 @@ static int gl518_probe(struct i2c_client *client) } static const struct i2c_device_id gl518_id[] = { - { "gl518sm" }, + { .name = "gl518sm" }, { } }; MODULE_DEVICE_TABLE(i2c, gl518_id); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 972f4f8caa2b..f4fe39912ee2 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -885,7 +885,7 @@ static int gl520_probe(struct i2c_client *client) } static const struct i2c_device_id gl520_id[] = { - { "gl520sm" }, + { .name = "gl520sm" }, { } }; MODULE_DEVICE_TABLE(i2c, gl520_id); diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 80de5f20781e..ed212615b2fe 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -40,12 +40,11 @@ enum FAN_PWM_ENABLE { AUTOMATIC = 2, }; -static struct { +struct gpd_fan_data { enum FAN_PWM_ENABLE pwm_enable; u8 pwm_value; - const struct gpd_fan_drvdata *drvdata; -} gpd_driver_priv; +}; struct gpd_fan_drvdata { const char *board_name; // Board name for module param comparison @@ -249,10 +248,10 @@ static const struct gpd_fan_drvdata *gpd_module_drvdata[] = { }; // Helper functions to handle EC read/write -static void gpd_ecram_read(u16 offset, u8 *val) +static void gpd_ecram_read(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 *val) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -270,10 +269,10 @@ static void gpd_ecram_read(u16 offset, u8 *val) *val = inb(data_port); } -static void gpd_ecram_write(u16 offset, u8 value) +static void gpd_ecram_write(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 value) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -291,198 +290,202 @@ static void gpd_ecram_write(u16 offset, u8 value) outb(value, data_port); } -static int gpd_generic_read_rpm(void) +static int gpd_generic_read_rpm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 high, low; - gpd_ecram_read(drvdata->rpm_read, &high); - gpd_ecram_read(drvdata->rpm_read + 1, &low); + gpd_ecram_read(drvdata, drvdata->rpm_read, &high); + gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low); return (u16)high << 8 | low; } -static int gpd_wm2_read_rpm(void) +static int gpd_wm2_read_rpm(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; + for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) { u8 PWMCTR; - gpd_ecram_read(pwm_ctr_offset, &PWMCTR); + gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR); if (PWMCTR != 0xB8) - gpd_ecram_write(pwm_ctr_offset, 0xB8); + gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8); } - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); } // Read value for fan1_input -static int gpd_read_rpm(void) +static int gpd_read_rpm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win4_6800u: case win_mini: case duo: case mpc2: - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); case win_max_2: - return gpd_wm2_read_rpm(); + return gpd_wm2_read_rpm(data); } return 0; } -static int gpd_wm2_read_pwm(void) +static int gpd_wm2_read_pwm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 var; - gpd_ecram_read(drvdata->pwm_write, &var); + gpd_ecram_read(drvdata, drvdata->pwm_write, &var); + + // EC PWM register valid range is 1 ~ pwm_max; 0 is an invalid state + if (unlikely(!var)) + return -EIO; // Match gpd_generic_write_pwm(u8) below return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); } // Read value for pwm1 -static int gpd_read_pwm(void) +static int gpd_read_pwm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case duo: case win4_6800u: case mpc2: - switch (gpd_driver_priv.pwm_enable) { + switch (data->pwm_enable) { case DISABLE: return 255; case MANUAL: - return gpd_driver_priv.pwm_value; + return data->pwm_value; case AUTOMATIC: return -EOPNOTSUPP; } break; case win_max_2: - return gpd_wm2_read_pwm(); + return gpd_wm2_read_pwm(data); } return 0; } // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it. -static inline u8 gpd_cast_pwm_range(u8 val) +static inline u8 gpd_cast_pwm_range(const struct gpd_fan_drvdata *drvdata, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; - return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1; } -static void gpd_generic_write_pwm(u8 val) +static void gpd_generic_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); } -static void gpd_duo_write_pwm(u8 val) +static void gpd_duo_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); - gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); + gpd_ecram_write(drvdata, drvdata->pwm_write + 1, pwm_reg); } // Write value for pwm1 -static int gpd_write_pwm(u8 val) +static int gpd_write_pwm(struct gpd_fan_data *data, u8 val) { - if (gpd_driver_priv.pwm_enable != MANUAL) + if (data->pwm_enable != MANUAL) return -EPERM; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case duo: - gpd_duo_write_pwm(val); + gpd_duo_write_pwm(data, val); break; case win_mini: case win4_6800u: case win_max_2: case mpc2: - gpd_generic_write_pwm(val); + gpd_generic_write_pwm(data, val); break; } return 0; } -static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_win_mini_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_generic_write_pwm(255); + gpd_generic_write_pwm(data, 255); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); + gpd_generic_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_duo_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_duo_write_pwm(255); + gpd_duo_write_pwm(data, 255); break; case MANUAL: - gpd_duo_write_pwm(gpd_driver_priv.pwm_value); + gpd_duo_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_wm2_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; switch (enable) { case DISABLE: - gpd_generic_write_pwm(255); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, 255); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, data->pwm_value); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case AUTOMATIC: - gpd_ecram_write(drvdata->manual_control_enable, 0); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0); break; } } // Write value for pwm1_enable -static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { if (enable == MANUAL) // Set pwm_value to max firstly when switching to manual mode, in // consideration of device safety. - gpd_driver_priv.pwm_value = 255; + data->pwm_value = 255; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case win4_6800u: case mpc2: - gpd_win_mini_set_pwm_enable(enable); + gpd_win_mini_set_pwm_enable(data, enable); break; case duo: - gpd_duo_set_pwm_enable(enable); + gpd_duo_set_pwm_enable(data, enable); break; case win_max_2: - gpd_wm2_set_pwm_enable(enable); + gpd_wm2_set_pwm_enable(data, enable); break; } } @@ -505,15 +508,16 @@ static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata, return 0; } -static int gpd_fan_hwmon_read(__always_unused struct device *dev, +static int gpd_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long *val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); int ret; if (type == hwmon_fan) { if (attr == hwmon_fan_input) { - ret = gpd_read_rpm(); + ret = gpd_read_rpm(data); if (ret < 0) return ret; @@ -524,10 +528,10 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, } else if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: - *val = gpd_driver_priv.pwm_enable; + *val = data->pwm_enable; return 0; case hwmon_pwm_input: - ret = gpd_read_pwm(); + ret = gpd_read_pwm(data); if (ret < 0) return ret; @@ -540,27 +544,29 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, return -EOPNOTSUPP; } -static int gpd_fan_hwmon_write(__always_unused struct device *dev, +static int gpd_fan_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); + if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: if (!in_range(val, 0, 3)) return -EINVAL; - gpd_driver_priv.pwm_enable = val; + data->pwm_enable = val; - gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); + gpd_set_pwm_enable(data, data->pwm_enable); return 0; case hwmon_pwm_input: if (!in_range(val, 0, 256)) return -EINVAL; - gpd_driver_priv.pwm_value = val; + data->pwm_value = val; - return gpd_write_pwm(val); + return gpd_write_pwm(data, val); } } @@ -584,26 +590,37 @@ static struct hwmon_chip_info gpd_fan_chip_info = { .info = gpd_fan_hwmon_channel_info }; -static void gpd_win4_init_ec(void) +static void gpd_win4_init_ec(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 chip_id, chip_ver; - gpd_ecram_read(0x2000, &chip_id); + gpd_ecram_read(drvdata, 0x2000, &chip_id); if (chip_id == 0x55) { - gpd_ecram_read(0x1060, &chip_ver); - gpd_ecram_write(0x1060, chip_ver | 0x80); + gpd_ecram_read(drvdata, 0x1060, &chip_ver); + gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80); } } -static void gpd_init_ec(void) +static void gpd_init_ec(struct gpd_fan_data *data) { // The buggy firmware won't initialize EC properly on boot. // Before its initialization, reading RPM will always return 0, // and writing PWM will have no effect. // Initialize it manually on driver load. - if (gpd_driver_priv.drvdata->board == win4_6800u) - gpd_win4_init_ec(); + if (data->drvdata->board == win4_6800u) + gpd_win4_init_ec(data); +} + +static void gpd_fan_reset_hardware(void *pdata) +{ + struct gpd_fan_data *data = pdata; + + if (data) { + data->pwm_enable = AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } } static int gpd_fan_probe(struct platform_device *pdev) @@ -611,7 +628,10 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct resource *region; const struct resource *res; - const struct device *hwdev; + struct device *hwdev; + struct gpd_fan_data *data; + const struct gpd_fan_drvdata *match; + int ret; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -624,29 +644,39 @@ static int gpd_fan_probe(struct platform_device *pdev) return dev_err_probe(dev, -EBUSY, "Failed to request region\n"); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + match = dev_get_platdata(dev); + if (!match) + return -EINVAL; + + data->drvdata = match; + data->pwm_enable = AUTOMATIC; + data->pwm_value = 255; + + dev_set_drvdata(dev, data); + + gpd_init_ec(data); + + ret = devm_add_action_or_reset(dev, gpd_fan_reset_hardware, data); + if (ret) + return ret; + hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, - NULL, + data, &gpd_fan_chip_info, NULL); if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); - - gpd_init_ec(); - return 0; } -static void gpd_fan_remove(__always_unused struct platform_device *pdev) -{ - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_set_pwm_enable(AUTOMATIC); -} - static struct platform_driver gpd_fan_driver = { .probe = gpd_fan_probe, - .remove = gpd_fan_remove, .driver = { .name = KBUILD_MODNAME, }, @@ -668,6 +698,7 @@ static int __init gpd_fan_init(void) if (!match) { const struct dmi_system_id *dmi_match = dmi_first_match(dmi_table); + if (dmi_match) match = dmi_match->driver_data; } @@ -675,10 +706,6 @@ static int __init gpd_fan_init(void) if (!match) return -ENODEV; - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_driver_priv.pwm_value = 255; - gpd_driver_priv.drvdata = match; - struct resource gpd_fan_resources[] = { { .start = match->addr_port, @@ -690,10 +717,11 @@ static int __init gpd_fan_init(void) gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver, gpd_fan_probe, gpd_fan_resources, - 1, NULL, 0); + 1, + match, sizeof(*match)); if (IS_ERR(gpd_fan_platform_device)) { - pr_warn("Failed to create platform device\n"); + pr_err("Failed to create platform device\n"); return PTR_ERR(gpd_fan_platform_device); } diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index a8892ced1e54..084828e1e281 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -592,8 +592,10 @@ static int gpio_fan_probe(struct platform_device *pdev) } /* Optional cooling device register for Device tree platforms */ - fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np, - "gpio-fan", fan_data, &gpio_fan_cool_ops); + fan_data->cdev = devm_thermal_of_child_cooling_device_register(dev, np, + "gpio-fan", + fan_data, + &gpio_fan_cool_ops); dev_info(dev, "GPIO fan initialized\n"); diff --git a/drivers/hwmon/hih6130.c b/drivers/hwmon/hih6130.c index 85af8299150a..7984be1e706d 100644 --- a/drivers/hwmon/hih6130.c +++ b/drivers/hwmon/hih6130.c @@ -233,7 +233,7 @@ static int hih6130_probe(struct i2c_client *client) /* Device ID table */ static const struct i2c_device_id hih6130_id[] = { - { "hih6130" }, + { .name = "hih6130" }, { } }; MODULE_DEVICE_TABLE(i2c, hih6130_id); diff --git a/drivers/hwmon/hs3001.c b/drivers/hwmon/hs3001.c index 50c6c15f8b18..b263cbeca074 100644 --- a/drivers/hwmon/hs3001.c +++ b/drivers/hwmon/hs3001.c @@ -169,8 +169,8 @@ static const struct hwmon_chip_info hs3001_chip_info = { /* device ID table */ static const struct i2c_device_id hs3001_ids[] = { - { "hs3001" }, - { }, + { .name = "hs3001" }, + { } }; MODULE_DEVICE_TABLE(i2c, hs3001_ids); diff --git a/drivers/hwmon/htu31.c b/drivers/hwmon/htu31.c index 7521a371aa6c..5d2cdbdb773b 100644 --- a/drivers/hwmon/htu31.c +++ b/drivers/hwmon/htu31.c @@ -322,7 +322,7 @@ static int htu31_probe(struct i2c_client *client) } static const struct i2c_device_id htu31_id[] = { - { "htu31" }, + { .name = "htu31" }, { } }; MODULE_DEVICE_TABLE(i2c, htu31_id); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 6812d1fd7c28..55a9a3ddd4aa 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -573,6 +573,7 @@ static const char * const hwmon_chip_attrs[] = { [hwmon_chip_curr_reset_history] = "curr_reset_history", [hwmon_chip_power_reset_history] = "power_reset_history", [hwmon_chip_update_interval] = "update_interval", + [hwmon_chip_update_interval_us] = "update_interval_us", [hwmon_chip_alarms] = "alarms", [hwmon_chip_samples] = "samples", [hwmon_chip_curr_samples] = "curr_samples", @@ -1082,6 +1083,7 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); * @dev: the parent device * @name: hwmon name attribute * @drvdata: driver data to attach to created device + * @extra_groups: pointer to list of additional non-standard attribute groups * * The use of this function is restricted. It is provided for legacy reasons * and must only be called from the thermal subsystem. @@ -1093,12 +1095,13 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); */ struct device * hwmon_device_register_for_thermal(struct device *dev, const char *name, - void *drvdata) + void *drvdata, + const struct attribute_group **extra_groups) { if (!name || !dev) return ERR_PTR(-EINVAL); - return __hwmon_device_register(dev, name, drvdata, NULL, NULL); + return __hwmon_device_register(dev, name, drvdata, NULL, extra_groups); } EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, "HWMON_THERMAL"); diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c index a116f1600e81..39aca2cdf27f 100644 --- a/drivers/hwmon/ina209.c +++ b/drivers/hwmon/ina209.c @@ -569,7 +569,7 @@ static void ina209_remove(struct i2c_client *client) } static const struct i2c_device_id ina209_id[] = { - { "ina209" }, + { .name = "ina209" }, { } }; MODULE_DEVICE_TABLE(i2c, ina209_id); diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index ff67b03189f7..3632bbca607e 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/regmap.h> +#include <linux/util_macros.h> /* INA238 register definitions */ #define INA238_CONFIG 0x0 @@ -49,6 +50,17 @@ #define INA238_DIAG_ALERT_BUSUL BIT(3) #define INA238_DIAG_ALERT_POL BIT(2) +/* INA238_ADC_CONFIG register field masks and shifts */ +#define INA238_ADC_CONFIG_MODE_MASK GENMASK(15, 12) +#define INA238_ADC_CONFIG_VBUSCT_SHIFT 9 +#define INA238_ADC_CONFIG_VBUSCT_MASK GENMASK(11, 9) +#define INA238_ADC_CONFIG_VSHCT_SHIFT 6 +#define INA238_ADC_CONFIG_VSHCT_MASK GENMASK(8, 6) +#define INA238_ADC_CONFIG_VTCT_SHIFT 3 +#define INA238_ADC_CONFIG_VTCT_MASK GENMASK(5, 3) +#define INA238_ADC_CONFIG_AVG_SHIFT 0 +#define INA238_ADC_CONFIG_AVG_MASK GENMASK(2, 0) + #define INA238_REGISTERS 0x20 #define INA238_RSHUNT_DEFAULT 2500 /* uOhm */ @@ -101,6 +113,21 @@ static const struct regmap_config ina238_regmap_config = { .val_bits = 16, }; +/* Lookup table for conversion times in usec for INA238 family */ +static const u16 ina238_conv_time[] = { + 50, 84, 150, 280, 540, 1052, 2074, 4120, +}; + +/* Lookup table for conversion times in usec for SQ52206 */ +static const u16 sq52206_conv_time[] = { + 66, 118, 310, 566, 1070, 2090, 4140, 8230, +}; + +/* Lookup table for number of samples used in averaging mode */ +static const int ina238_avg_samples[] = { + 1, 4, 16, 64, 128, 256, 512, 1024, +}; + enum ina238_ids { ina228, ina237, ina238, ina700, ina780, sq52206 }; struct ina238_config { @@ -112,6 +139,7 @@ struct ina238_config { u32 power_calculate_factor; /* fixed parameter for power calculation, from datasheet */ u32 bus_voltage_lsb; /* bus voltage LSB, in nV */ int current_lsb; /* current LSB, in uA */ + const u16 *conv_time; /* conversion time lookup table */ }; struct ina238_data { @@ -124,6 +152,7 @@ struct ina238_data { int current_lsb; /* current LSB, in uA */ int power_lsb; /* power LSB, in uW */ int energy_lsb; /* energy LSB, in uJ */ + u16 adc_config; /* cached ADC_CONFIG register value */ }; static const struct ina238_config ina238_config[] = { @@ -135,6 +164,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 16, + .conv_time = ina238_conv_time, }, [ina237] = { .has_20bit_voltage_current = false, @@ -144,6 +174,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, + .conv_time = ina238_conv_time, }, [ina238] = { .has_20bit_voltage_current = false, @@ -153,6 +184,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, + .conv_time = ina238_conv_time, }, [ina700] = { .has_20bit_voltage_current = false, @@ -163,6 +195,7 @@ static const struct ina238_config ina238_config[] = { .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, .current_lsb = 480, + .conv_time = ina238_conv_time, }, [ina780] = { .has_20bit_voltage_current = false, @@ -173,6 +206,7 @@ static const struct ina238_config ina238_config[] = { .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, .current_lsb = 2400, + .conv_time = ina238_conv_time, }, [sq52206] = { .has_20bit_voltage_current = false, @@ -182,6 +216,7 @@ static const struct ina238_config ina238_config[] = { .config_default = SQ52206_CONFIG_DEFAULT, .bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB, .temp_resolution = 16, + .conv_time = sq52206_conv_time, }, }; @@ -261,6 +296,111 @@ static int ina228_read_voltage(struct ina238_data *data, int channel, long *val) return 0; } +/* Converting ADC_CONFIG register value to update_interval in usec */ +static inline u32 ina238_reg_to_interval_us(struct ina238_data *data) +{ + const u16 *ct = data->config->conv_time; + u32 vbusct = ct[(data->adc_config & INA238_ADC_CONFIG_VBUSCT_MASK) >> + INA238_ADC_CONFIG_VBUSCT_SHIFT]; + u32 vshct = ct[(data->adc_config & INA238_ADC_CONFIG_VSHCT_MASK) >> + INA238_ADC_CONFIG_VSHCT_SHIFT]; + u32 vtct = ct[(data->adc_config & INA238_ADC_CONFIG_VTCT_MASK) >> + INA238_ADC_CONFIG_VTCT_SHIFT]; + + return vbusct + vshct + vtct; +} + +static inline u32 ina238_samples(struct ina238_data *data) +{ + return ina238_avg_samples[(data->adc_config & INA238_ADC_CONFIG_AVG_MASK) >> + INA238_ADC_CONFIG_AVG_SHIFT]; +} + +/* Converting update_interval(_us) to a per-field conversion time in usec. + * interval_us is the total ADC cycle time including averaging in microseconds. + * All three conversion fields (VBUSCT, VSHCT, VTCT) are set equal, so the + * per-field time is interval_us / (samples * 3). + */ +static inline u32 ina238_interval_us_to_conv_time(u32 interval_us, u32 samples) +{ + return DIV_ROUND_CLOSEST_ULL(interval_us, samples * 3); +} + +/* Write a per-field conversion time (in usec) to the ADC_CONFIG register */ +static int ina238_write_conv_time(struct ina238_data *data, u32 conv_time_us) +{ + u16 adc_config; + int idx, ret; + + idx = find_closest(conv_time_us, data->config->conv_time, + ARRAY_SIZE(ina238_conv_time)); + adc_config = (data->adc_config & + ~(INA238_ADC_CONFIG_VBUSCT_MASK | + INA238_ADC_CONFIG_VSHCT_MASK | + INA238_ADC_CONFIG_VTCT_MASK)) | + ((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; +} + +static int ina238_read_chip(struct device *dev, u32 attr, long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_samples: + *val = ina238_samples(data); + return 0; + case hwmon_chip_update_interval: + /* Return in msec */ + *val = DIV_ROUND_CLOSEST(ina238_reg_to_interval_us(data) * + ina238_samples(data), 1000); + return 0; + case hwmon_chip_update_interval_us: + /* Return in usec */ + *val = ina238_reg_to_interval_us(data) * ina238_samples(data); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int ina238_write_chip(struct device *dev, u32 attr, long val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + u16 adc_config; + int idx, ret; + + switch (attr) { + case hwmon_chip_samples: + idx = find_closest(val, ina238_avg_samples, + ARRAY_SIZE(ina238_avg_samples)); + adc_config = (data->adc_config & ~INA238_ADC_CONFIG_AVG_MASK) | + (idx << INA238_ADC_CONFIG_AVG_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; + case hwmon_chip_update_interval: + /* Convert ms to us before passing to the shared helper */ + val = clamp_val(val, 0, INT_MAX / 1000) * 1000; + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); + case hwmon_chip_update_interval_us: + val = clamp_val(val, 0, INT_MAX); + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); + default: + return -EOPNOTSUPP; + } +} + static int ina238_read_in(struct device *dev, u32 attr, int channel, long *val) { @@ -587,6 +727,8 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { switch (type) { + case hwmon_chip: + return ina238_read_chip(dev, attr, val); case hwmon_in: return ina238_read_in(dev, attr, channel, val); case hwmon_curr: @@ -607,6 +749,8 @@ static int ina238_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { switch (type) { + case hwmon_chip: + return ina238_write_chip(dev, attr, val); case hwmon_in: return ina238_write_in(dev, attr, channel, val); case hwmon_curr: @@ -629,6 +773,15 @@ static umode_t ina238_is_visible(const void *drvdata, bool has_energy = data->config->has_energy; switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_samples: + case hwmon_chip_update_interval: + case hwmon_chip_update_interval_us: + return 0644; + default: + return 0; + } case hwmon_in: switch (attr) { case hwmon_in_input: @@ -692,6 +845,9 @@ static umode_t ina238_is_visible(const void *drvdata, HWMON_I_MIN | HWMON_I_MIN_ALARM) static const struct hwmon_channel_info * const ina238_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL | + HWMON_C_UPDATE_INTERVAL_US), HWMON_CHANNEL_INFO(in, /* 0: shunt voltage */ INA238_HWMON_IN_CONFIG, @@ -798,8 +954,8 @@ static int ina238_probe(struct i2c_client *client) } /* Setup ADC_CONFIG register */ - ret = regmap_write(data->regmap, INA238_ADC_CONFIG, - INA238_ADC_CONFIG_DEFAULT); + data->adc_config = INA238_ADC_CONFIG_DEFAULT; + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, data->adc_config); if (ret < 0) { dev_err(dev, "error configuring the device: %d\n", ret); return -ENODEV; @@ -837,12 +993,12 @@ static int ina238_probe(struct i2c_client *client) } static const struct i2c_device_id ina238_id[] = { - { "ina228", ina228 }, - { "ina237", ina237 }, - { "ina238", ina238 }, - { "ina700", ina700 }, - { "ina780", ina780 }, - { "sq52206", sq52206 }, + { .name = "ina228", .driver_data = ina228 }, + { .name = "ina237", .driver_data = ina237 }, + { .name = "ina238", .driver_data = ina238 }, + { .name = "ina700", .driver_data = ina700 }, + { .name = "ina780", .driver_data = ina780 }, + { .name = "sq52206", .driver_data = sq52206 }, { } }; MODULE_DEVICE_TABLE(i2c, ina238_id); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 613ffb622b7c..c4742e84b999 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -875,11 +875,11 @@ static ssize_t shunt_resistor_store(struct device *dev, if (status < 0) return status; - hwmon_lock(dev); - status = ina2xx_set_shunt(data, val); - hwmon_unlock(dev); - if (status < 0) - return status; + scoped_guard(hwmon_lock, dev) { + status = ina2xx_set_shunt(data, val); + if (status < 0) + return status; + } return count; } @@ -1000,14 +1000,14 @@ static int ina2xx_probe(struct i2c_client *client) } static const struct i2c_device_id ina2xx_id[] = { - { "ina219", ina219 }, - { "ina220", ina219 }, - { "ina226", ina226 }, - { "ina230", ina226 }, - { "ina231", ina226 }, - { "ina234", ina234 }, - { "ina260", ina260 }, - { "sy24655", sy24655 }, + { .name = "ina219", .driver_data = ina219 }, + { .name = "ina220", .driver_data = ina219 }, + { .name = "ina226", .driver_data = ina226 }, + { .name = "ina230", .driver_data = ina226 }, + { .name = "ina231", .driver_data = ina226 }, + { .name = "ina234", .driver_data = ina234 }, + { .name = "ina260", .driver_data = ina260 }, + { .name = "sy24655", .driver_data = sy24655 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 5ecc68dcf169..3ab5de3a1110 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -1002,7 +1002,7 @@ static const struct of_device_id ina3221_of_match_table[] = { MODULE_DEVICE_TABLE(of, ina3221_of_match_table); static const struct i2c_device_id ina3221_ids[] = { - { "ina3221" }, + { .name = "ina3221" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ina3221_ids); diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index aa01a4bedc21..e85d42a45113 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -773,6 +773,7 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids); static struct platform_driver intel_m10bmc_hwmon_driver = { .probe = m10bmc_hwmon_probe, @@ -783,7 +784,6 @@ static struct platform_driver intel_m10bmc_hwmon_driver = { }; module_platform_driver(intel_m10bmc_hwmon_driver); -MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c index 96fcfbfff48f..0c60d2ef28f5 100644 --- a/drivers/hwmon/isl28022.c +++ b/drivers/hwmon/isl28022.c @@ -475,7 +475,7 @@ static int isl28022_probe(struct i2c_client *client) } static const struct i2c_device_id isl28022_ids[] = { - { "isl28022" }, + { .name = "isl28022" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, isl28022_ids); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5fd310662ee4..87edb1b6048b 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1412,6 +1412,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; + if (val < 0) + val = 0; + err = it87_lock(data); if (err) return err; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 6549dc543781..77fece680358 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -579,7 +579,7 @@ static const struct dev_pm_ops jc42_dev_pm_ops = { #endif /* CONFIG_PM */ static const struct i2c_device_id jc42_id[] = { - { "jc42" }, + { .name = "jc42" }, { } }; MODULE_DEVICE_TABLE(i2c, jc42_id); diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index a5d8f45b7881..de0760dc597d 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -20,7 +20,9 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/pci_ids.h> + #include <asm/amd/node.h> +#include <asm/cpuid/api.h> #include <asm/processor.h> MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor"); diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 2b80ac410cd1..53241164570e 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <asm/processor.h> +#include <asm/cpuid/api.h> #define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) #define REG_TEMP 0xe4 diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c index 64a335a64a2e..2553e49f8401 100644 --- a/drivers/hwmon/lineage-pem.c +++ b/drivers/hwmon/lineage-pem.c @@ -502,8 +502,8 @@ static int pem_probe(struct i2c_client *client) } static const struct i2c_device_id pem_id[] = { - {"lineage_pem"}, - {} + { .name = "lineage_pem" }, + { } }; MODULE_DEVICE_TABLE(i2c, pem_id); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 30500b4d2221..e2a429e579ac 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -92,6 +92,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define LM96163_REG_REMOTE_TEMP_U_LSB 0x32 #define LM96163_REG_CONFIG_ENHANCED 0x45 +#define LM63_PWM_BASE_FAST_HZ 180000 +#define LM63_PWM_BASE_SLOW_HZ 700 + #define LM63_MAX_CONVRATE 9 #define LM63_MAX_CONVRATE_HZ 32 @@ -455,6 +458,91 @@ static ssize_t pwm1_enable_store(struct device *dev, return count; } +static ssize_t pwm1_freq_show(struct device *dev, + struct device_attribute *dummy, char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + unsigned int base, freq; + + mutex_lock(&data->update_lock); + base = (data->config_fan & 0x08) ? + LM63_PWM_BASE_SLOW_HZ : LM63_PWM_BASE_FAST_HZ; + freq = data->pwm1_freq; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", base / freq); +} + +/* + * Pick the closest CONFIG_FAN.SCS + PFR for the requested frequency. + * PWM_FREQ writes need the LUT unlocked, same as set_pwm1(). LUT PWM + * bytes are register-relative; rewrite them after a frequency change + * if duty cycles must be preserved. + */ +static ssize_t pwm1_freq_store(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr; + bool slow_clock; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val == 0) + return -EINVAL; + + pfr_fast = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_FAST_HZ, val), + 1UL, 31UL); + pfr_slow = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_SLOW_HZ, val), + 1UL, 31UL); + err_fast = abs_diff(LM63_PWM_BASE_FAST_HZ / pfr_fast, val); + err_slow = abs_diff(LM63_PWM_BASE_SLOW_HZ / pfr_slow, val); + + if (err_slow < err_fast) { + slow_clock = true; + pfr = pfr_slow; + } else { + slow_clock = false; + pfr = pfr_fast; + } + + mutex_lock(&data->update_lock); + ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + data->config_fan = ret; + + if (!(data->config_fan & 0x20)) { /* register is read-only */ + mutex_unlock(&data->update_lock); + return -EPERM; + } + + if (data->kind == lm96163) { + ret = i2c_smbus_read_byte_data(client, LM96163_REG_CONFIG_ENHANCED); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + data->pwm_highres = !slow_clock && pfr == 8 && (ret & 0x10); + } + + if (slow_clock) + data->config_fan |= 0x08; + else + data->config_fan &= ~0x08; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan); + i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr); + data->pwm1_freq = pfr; + mutex_unlock(&data->update_lock); + return count; +} + /* * There are 8bit registers for both local(temp1) and remote(temp2) sensor. * For remote sensor registers temp2_offset has to be considered, @@ -630,6 +718,47 @@ static ssize_t show_lut_temp_hyst(struct device *dev, } /* + * The LM63 has a single hysteresis register shared by all LUT entries. + * Expose it as a chip-wide hysteresis amount in millidegrees; the + * per-point pwm1_auto_pointN_temp_hyst attributes remain read-only and + * show the resulting absolute trip-down temperature for each entry. + */ +static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev, + struct device_attribute *dummy, + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + u8 hyst; + + mutex_lock(&data->update_lock); + hyst = data->lut_temp_hyst; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", TEMP8_FROM_REG(hyst)); +} + +static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->lut_temp_hyst = HYST_TO_REG(val); + i2c_smbus_write_byte_data(client, LM63_REG_LUT_TEMP_HYST, + data->lut_temp_hyst); + mutex_unlock(&data->update_lock); + return count; +} + +/* * And now the other way around, user-space provides an absolute * hysteresis value and we have to store a relative one */ @@ -764,6 +893,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); static DEVICE_ATTR_RW(pwm1_enable); +static DEVICE_ATTR_RW(pwm1_freq); +static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 1); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, @@ -869,6 +1000,8 @@ static DEVICE_ATTR_RW(update_interval); static struct attribute *lm63_attributes[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1_freq.attr, + &dev_attr_pwm1_auto_point_temp_hyst.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, @@ -1152,9 +1285,9 @@ static int lm63_probe(struct i2c_client *client) */ static const struct i2c_device_id lm63_id[] = { - { "lm63", lm63 }, - { "lm64", lm64 }, - { "lm96163", lm96163 }, + { .name = "lm63", .driver_data = lm63 }, + { .name = "lm64", .driver_data = lm64 }, + { .name = "lm96163", .driver_data = lm96163 }, { } }; MODULE_DEVICE_TABLE(i2c, lm63_id); diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 581b01572e1b..63ee67481a16 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -220,7 +220,7 @@ lm73_probe(struct i2c_client *client) } static const struct i2c_device_id lm73_ids[] = { - { "lm73" }, + { .name = "lm73" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm73_ids); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index c283443e363b..104149a03bad 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -14,7 +14,8 @@ #include <linux/i3c/device.h> #include <linux/hwmon.h> #include <linux/err.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/util_macros.h> #include <linux/regulator/consumer.h> @@ -123,7 +124,9 @@ struct lm75_data { static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 }; -#define LM75_SAMPLE_CLEAR_MASK (3 << 5) +#define LM75_ALERT_POLARITY_HIGH_8_BIT (BIT(2)) +#define LM75_ALERT_POLARITY_HIGH_16_BIT (BIT(2) << 8) +#define LM75_SAMPLE_CLEAR_MASK (3 << 5) /* The structure below stores the configuration values of the supported devices. * In case of being supported multiple configurations, the default one must @@ -539,6 +542,8 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, if (config_data->params->num_sample_times > 1) return 0644; return 0444; + default: + break; } break; case hwmon_temp: @@ -555,6 +560,8 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, if (config_data->params->alarm) return 0444; break; + default: + break; } break; default: @@ -725,6 +732,7 @@ static void lm75_remove(void *data) static int lm75_generic_probe(struct device *dev, const char *name, enum lm75_type kind, int irq, struct regmap *regmap) { + u16 clr_mask, pol_mask, set_mask; struct device *hwmon_dev; struct lm75_data *data; int status, err; @@ -762,8 +770,18 @@ static int lm75_generic_probe(struct device *dev, const char *name, return err; data->orig_conf = status; - err = lm75_write_config(data, data->params->set_mask, - data->params->clr_mask); + /* Enforce polarity active-low (default) or active-high (devicetree) */ + if (!data->params->config_reg_16bits) + pol_mask = LM75_ALERT_POLARITY_HIGH_8_BIT; + else + pol_mask = LM75_ALERT_POLARITY_HIGH_16_BIT; + + clr_mask = data->params->clr_mask | pol_mask; + set_mask = data->params->set_mask & ~pol_mask; + if (device_property_read_bool(dev, "ti,alert-polarity-active-high")) + set_mask |= pol_mask; + + err = lm75_write_config(data, set_mask, clr_mask); if (err) return err; @@ -816,37 +834,37 @@ static int lm75_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id lm75_i2c_ids[] = { - { "adt75", adt75, }, - { "as6200", as6200, }, - { "at30ts74", at30ts74, }, - { "ds1775", ds1775, }, - { "ds75", ds75, }, - { "ds7505", ds7505, }, - { "g751", g751, }, - { "lm75", lm75, }, - { "lm75a", lm75a, }, - { "lm75b", lm75b, }, - { "max6625", max6625, }, - { "max6626", max6626, }, - { "max31725", max31725, }, - { "max31726", max31725, }, - { "mcp980x", mcp980x, }, - { "p3t1750", p3t1750, }, - { "p3t1755", p3t1755, }, - { "pct2075", pct2075, }, - { "stds75", stds75, }, - { "stlm75", stlm75, }, - { "tcn75", tcn75, }, - { "tmp100", tmp100, }, - { "tmp101", tmp101, }, - { "tmp105", tmp105, }, - { "tmp112", tmp112, }, - { "tmp175", tmp175, }, - { "tmp275", tmp275, }, - { "tmp75", tmp75, }, - { "tmp75b", tmp75b, }, - { "tmp75c", tmp75c, }, - { "tmp1075", tmp1075, }, + { .name = "adt75", .driver_data = adt75 }, + { .name = "as6200", .driver_data = as6200 }, + { .name = "at30ts74", .driver_data = at30ts74 }, + { .name = "ds1775", .driver_data = ds1775 }, + { .name = "ds75", .driver_data = ds75 }, + { .name = "ds7505", .driver_data = ds7505 }, + { .name = "g751", .driver_data = g751 }, + { .name = "lm75", .driver_data = lm75 }, + { .name = "lm75a", .driver_data = lm75a }, + { .name = "lm75b", .driver_data = lm75b }, + { .name = "max6625", .driver_data = max6625 }, + { .name = "max6626", .driver_data = max6626 }, + { .name = "max31725", .driver_data = max31725 }, + { .name = "max31726", .driver_data = max31725 }, + { .name = "mcp980x", .driver_data = mcp980x }, + { .name = "p3t1750", .driver_data = p3t1750 }, + { .name = "p3t1755", .driver_data = p3t1755 }, + { .name = "pct2075", .driver_data = pct2075 }, + { .name = "stds75", .driver_data = stds75 }, + { .name = "stlm75", .driver_data = stlm75 }, + { .name = "tcn75", .driver_data = tcn75 }, + { .name = "tmp100", .driver_data = tmp100 }, + { .name = "tmp101", .driver_data = tmp101 }, + { .name = "tmp105", .driver_data = tmp105 }, + { .name = "tmp112", .driver_data = tmp112 }, + { .name = "tmp175", .driver_data = tmp175 }, + { .name = "tmp275", .driver_data = tmp275 }, + { .name = "tmp75", .driver_data = tmp75 }, + { .name = "tmp75b", .driver_data = tmp75b }, + { .name = "tmp75c", .driver_data = tmp75c }, + { .name = "tmp1075", .driver_data = tmp1075 }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm75_i2c_ids); @@ -882,7 +900,7 @@ static int lm75_i3c_probe(struct i3c_device *i3cdev) return lm75_generic_probe(dev, id_data->name, id_data->type, 0, regmap); } -static const struct of_device_id __maybe_unused lm75_of_match[] = { +static const struct of_device_id lm75_of_match[] = { { .compatible = "adi,adt75", .data = (void *)adt75 @@ -1135,7 +1153,7 @@ static struct i2c_driver lm75_i2c_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm75", - .of_match_table = of_match_ptr(lm75_of_match), + .of_match_table = lm75_of_match, .pm = LM75_DEV_PM_OPS, }, .probe = lm75_i2c_probe, diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index 80f7a6a3f9a2..96c5c2584d37 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -337,7 +337,7 @@ static int lm77_probe(struct i2c_client *client) } static const struct i2c_device_id lm77_id[] = { - { "lm77" }, + { .name = "lm77" }, { } }; MODULE_DEVICE_TABLE(i2c, lm77_id); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 9378a47bf5af..e4834f9eca6a 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -649,8 +649,8 @@ static int lm78_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id lm78_i2c_id[] = { - { "lm78", lm78 }, - { "lm79", lm79 }, + { .name = "lm78", .driver_data = lm78 }, + { .name = "lm79", .driver_data = lm79 }, { } }; MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 63c7831bd3e1..94f3dabaaa6f 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -622,8 +622,8 @@ static int lm80_probe(struct i2c_client *client) */ static const struct i2c_device_id lm80_id[] = { - { "lm80", 0 }, - { "lm96080", 1 }, + { .name = "lm80" }, + { .name = "lm96080" }, { } }; MODULE_DEVICE_TABLE(i2c, lm80_id); diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index f800fe2ef18b..8d49df8c9314 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -443,8 +443,8 @@ static int lm83_probe(struct i2c_client *client) */ static const struct i2c_device_id lm83_id[] = { - { "lm83", lm83 }, - { "lm82", lm82 }, + { .name = "lm83", .driver_data = lm83 }, + { .name = "lm82", .driver_data = lm82 }, { } }; MODULE_DEVICE_TABLE(i2c, lm83_id); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 1c244ed75122..c56e164d61c1 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1618,18 +1618,18 @@ static int lm85_probe(struct i2c_client *client) } static const struct i2c_device_id lm85_id[] = { - { "adm1027", adm1027 }, - { "adt7463", adt7463 }, - { "adt7468", adt7468 }, - { "lm85", lm85 }, - { "lm85b", lm85 }, - { "lm85c", lm85 }, - { "lm96000", lm96000 }, - { "emc6d100", emc6d100 }, - { "emc6d101", emc6d100 }, - { "emc6d102", emc6d102 }, - { "emc6d103", emc6d103 }, - { "emc6d103s", emc6d103s }, + { .name = "adm1027", .driver_data = adm1027 }, + { .name = "adt7463", .driver_data = adt7463 }, + { .name = "adt7468", .driver_data = adt7468 }, + { .name = "lm85", .driver_data = lm85 }, + { .name = "lm85b", .driver_data = lm85 }, + { .name = "lm85c", .driver_data = lm85 }, + { .name = "lm96000", .driver_data = lm96000 }, + { .name = "emc6d100", .driver_data = emc6d100 }, + { .name = "emc6d101", .driver_data = emc6d100 }, + { .name = "emc6d102", .driver_data = emc6d102 }, + { .name = "emc6d103", .driver_data = emc6d103 }, + { .name = "emc6d103s", .driver_data = emc6d103s }, { } }; MODULE_DEVICE_TABLE(i2c, lm85_id); diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 37bf2d1d3d09..c09074f44708 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -981,8 +981,8 @@ static int lm87_probe(struct i2c_client *client) */ static const struct i2c_device_id lm87_id[] = { - { "lm87" }, - { "adm1024" }, + { .name = "lm87" }, + { .name = "adm1024" }, { } }; MODULE_DEVICE_TABLE(i2c, lm87_id); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1eeb608e5903..4b9c0ccdf260 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -243,54 +243,54 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, */ static const struct i2c_device_id lm90_id[] = { - { "adm1020", max1617 }, - { "adm1021", max1617 }, - { "adm1023", adm1023 }, - { "adm1032", adm1032 }, - { "adt7421", adt7461a }, - { "adt7461", adt7461 }, - { "adt7461a", adt7461a }, - { "adt7481", adt7481 }, - { "adt7482", adt7481 }, - { "adt7483a", adt7481 }, - { "g781", g781 }, - { "gl523sm", max1617 }, - { "lm84", lm84 }, - { "lm86", lm90 }, - { "lm89", lm90 }, - { "lm90", lm90 }, - { "lm99", lm99 }, - { "max1617", max1617 }, - { "max6642", max6642 }, - { "max6646", max6646 }, - { "max6647", max6646 }, - { "max6648", max6648 }, - { "max6649", max6646 }, - { "max6654", max6654 }, - { "max6657", max6657 }, - { "max6658", max6657 }, - { "max6659", max6659 }, - { "max6680", max6680 }, - { "max6681", max6680 }, - { "max6690", max6654 }, - { "max6692", max6648 }, - { "max6695", max6696 }, - { "max6696", max6696 }, - { "mc1066", max1617 }, - { "nct1008", adt7461a }, - { "nct210", nct210 }, - { "nct214", nct72 }, - { "nct218", nct72 }, - { "nct72", nct72 }, - { "nct7716", nct7716 }, - { "nct7717", nct7717 }, - { "nct7718", nct7718 }, - { "ne1618", ne1618 }, - { "w83l771", w83l771 }, - { "sa56004", sa56004 }, - { "thmc10", max1617 }, - { "tmp451", tmp451 }, - { "tmp461", tmp461 }, + { .name = "adm1020", .driver_data = max1617 }, + { .name = "adm1021", .driver_data = max1617 }, + { .name = "adm1023", .driver_data = adm1023 }, + { .name = "adm1032", .driver_data = adm1032 }, + { .name = "adt7421", .driver_data = adt7461a }, + { .name = "adt7461", .driver_data = adt7461 }, + { .name = "adt7461a", .driver_data = adt7461a }, + { .name = "adt7481", .driver_data = adt7481 }, + { .name = "adt7482", .driver_data = adt7481 }, + { .name = "adt7483a", .driver_data = adt7481 }, + { .name = "g781", .driver_data = g781 }, + { .name = "gl523sm", .driver_data = max1617 }, + { .name = "lm84", .driver_data = lm84 }, + { .name = "lm86", .driver_data = lm90 }, + { .name = "lm89", .driver_data = lm90 }, + { .name = "lm90", .driver_data = lm90 }, + { .name = "lm99", .driver_data = lm99 }, + { .name = "max1617", .driver_data = max1617 }, + { .name = "max6642", .driver_data = max6642 }, + { .name = "max6646", .driver_data = max6646 }, + { .name = "max6647", .driver_data = max6646 }, + { .name = "max6648", .driver_data = max6648 }, + { .name = "max6649", .driver_data = max6646 }, + { .name = "max6654", .driver_data = max6654 }, + { .name = "max6657", .driver_data = max6657 }, + { .name = "max6658", .driver_data = max6657 }, + { .name = "max6659", .driver_data = max6659 }, + { .name = "max6680", .driver_data = max6680 }, + { .name = "max6681", .driver_data = max6680 }, + { .name = "max6690", .driver_data = max6654 }, + { .name = "max6692", .driver_data = max6648 }, + { .name = "max6695", .driver_data = max6696 }, + { .name = "max6696", .driver_data = max6696 }, + { .name = "mc1066", .driver_data = max1617 }, + { .name = "nct1008", .driver_data = adt7461a }, + { .name = "nct210", .driver_data = nct210 }, + { .name = "nct214", .driver_data = nct72 }, + { .name = "nct218", .driver_data = nct72 }, + { .name = "nct72", .driver_data = nct72 }, + { .name = "nct7716", .driver_data = nct7716 }, + { .name = "nct7717", .driver_data = nct7717 }, + { .name = "nct7718", .driver_data = nct7718 }, + { .name = "ne1618", .driver_data = ne1618 }, + { .name = "w83l771", .driver_data = w83l771 }, + { .name = "sa56004", .driver_data = sa56004 }, + { .name = "thmc10", .driver_data = max1617 }, + { .name = "tmp451", .driver_data = tmp451 }, + { .name = "tmp461", .driver_data = tmp461 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -1226,13 +1226,8 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) static int lm90_update_alarms(struct lm90_data *data, bool force) { - int err; - - hwmon_lock(data->hwmon_dev); - err = lm90_update_alarms_locked(data, force); - hwmon_unlock(data->hwmon_dev); - - return err; + guard(hwmon_lock)(data->hwmon_dev); + return lm90_update_alarms_locked(data, force); } static void lm90_alert_work(struct work_struct *__work) @@ -2598,9 +2593,9 @@ static void lm90_stop_work(void *_data) { struct lm90_data *data = _data; - hwmon_lock(data->hwmon_dev); - data->shutdown = true; - hwmon_unlock(data->hwmon_dev); + scoped_guard(hwmon_lock, data->hwmon_dev) { + data->shutdown = true; + } cancel_delayed_work_sync(&data->alert_work); cancel_work_sync(&data->report_work); } @@ -2946,17 +2941,17 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, */ struct lm90_data *data = i2c_get_clientdata(client); - hwmon_lock(data->hwmon_dev); - if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && - (data->current_alarms & data->alert_alarms)) { - if (!(data->config & 0x80)) { - dev_dbg(&client->dev, "Disabling ALERT#\n"); - lm90_update_confreg(data, data->config | 0x80); + scoped_guard(hwmon_lock, data->hwmon_dev) { + if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); + lm90_update_confreg(data, data->config | 0x80); + } + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } - schedule_delayed_work(&data->alert_work, - max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } - hwmon_unlock(data->hwmon_dev); } else { dev_dbg(&client->dev, "Everything OK\n"); } diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 91a6b7525bb6..4aadbabc27bb 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -405,10 +405,10 @@ static int lm92_probe(struct i2c_client *client) * Module and driver stuff */ -/* .driver_data is limit register resolution */ +/* .driver_data is limit register resolution */ static const struct i2c_device_id lm92_id[] = { - { "lm92", 13 }, - { "max6635", 9 }, + { .name = "lm92", .driver_data = 13 }, + { .name = "max6635", .driver_data = 9 }, { } }; MODULE_DEVICE_TABLE(i2c, lm92_id); diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index be4853fad80f..d58a3c2a8593 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -2624,8 +2624,8 @@ static int lm93_probe(struct i2c_client *client) } static const struct i2c_device_id lm93_id[] = { - { "lm93" }, - { "lm94" }, + { .name = "lm93" }, + { .name = "lm94" }, { } }; MODULE_DEVICE_TABLE(i2c, lm93_id); diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 387b3ba81dbf..74f280b90e3e 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -532,8 +532,8 @@ static int lm95234_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95234_id[] = { - { "lm95233", lm95233 }, - { "lm95234", lm95234 }, + { .name = "lm95233", .driver_data = lm95233 }, + { .name = "lm95234", .driver_data = lm95234 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95234_id); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 456381b0938e..0cb0edb3845d 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -441,8 +441,8 @@ static int lm95241_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95241_id[] = { - { "lm95231" }, - { "lm95241" }, + { .name = "lm95231" }, + { .name = "lm95241" }, { } }; MODULE_DEVICE_TABLE(i2c, lm95241_id); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 9ed300c6b5f7..11553391f54b 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -546,8 +546,8 @@ static int lm95245_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95245_id[] = { - { "lm95235" }, - { "lm95245" }, + { .name = "lm95235" }, + { .name = "lm95245" }, { } }; MODULE_DEVICE_TABLE(i2c, lm95245_id); diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index 3e0e0e0687bd..b521100afe53 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -508,7 +508,7 @@ static int ltc2945_probe(struct i2c_client *client) } static const struct i2c_device_id ltc2945_id[] = { - {"ltc2945"}, + { .name = "ltc2945" }, { } }; diff --git a/drivers/hwmon/ltc2947-i2c.c b/drivers/hwmon/ltc2947-i2c.c index 176d710706dd..e07d33983d5c 100644 --- a/drivers/hwmon/ltc2947-i2c.c +++ b/drivers/hwmon/ltc2947-i2c.c @@ -27,8 +27,8 @@ static int ltc2947_probe(struct i2c_client *i2c) } static const struct i2c_device_id ltc2947_id[] = { - {"ltc2947"}, - {} + { .name = "ltc2947" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2947_id); diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c index f1c1933c52cf..a2725e4b2f21 100644 --- a/drivers/hwmon/ltc2990.c +++ b/drivers/hwmon/ltc2990.c @@ -259,8 +259,8 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ltc2990_i2c_id[] = { - { "ltc2990" }, - {} + { .name = "ltc2990" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2990_i2c_id); diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c index 6d5d4cb846da..bc8d803e8cc9 100644 --- a/drivers/hwmon/ltc2991.c +++ b/drivers/hwmon/ltc2991.c @@ -409,8 +409,8 @@ static const struct of_device_id ltc2991_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2991_of_match); static const struct i2c_device_id ltc2991_i2c_id[] = { - { "ltc2991" }, - {} + { .name = "ltc2991" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2991_i2c_id); diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c index 2617c4538af9..43b6029c0840 100644 --- a/drivers/hwmon/ltc2992.c +++ b/drivers/hwmon/ltc2992.c @@ -948,8 +948,8 @@ static const struct of_device_id ltc2992_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2992_of_match); static const struct i2c_device_id ltc2992_i2c_id[] = { - {"ltc2992"}, - {} + { .name = "ltc2992" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2992_i2c_id); diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index fa66eda78efe..fa7c57aae5f5 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -188,7 +188,7 @@ static int ltc4151_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4151_id[] = { - { "ltc4151" }, + { .name = "ltc4151" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4151_id); diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index cce452711cec..3d439fbbbef9 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -245,7 +245,7 @@ static int ltc4215_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4215_id[] = { - { "ltc4215" }, + { .name = "ltc4215" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4215_id); diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c index f7eb007fd766..4fc69be5f2bc 100644 --- a/drivers/hwmon/ltc4222.c +++ b/drivers/hwmon/ltc4222.c @@ -200,7 +200,7 @@ static int ltc4222_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4222_id[] = { - {"ltc4222"}, + { .name = "ltc4222" }, { } }; diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index e8131a48bda7..d87aa9c32c87 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -461,7 +461,7 @@ static int ltc4245_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4245_id[] = { - { "ltc4245" }, + { .name = "ltc4245" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4245_id); diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c index 9750dc9aa336..37a85125d619 100644 --- a/drivers/hwmon/ltc4260.c +++ b/drivers/hwmon/ltc4260.c @@ -163,7 +163,7 @@ static int ltc4260_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4260_id[] = { - {"ltc4260"}, + { .name = "ltc4260" }, { } }; diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 2cd218a6a3be..a2e52ca0b06e 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -222,8 +222,8 @@ static int ltc4261_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4261_id[] = { - {"ltc4261"}, - {} + { .name = "ltc4261" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc4261_id); diff --git a/drivers/hwmon/ltc4283.c b/drivers/hwmon/ltc4283.c new file mode 100644 index 000000000000..d8931c9a4685 --- /dev/null +++ b/drivers/hwmon/ltc4283.c @@ -0,0 +1,1795 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices LTC4283 I2C Negative Voltage Hot Swap Controller (HWMON) + * + * Copyright 2025 Analog Devices Inc. + */ +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/bits.h> + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/device/devres.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/math.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/module.h> + +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +#define LTC4283_SYSTEM_STATUS 0x00 +#define LTC4283_FAULT_STATUS 0x03 +#define LTC4283_OV_MASK BIT(0) +#define LTC4283_UV_MASK BIT(1) +#define LTC4283_OC_MASK BIT(2) +#define LTC4283_FET_BAD_MASK BIT(3) +#define LTC4283_FET_SHORT_MASK BIT(6) +#define LTC4283_FAULT_LOG 0x04 +#define LTC4283_OV_FAULT_MASK BIT(0) +#define LTC4283_UV_FAULT_MASK BIT(1) +#define LTC4283_OC_FAULT_MASK BIT(2) +#define LTC4283_FET_BAD_FAULT_MASK BIT(3) +#define LTC4283_PGI_FAULT_MASK BIT(4) +#define LTC4283_PWR_FAIL_FAULT_MASK BIT(5) +#define LTC4283_FET_SHORT_FAULT_MASK BIT(6) +#define LTC4283_ADC_ALM_LOG_1 0x05 +#define LTC4283_POWER_LOW_ALM BIT(0) +#define LTC4283_POWER_HIGH_ALM BIT(1) +#define LTC4283_SENSE_LOW_ALM BIT(4) +#define LTC4283_SENSE_HIGH_ALM BIT(5) +#define LTC4283_ADC_ALM_LOG_2 0x06 +#define LTC4283_ADC_ALM_LOG_3 0x07 +#define LTC4283_ADC_ALM_LOG_4 0x08 +#define LTC4283_ADC_ALM_LOG_5 0x09 +#define LTC4283_CONTROL_1 0x0a +#define LTC4283_RW_PAGE_MASK BIT(0) +#define LTC4283_PIGIO2_ACLB_MASK BIT(2) +#define LTC4283_PWRGD_RST_CTRL_MASK BIT(3) +#define LTC4283_FET_BAD_OFF_MASK BIT(4) +#define LTC4283_THERM_TMR_MASK BIT(5) +#define LTC4283_DVDT_MASK BIT(6) +#define LTC4283_CONTROL_2 0x0b +#define LTC4283_OV_RETRY_MASK BIT(0) +#define LTC4283_UV_RETRY_MASK BIT(1) +#define LTC4283_OC_RETRY_MASK GENMASK(3, 2) +#define LTC4283_FET_BAD_RETRY_MASK GENMASK(5, 4) +#define LTC4283_EXT_FAULT_RETRY_MASK BIT(7) +#define LTC4283_RESERVED_OC 0x0c +#define LTC4283_CONFIG_1 0x0d +#define LTC4283_FB_MASK GENMASK(3, 2) +#define LTC4283_ILIM_MASK GENMASK(7, 4) +#define LTC4283_CONFIG_2 0x0e +#define LTC4283_COOLING_DL_MASK GENMASK(3, 1) +#define LTC4283_FTBD_DL_MASK GENMASK(5, 4) +#define LTC4283_CONFIG_3 0x0f +#define LTC4283_VPWR_DRNS_MASK BIT(6) +#define LTC4283_EXTFLT_TURN_OFF_MASK BIT(7) +#define LTC4283_PGIO_CONFIG 0x10 +#define LTC4283_PGIO1_CFG_MASK GENMASK(1, 0) +#define LTC4283_PGIO2_CFG_MASK GENMASK(3, 2) +#define LTC4283_PGIO3_CFG_MASK GENMASK(5, 4) +#define LTC4283_PGIO4_CFG_MASK GENMASK(7, 6) +#define LTC4283_PGIO_CONFIG_2 0x11 +#define LTC4283_ADC_MASK GENMASK(2, 0) +#define LTC4283_ADC_SELECT(c) (0x13 + (c) / 8) +#define LTC4283_ADC_SELECT_MASK(c) BIT((c) % 8) +#define LTC4283_SENSE_MIN_TH 0x1b +#define LTC4283_SENSE_MAX_TH 0x1c +#define LTC4283_VPWR_MIN_TH 0x1d +#define LTC4283_VPWR_MAX_TH 0x1e +#define LTC4283_POWER_MIN_TH 0x1f +#define LTC4283_POWER_MAX_TH 0x20 +#define LTC4283_ADC_2_MIN_TH(c) (0x21 + (c) * 2) +#define LTC4283_ADC_2_MAX_TH(c) (0x22 + (c) * 2) +#define LTC4283_ADC_2_MIN_TH_DIFF(c) (0x39 + (c) * 2) +#define LTC4283_ADC_2_MAX_TH_DIFF(c) (0x3a + (c) * 2) +#define LTC4283_SENSE 0x41 +#define LTC4283_SENSE_MIN 0x42 +#define LTC4283_SENSE_MAX 0x43 +#define LTC4283_VPWR 0x44 +#define LTC4283_VPWR_MIN 0x45 +#define LTC4283_VPWR_MAX 0x46 +#define LTC4283_POWER 0x47 +#define LTC4283_POWER_MIN 0x48 +#define LTC4283_POWER_MAX 0x49 +#define LTC4283_RESERVED_68 0x68 +#define LTC4283_RESERVED_6D 0x6D +/* get channels from ADC 2 */ +#define LTC4283_ADC_2(c) (0x4a + (c) * 3) +#define LTC4283_ADC_2_MIN(c) (0x4b + (c) * 3) +#define LTC4283_ADC_2_MAX(c) (0x4c + (c) * 3) +#define LTC4283_ADC_2_DIFF(c) (0x6e + (c) * 3) +#define LTC4283_ADC_2_MIN_DIFF(c) (0x6f + (c) * 3) +#define LTC4283_ADC_2_MAX_DIFF(c) (0x70 + (c) * 3) +#define LTC4283_ENERGY 0x7a +#define LTC4283_METER_CONTROL 0x84 +#define LTC4283_INTEGRATE_I_MASK BIT(0) +#define LTC4283_METER_HALT_MASK BIT(6) +#define LTC4283_RESERVED_86 0x86 +#define LTC4283_RESERVED_8F 0x8F +#define LTC4283_FAULT_LOG_CTRL 0x90 +#define LTC4283_FAULT_LOG_EN_MASK BIT(7) +#define LTC4283_RESERVED_91 0x91 +#define LTC4283_RESERVED_A1 0xA1 +#define LTC4283_RESERVED_A3 0xA3 +#define LTC4283_RESERVED_AC 0xAC +#define LTC4283_POWER_PLAY_MSB 0xE7 +#define LTC4283_POWER_PLAY_LSB 0xE8 +#define LTC4283_RESERVED_F1 0xF1 +#define LTC4283_RESERVED_FF 0xFF + +/* also applies for differential channels */ +#define LTC4283_ADC1_FS_uV 32768 +#define LTC4283_ADC2_FS_mV 2048 +#define LTC4283_TCONV_uS 64103 +#define LTC4283_VILIM_MIN_uV 15000 +#define LTC4283_VILIM_MAX_uV 30000 +#define LTC4283_VILIM_RANGE \ + (LTC4283_VILIM_MAX_uV - LTC4283_VILIM_MIN_uV + 1) + +#define LTC4283_PGIO_FUNC_GPIO 2 +#define LTC4283_PGIO2_FUNC_ACLB 3 + +/* + * Maximum value for rsense in nano ohms. The reasoning for this value is that + * it's the max value for which multiplying by 256 does not overflow long on + * 32bits. For the minimum value, is a sane minimum rsense for which power_max + * does not overflow 32bits. + */ +#define LTC4283_MAX_RSENSE 1677721599 +#define LTC4283_MIN_RSENSE 50000 + +/* voltage channels */ +enum { + LTC4283_CHAN_VIN, + LTC4283_CHAN_VPWR, + LTC4283_CHAN_ADI_1, + LTC4283_CHAN_ADI_2, + LTC4283_CHAN_ADI_3, + LTC4283_CHAN_ADI_4, + LTC4283_CHAN_ADIO_1, + LTC4283_CHAN_ADIO_2, + LTC4283_CHAN_ADIO_3, + LTC4283_CHAN_ADIO_4, + LTC4283_CHAN_DRNS, + LTC4283_CHAN_DRAIN, + /* differential channels */ + LTC4283_CHAN_ADIN12, + LTC4283_CHAN_ADIN34, + LTC4283_CHAN_ADIO12, + LTC4283_CHAN_ADIO34, + LTC4283_CHAN_MAX +}; + +/* Just for ease of use on the regmap */ +#define LTC4283_ADIO34_MAX \ + LTC4283_ADC_2_MAX_DIFF(LTC4283_CHAN_ADIO34 - LTC4283_CHAN_ADIN12) + +struct ltc4283_hwmon { + struct regmap *map; + struct i2c_client *client; + unsigned long gpio_mask; + unsigned long ch_enable_mask; + /* in microwatt */ + unsigned long power_max; + /* in millivolt */ + u32 vsense_max; + /* in tenths of microohm*/ + u32 rsense; + bool energy_en; + bool ext_fault; +}; + +static int ltc4283_read_voltage_word(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long *val) +{ + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(__raw * fs, BIT(16)); + return 0; +} + +static int ltc4283_read_voltage_byte(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long *val) +{ + int ret; + u32 in; + + ret = regmap_read(st->map, reg, &in); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(in * fs, BIT(8)); + return 0; +} + +static u32 ltc4283_in_reg(u32 attr, u32 channel) +{ + switch (attr) { + case hwmon_in_input: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_highest: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MAX; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MAX(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MAX_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_lowest: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MIN; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_max: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MAX_TH; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + default: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MIN_TH; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + } +} + +static int ltc4283_read_in_vals(const struct ltc4283_hwmon *st, + u32 attr, u32 channel, long *val) +{ + u32 reg = ltc4283_in_reg(attr, channel); + int ret; + + if (channel < LTC4283_CHAN_ADIN12) { + if (attr != hwmon_in_max && attr != hwmon_in_min) + return ltc4283_read_voltage_word(st, reg, + LTC4283_ADC2_FS_mV, + val); + + return ltc4283_read_voltage_byte(st, reg, + LTC4283_ADC2_FS_mV, val); + } + + if (attr != hwmon_in_max && attr != hwmon_in_min) + ret = ltc4283_read_voltage_word(st, reg, + LTC4283_ADC1_FS_uV, val); + else + ret = ltc4283_read_voltage_byte(st, reg, + LTC4283_ADC1_FS_uV, val); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(*val, MILLI); + return 0; +} + +static int ltc4283_read_alarm(struct ltc4283_hwmon *st, u32 reg, + u32 mask, long *val) +{ + u32 alarm; + int ret; + + ret = regmap_read(st->map, reg, &alarm); + if (ret) + return ret; + + *val = !!(alarm & mask); + + /* If not status/fault logs, clear the alarm after reading it. */ + if (reg != LTC4283_FAULT_STATUS && reg != LTC4283_FAULT_LOG) + return regmap_write(st->map, reg, alarm & ~mask); + + return 0; +} + +static int ltc4283_read_in_alarm(struct ltc4283_hwmon *st, u32 channel, + bool max_alm, long *val) +{ + if (channel == LTC4283_CHAN_VPWR) + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + BIT(2 + max_alm), val); + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_ADI_4) { + u32 bit = (channel - LTC4283_CHAN_ADI_1) * 2; + /* + * Lower channels go to higher bits. We also want to go +1 down + * in the min_alarm case. + */ + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_2, + BIT(7 - bit - !max_alm), val); + } + + if (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) { + u32 bit = (channel - LTC4283_CHAN_ADIO_1) * 2; + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_3, + BIT(7 - bit - !max_alm), val); + } + + if (channel >= LTC4283_CHAN_ADIN12 && channel <= LTC4283_CHAN_ADIO34) { + u32 bit = (channel - LTC4283_CHAN_ADIN12) * 2; + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_5, + BIT(7 - bit - !max_alm), val); + } + + if (channel == LTC4283_CHAN_DRNS) + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4, + BIT(6 + max_alm), val); + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4, BIT(4 + max_alm), + val); +} + +static int ltc4283_read_in(struct ltc4283_hwmon *st, u32 attr, u32 channel, + long *val) +{ + switch (attr) { + case hwmon_in_input: + if (!test_bit(channel, &st->ch_enable_mask)) + return -ENODATA; + + return ltc4283_read_in_vals(st, attr, channel, val); + case hwmon_in_highest: + case hwmon_in_lowest: + case hwmon_in_max: + case hwmon_in_min: + return ltc4283_read_in_vals(st, attr, channel, val); + case hwmon_in_max_alarm: + return ltc4283_read_in_alarm(st, channel, true, val); + case hwmon_in_min_alarm: + return ltc4283_read_in_alarm(st, channel, false, val); + case hwmon_in_crit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_OV_MASK, val); + case hwmon_in_lcrit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_UV_MASK, val); + case hwmon_in_fault: + /* + * We report failure if we detect either a fer_bad or a + * fet_short in the status register. + */ + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_FET_BAD_MASK | LTC4283_FET_SHORT_MASK, val); + case hwmon_in_enable: + *val = test_bit(channel, &st->ch_enable_mask); + return 0; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ltc4283_read_current_word(const struct ltc4283_hwmon *st, u32 reg, + long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI; + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + *val = DIV64_U64_ROUND_CLOSEST(__raw * temp, + BIT_ULL(16) * st->rsense); + + return 0; +} + +static int ltc4283_read_current_byte(const struct ltc4283_hwmon *st, u32 reg, + long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI; + u32 curr; + int ret; + + ret = regmap_read(st->map, reg, &curr); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST_ULL(curr * temp, BIT(8) * st->rsense); + return 0; +} + +static int ltc4283_read_curr(struct ltc4283_hwmon *st, u32 attr, long *val) +{ + switch (attr) { + case hwmon_curr_input: + return ltc4283_read_current_word(st, LTC4283_SENSE, val); + case hwmon_curr_highest: + return ltc4283_read_current_word(st, LTC4283_SENSE_MAX, val); + case hwmon_curr_lowest: + return ltc4283_read_current_word(st, LTC4283_SENSE_MIN, val); + case hwmon_curr_max: + return ltc4283_read_current_byte(st, LTC4283_SENSE_MAX_TH, val); + case hwmon_curr_min: + return ltc4283_read_current_byte(st, LTC4283_SENSE_MIN_TH, val); + case hwmon_curr_max_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_SENSE_HIGH_ALM, val); + case hwmon_curr_min_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_SENSE_LOW_ALM, val); + case hwmon_curr_crit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_OC_MASK, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_read_power_word(const struct ltc4283_hwmon *st, + u32 reg, long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + /* + * Power is given by: + * P = CODE(16b) * 32.768mV * 2.048V / (2^16 * Rsense) + */ + *val = DIV64_U64_ROUND_CLOSEST(temp * __raw, BIT_ULL(16) * st->rsense); + + return 0; +} + +static int ltc4283_read_power_byte(const struct ltc4283_hwmon *st, + u32 reg, long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u32 power; + int ret; + + ret = regmap_read(st->map, reg, &power); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST_ULL(power * temp, BIT(8) * st->rsense); + + return 0; +} + +static int ltc4283_read_power(struct ltc4283_hwmon *st, u32 attr, long *val) +{ + switch (attr) { + case hwmon_power_input: + return ltc4283_read_power_word(st, LTC4283_POWER, val); + case hwmon_power_input_highest: + return ltc4283_read_power_word(st, LTC4283_POWER_MAX, val); + case hwmon_power_input_lowest: + return ltc4283_read_power_word(st, LTC4283_POWER_MIN, val); + case hwmon_power_max_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_POWER_HIGH_ALM, val); + case hwmon_power_min_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_POWER_LOW_ALM, val); + case hwmon_power_max: + return ltc4283_read_power_byte(st, LTC4283_POWER_MAX_TH, val); + case hwmon_power_min: + return ltc4283_read_power_byte(st, LTC4283_POWER_MIN_TH, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_read_energy(struct ltc4283_hwmon *st, u32 attr, s64 *val) +{ + u64 temp = LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV, energy; + u8 raw[8] = {}; + int ret; + + if (!st->energy_en) + return -ENODATA; + + ret = i2c_smbus_read_i2c_block_data(st->client, LTC4283_ENERGY, 6, raw); + if (ret < 0) + return ret; + if (ret != 6) + return -EIO; + + energy = get_unaligned_be64(raw) >> 16; + + /* + * The formula for energy is given by: + * E = CODE(48b) * 32.768mV * 2.048V * Tconv / 2^24 * Rsense + * + * As Rsense can have tenths of micro-ohm resolution, we need to + * multiply by DECA to get microjoule. + */ + + /* + * Use mul_u64_u64_div_u64() to handle the 128-bit intermediate + * product of energy (up to 48 bits) * temp * Tconv without overflow. + * Multiply rsense by CENTI to convert from tenths-of-microohm back + * to nanoohm so the result comes out in microjoule. + */ + energy = mul_u64_u64_div_u64(energy, temp * LTC4283_TCONV_uS, + BIT_ULL(24) * st->rsense * CENTI); + + *val = energy; + return 0; +} + +static int ltc4283_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct ltc4283_hwmon *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + return ltc4283_read_in(st, attr, channel, val); + case hwmon_curr: + return ltc4283_read_curr(st, attr, val); + case hwmon_power: + return ltc4283_read_power(st, attr, val); + case hwmon_energy: + *val = st->energy_en; + return 0; + case hwmon_energy64: + return ltc4283_read_energy(st, attr, (s64 *)val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_power_byte(const struct ltc4283_hwmon *st, u32 reg, + long val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u32 __raw; + + val = clamp_val(val, 0, st->power_max); + __raw = DIV64_U64_ROUND_CLOSEST(val * BIT_ULL(8) * st->rsense, temp); + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_write_power_word(const struct ltc4283_hwmon *st, + u32 reg, unsigned long val) +{ + u64 divisor = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u16 __raw; + + __raw = mul_u64_u64_div_u64(val, st->rsense * BIT_ULL(16), divisor); + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_reset_power_hist(struct ltc4283_hwmon *st) +{ + int ret; + + ret = ltc4283_write_power_word(st, LTC4283_POWER_MIN, st->power_max); + if (ret) + return ret; + + ret = ltc4283_write_power_word(st, LTC4283_POWER_MAX, 0); + if (ret) + return ret; + + /* Clear possible power faults. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_PWR_FAIL_FAULT_MASK | LTC4283_PGI_FAULT_MASK); +} + +static int ltc4283_write_power(struct ltc4283_hwmon *st, u32 attr, long val) +{ + switch (attr) { + case hwmon_power_max: + return ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, val); + case hwmon_power_min: + return ltc4283_write_power_byte(st, LTC4283_POWER_MIN_TH, val); + case hwmon_power_reset_history: + return ltc4283_reset_power_hist(st); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_in_history(struct ltc4283_hwmon *st, u32 reg, + long lowest, u32 fs) +{ + u32 __raw; + int ret; + + __raw = DIV_ROUND_CLOSEST(BIT(16) * lowest, fs); + if (__raw == BIT(16)) + __raw = U16_MAX; + + ret = regmap_write(st->map, reg, __raw); + if (ret) + return ret; + + return regmap_write(st->map, reg + 1, 0); +} + +static int ltc4283_write_in_byte(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long val) +{ + u32 __raw; + + val = clamp_val(val, 0, fs); + __raw = DIV_ROUND_CLOSEST(val * BIT(8), fs); + if (__raw == BIT(8)) + __raw = U8_MAX; + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_reset_in_hist(struct ltc4283_hwmon *st, u32 channel) +{ + u32 reg, fs; + int ret; + + /* + * Make sure to clear possible under/over voltage faults. Otherwise the + * chip won't latch on again. + */ + if (channel == LTC4283_CHAN_VIN) + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_OV_FAULT_MASK | LTC4283_UV_FAULT_MASK); + + if (channel == LTC4283_CHAN_VPWR) + return ltc4283_write_in_history(st, LTC4283_VPWR_MIN, + LTC4283_ADC2_FS_mV, + LTC4283_ADC2_FS_mV); + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) { + fs = LTC4283_ADC2_FS_mV; + reg = LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1); + } else { + fs = LTC4283_ADC1_FS_uV; + reg = LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12); + } + + ret = ltc4283_write_in_history(st, reg, fs, fs); + if (ret) + return ret; + if (channel != LTC4283_CHAN_DRAIN) + return 0; + + /* Then, let's also clear possible fet faults. Same as above. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_FET_BAD_FAULT_MASK | LTC4283_FET_SHORT_FAULT_MASK); +} + +static int ltc4283_write_in_en(struct ltc4283_hwmon *st, u32 channel, bool en) +{ + unsigned int bit, adc_idx = channel - LTC4283_CHAN_ADI_1; + unsigned int reg = LTC4283_ADC_SELECT(adc_idx); + int ret; + + bit = LTC4283_ADC_SELECT_MASK(adc_idx); + if (channel > LTC4283_CHAN_DRAIN) + /* Account for two reserved fields after DRAIN. */ + bit <<= 2; + + if (en) + ret = regmap_set_bits(st->map, reg, bit); + else + ret = regmap_clear_bits(st->map, reg, bit); + if (ret) + return ret; + + __assign_bit(channel, &st->ch_enable_mask, en); + return 0; +} + +static int ltc4283_write_minmax(struct ltc4283_hwmon *st, long val, + u32 channel, bool is_max) +{ + u32 reg; + + if (channel == LTC4283_CHAN_VPWR) { + if (is_max) + return ltc4283_write_in_byte(st, LTC4283_VPWR_MAX_TH, + LTC4283_ADC2_FS_mV, val); + + return ltc4283_write_in_byte(st, LTC4283_VPWR_MIN_TH, + LTC4283_ADC2_FS_mV, val); + } + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) { + if (is_max) { + reg = LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1); + return ltc4283_write_in_byte(st, reg, + LTC4283_ADC2_FS_mV, val); + } + + reg = LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC2_FS_mV, val); + } + + /* Clamp before multiplying to avoid overflow on any arch. */ + val = clamp_val(val, 0, LONG_MAX / MILLI); + + if (is_max) { + reg = LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV, + val * MILLI); + } + + reg = LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV, val * MILLI); +} + +static int ltc4283_write_in(struct ltc4283_hwmon *st, u32 attr, long val, + int channel) +{ + switch (attr) { + case hwmon_in_max: + return ltc4283_write_minmax(st, val, channel, true); + case hwmon_in_min: + return ltc4283_write_minmax(st, val, channel, false); + case hwmon_in_reset_history: + return ltc4283_reset_in_hist(st, channel); + case hwmon_in_enable: + return ltc4283_write_in_en(st, channel, !!val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_curr_byte(const struct ltc4283_hwmon *st, + u32 reg, long val) +{ + u32 temp = LTC4283_ADC1_FS_uV * DECA * MILLI; + u32 reg_val, isense_max; + + isense_max = DIV_ROUND_CLOSEST(st->vsense_max * MICRO * DECA, st->rsense); + val = clamp_val(val, 0, isense_max); + reg_val = DIV_ROUND_CLOSEST_ULL(val * BIT_ULL(8) * st->rsense, temp); + + return regmap_write(st->map, reg, reg_val); +} + +static int ltc4283_write_curr_history(struct ltc4283_hwmon *st) +{ + int ret; + + ret = ltc4283_write_in_history(st, LTC4283_SENSE_MIN, + st->vsense_max * MILLI, + LTC4283_ADC1_FS_uV); + if (ret) + return ret; + + /* Now, let's also clear possible overcurrent logs. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_OC_FAULT_MASK); +} + +static int ltc4283_write_curr(struct ltc4283_hwmon *st, u32 attr, long val) +{ + switch (attr) { + case hwmon_curr_max: + return ltc4283_write_curr_byte(st, LTC4283_SENSE_MAX_TH, val); + case hwmon_curr_min: + return ltc4283_write_curr_byte(st, LTC4283_SENSE_MIN_TH, val); + case hwmon_curr_reset_history: + return ltc4283_write_curr_history(st); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_energy_enable_set(struct ltc4283_hwmon *st, long val) +{ + int ret; + + /* Setting the bit halts the meter. */ + val = !!val; + ret = regmap_update_bits(st->map, LTC4283_METER_CONTROL, + LTC4283_METER_HALT_MASK, + FIELD_PREP(LTC4283_METER_HALT_MASK, !val)); + if (ret) + return ret; + + st->energy_en = val; + + return 0; +} + +static int ltc4283_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct ltc4283_hwmon *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_power: + return ltc4283_write_power(st, attr, val); + case hwmon_in: + return ltc4283_write_in(st, attr, val, channel); + case hwmon_curr: + return ltc4283_write_curr(st, attr, val); + case hwmon_energy: + return ltc4283_energy_enable_set(st, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t ltc4283_in_is_visible(const struct ltc4283_hwmon *st, + u32 attr, int channel) +{ + /* If ADIO is set as a GPIO, don´t make it visible. */ + if (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) { + /* ADIOX pins come at index 0 in the gpio mask. */ + channel -= LTC4283_CHAN_ADIO_1; + if (test_bit(channel, &st->gpio_mask)) + return 0; + } + + /* Also take care of differential channels. */ + if (channel >= LTC4283_CHAN_ADIO12 && channel <= LTC4283_CHAN_ADIO34) { + channel -= LTC4283_CHAN_ADIO12; + /* If one channel in the pair is used, make it invisible. */ + if (test_bit(channel * 2, &st->gpio_mask) || + test_bit(channel * 2 + 1, &st->gpio_mask)) + return 0; + } + + switch (attr) { + case hwmon_in_input: + case hwmon_in_highest: + case hwmon_in_lowest: + case hwmon_in_max_alarm: + case hwmon_in_min_alarm: + case hwmon_in_label: + case hwmon_in_lcrit_alarm: + case hwmon_in_crit_alarm: + case hwmon_in_fault: + return 0444; + case hwmon_in_max: + case hwmon_in_min: + case hwmon_in_enable: + return 0644; + case hwmon_in_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_curr_is_visible(u32 attr) +{ + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_highest: + case hwmon_curr_lowest: + case hwmon_curr_max_alarm: + case hwmon_curr_min_alarm: + case hwmon_curr_crit_alarm: + case hwmon_curr_label: + return 0444; + case hwmon_curr_max: + case hwmon_curr_min: + return 0644; + case hwmon_curr_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_power_is_visible(u32 attr) +{ + switch (attr) { + case hwmon_power_input: + case hwmon_power_input_highest: + case hwmon_power_input_lowest: + case hwmon_power_label: + case hwmon_power_max_alarm: + case hwmon_power_min_alarm: + return 0444; + case hwmon_power_max: + case hwmon_power_min: + return 0644; + case hwmon_power_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_in: + return ltc4283_in_is_visible(data, attr, channel); + case hwmon_curr: + return ltc4283_curr_is_visible(attr); + case hwmon_power: + return ltc4283_power_is_visible(attr); + case hwmon_energy: + /* hwmon_energy_enable */ + return 0644; + case hwmon_energy64: + /* hwmon_energy_input */ + return 0444; + default: + return 0; + } +} + +static const char * const ltc4283_in_strs[] = { + "VIN", "VPWR", "VADI1", "VADI2", "VADI3", "VADI4", "VADIO1", "VADIO2", + "VADIO3", "VADIO4", "DRNS", "DRAIN", "ADIN2-ADIN1", "ADIN4-ADIN3", + "ADIO2-ADIO1", "ADIO4-ADIO3" +}; + +static int ltc4283_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_in: + *str = ltc4283_in_strs[channel]; + return 0; + case hwmon_curr: + *str = "ISENSE"; + return 0; + case hwmon_power: + *str = "Power"; + return 0; + default: + return -EOPNOTSUPP; + } +} + +/* + * Set max limits for ISENSE and Power as that depends on the max voltage on + * rsense that is defined in ILIM_ADJUST. This is specially important for power + * because for some rsense and vfsout values, if we allow the default raw 255 + * value, that would overflow long in 32bit archs when reading back the max + * power limit. + */ +static int ltc4283_set_max_limits(struct ltc4283_hwmon *st, struct device *dev) +{ + u32 temp = st->vsense_max * DECA * MICRO; + int ret; + + ret = ltc4283_write_in_byte(st, LTC4283_SENSE_MAX_TH, LTC4283_ADC1_FS_uV, + st->vsense_max * MILLI); + if (ret) + return ret; + + /* Power is given by ISENSE * Vout. */ + st->power_max = DIV_ROUND_CLOSEST(temp, st->rsense) * LTC4283_ADC2_FS_mV; + return ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, st->power_max); +} + +static int ltc4283_parse_array_prop(const struct ltc4283_hwmon *st, + struct device *dev, const char *prop, + const u32 *vals, u32 n_vals) +{ + u32 prop_val; + int ret; + u32 i; + + ret = device_property_read_u32(dev, prop, &prop_val); + if (ret) + return n_vals; + + for (i = 0; i < n_vals; i++) { + if (prop_val != vals[i]) + continue; + + return i; + } + + return dev_err_probe(dev, -EINVAL, + "Invalid %s property value %u\n", prop, prop_val); +} + +static int ltc4283_get_defaults(struct ltc4283_hwmon *st) +{ + u32 reg_val, ilm_adjust, c; + int ret; + + ret = regmap_read(st->map, LTC4283_METER_CONTROL, ®_val); + if (ret) + return ret; + + st->energy_en = !FIELD_GET(LTC4283_METER_HALT_MASK, reg_val); + + ret = regmap_read(st->map, LTC4283_CONFIG_1, ®_val); + if (ret) + return ret; + + ilm_adjust = FIELD_GET(LTC4283_ILIM_MASK, reg_val); + st->vsense_max = LTC4283_VILIM_MIN_uV / MILLI + ilm_adjust; + + ret = regmap_read(st->map, LTC4283_PGIO_CONFIG, ®_val); + if (ret) + return ret; + + /* Can be latter overwritten in ltc4283_pgio_config() */ + if (FIELD_GET(LTC4283_PGIO4_CFG_MASK, reg_val) < LTC4283_PGIO_FUNC_GPIO) + st->ext_fault = true; + + /* VPWR and VIN are always enabled */ + __set_bit(LTC4283_CHAN_VIN, &st->ch_enable_mask); + __set_bit(LTC4283_CHAN_VPWR, &st->ch_enable_mask); + for (c = LTC4283_CHAN_ADI_1; c < LTC4283_CHAN_MAX; c++) { + u32 chan = c - LTC4283_CHAN_ADI_1, bit; + + ret = regmap_read(st->map, LTC4283_ADC_SELECT(chan), ®_val); + if (ret) + return ret; + + bit = LTC4283_ADC_SELECT_MASK(chan); + if (c > LTC4283_CHAN_DRAIN) + /* account for two reserved fields after DRAIN */ + bit <<= 2; + + if (!(bit & reg_val)) + continue; + + __set_bit(c, &st->ch_enable_mask); + } + + return 0; +} + +static const char * const ltc4283_pgio1_funcs[] = { + "inverted_power_good", "power_good", "gpio" +}; + +static const char * const ltc4283_pgio2_funcs[] = { + "inverted_power_good", "power_good", "gpio", "active_current_limiting" +}; + +static const char * const ltc4283_pgio3_funcs[] = { + "inverted_power_good_input", "power_good_input", "gpio" +}; + +static const char * const ltc4283_pgio4_funcs[] = { + "inverted_external_fault", "external_fault", "gpio" +}; + +enum { + LTC4283_PIN_ADIO1, + LTC4283_PIN_ADIO2, + LTC4283_PIN_ADIO3, + LTC4283_PIN_ADIO4, + LTC4283_PIN_PGIO1, + LTC4283_PIN_PGIO2, + LTC4283_PIN_PGIO3, + LTC4283_PIN_PGIO4, +}; + +static int ltc4283_pgio_config(struct ltc4283_hwmon *st, struct device *dev) +{ + int ret, func; + + func = device_property_match_property_string(dev, "adi,pgio1-func", + ltc4283_pgio1_funcs, + ARRAY_SIZE(ltc4283_pgio1_funcs)); + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio1-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO1, &st->gpio_mask); + /* If GPIO, default to an input pin. */ + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO1_CFG_MASK, + FIELD_PREP(LTC4283_PGIO1_CFG_MASK, func)); + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio2-func", + ltc4283_pgio2_funcs, + ARRAY_SIZE(ltc4283_pgio2_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio2-func property\n"); + if (func >= 0) { + if (func != LTC4283_PGIO2_FUNC_ACLB) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO2, &st->gpio_mask); + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO2_CFG_MASK, + FIELD_PREP(LTC4283_PGIO2_CFG_MASK, func)); + } else { + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, + LTC4283_PIGIO2_ACLB_MASK); + } + + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio3-func", + ltc4283_pgio3_funcs, + ARRAY_SIZE(ltc4283_pgio3_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio3-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO3, &st->gpio_mask); + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO3_CFG_MASK, + FIELD_PREP(LTC4283_PGIO3_CFG_MASK, func)); + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio4-func", + ltc4283_pgio4_funcs, + ARRAY_SIZE(ltc4283_pgio4_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio4-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO4, &st->gpio_mask); + func++; + st->ext_fault = false; + } else { + st->ext_fault = true; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO4_CFG_MASK, + FIELD_PREP(LTC4283_PGIO4_CFG_MASK, func)); + if (ret) + return ret; + } + + return 0; +} + +static int ltc4283_adio_config(struct ltc4283_hwmon *st, struct device *dev, + const char *prop, u32 pin) +{ + u32 adc_idx; + int ret; + + if (!device_property_read_bool(dev, prop)) + return 0; + + adc_idx = LTC4283_CHAN_ADIO_1 - LTC4283_CHAN_ADI_1 + pin; + ret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(adc_idx), + LTC4283_ADC_SELECT_MASK(adc_idx)); + if (ret) + return ret; + + __set_bit(pin, &st->gpio_mask); + return 0; +} + +static int ltc4283_pin_config(struct ltc4283_hwmon *st, struct device *dev) +{ + int ret; + + ret = ltc4283_pgio_config(st, dev); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio1", LTC4283_PIN_ADIO1); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio2", LTC4283_PIN_ADIO2); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio3", LTC4283_PIN_ADIO3); + if (ret) + return ret; + + return ltc4283_adio_config(st, dev, "adi,gpio-on-adio4", LTC4283_PIN_ADIO4); +} + +static const char * const ltc4283_oc_fet_retry[] = { + "latch-off", "1", "7", "unlimited" +}; + +static const u32 ltc4283_fb_factor[] = { + 100, 50, 20, 10 +}; + +static const u32 ltc4283_cooling_dl[] = { + 512, 1002, 2005, 4100, 8190, 16400, 32800, 65600 +}; + +static const u32 ltc4283_fet_bad_delay[] = { + 256, 512, 1002, 2005 +}; + +static int ltc4283_setup(struct ltc4283_hwmon *st, struct device *dev) +{ + u32 val; + int ret; + + /* The part has an eeprom so let's get the needed defaults from it */ + ret = ltc4283_get_defaults(st); + if (ret) + return ret; + + /* + * Default to LTC4283_MIN_RSENSE so we can probe without FW properties. + */ + st->rsense = LTC4283_MIN_RSENSE; + ret = device_property_read_u32(dev, "adi,rsense-nano-ohms", + &st->rsense); + if (!ret) { + if (st->rsense < LTC4283_MIN_RSENSE || st->rsense > LTC4283_MAX_RSENSE) + return dev_err_probe(dev, -EINVAL, + "adi,rsense-nano-ohms(%u) too small or too large [%u %u]\n", + st->rsense, LTC4283_MIN_RSENSE, LTC4283_MAX_RSENSE); + } + + /* + * The resolution for rsense is tenths of micro (eg: 62.5 uOhm) which + * means we need nano in the bindings. However, to make things easier to + * handle (with respect to overflows) we divide it by 100 as we don't + * really need the last two digits. + */ + st->rsense /= CENTI; + + ret = device_property_read_u32(dev, "adi,current-limit-sense-microvolt", + &st->vsense_max); + if (!ret) { + u32 reg_val; + + if (!in_range(st->vsense_max, LTC4283_VILIM_MIN_uV, + LTC4283_VILIM_RANGE)) { + return dev_err_probe(dev, -EINVAL, + "adi,current-limit-sense-microvolt (%u) out of range [%u %u]\n", + st->vsense_max, LTC4283_VILIM_MIN_uV, + LTC4283_VILIM_MAX_uV); + } + + st->vsense_max /= MILLI; + reg_val = FIELD_PREP(LTC4283_ILIM_MASK, + st->vsense_max - LTC4283_VILIM_MIN_uV / MILLI); + ret = regmap_update_bits(st->map, LTC4283_CONFIG_1, + LTC4283_ILIM_MASK, reg_val); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,current-limit-foldback-factor", + ltc4283_fb_factor, ARRAY_SIZE(ltc4283_fb_factor)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_fb_factor)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_1, LTC4283_FB_MASK, + FIELD_PREP(LTC4283_FB_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,cooling-delay-ms", + ltc4283_cooling_dl, ARRAY_SIZE(ltc4283_cooling_dl)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_cooling_dl)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_COOLING_DL_MASK, + FIELD_PREP(LTC4283_COOLING_DL_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,fet-bad-timer-delay-ms", + ltc4283_fet_bad_delay, ARRAY_SIZE(ltc4283_fet_bad_delay)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_fet_bad_delay)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_FTBD_DL_MASK, + FIELD_PREP(LTC4283_FTBD_DL_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_set_max_limits(st, dev); + if (ret) + return ret; + + ret = ltc4283_pin_config(st, dev); + if (ret) + return ret; + + if (device_property_read_bool(dev, "adi,power-good-reset-on-fet")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_PWRGD_RST_CTRL_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,fet-turn-off-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_FET_BAD_OFF_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,tmr-pull-down-disable")) { + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, + LTC4283_THERM_TMR_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,dvdt-inrush-control-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_DVDT_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,undervoltage-retry-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_2, + LTC4283_UV_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,overvoltage-retry-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_2, + LTC4283_OV_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,external-fault-retry-enable")) { + if (!st->ext_fault) + return dev_err_probe(dev, -EINVAL, + "adi,external-fault-retry-enable set but PGIO4 not configured\n"); + ret = regmap_set_bits(st->map, LTC4283_CONTROL_2, + LTC4283_EXT_FAULT_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,fault-log-enable")) { + ret = regmap_set_bits(st->map, LTC4283_FAULT_LOG_CTRL, + LTC4283_FAULT_LOG_EN_MASK); + if (ret) + return ret; + } + + ret = device_property_match_property_string(dev, "adi,overcurrent-retries", + ltc4283_oc_fet_retry, + ARRAY_SIZE(ltc4283_oc_fet_retry)); + /* We still want to catch when an invalid string is given. */ + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "adi,overcurrent-retries invalid value\n"); + if (ret >= 0) { + ret = regmap_update_bits(st->map, LTC4283_CONTROL_2, + LTC4283_OC_RETRY_MASK, + FIELD_PREP(LTC4283_OC_RETRY_MASK, ret)); + if (ret) + return ret; + } + + ret = device_property_match_property_string(dev, "adi,fet-bad-retries", + ltc4283_oc_fet_retry, + ARRAY_SIZE(ltc4283_oc_fet_retry)); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "adi,fet-bad-retries invalid value\n"); + if (ret >= 0) { + ret = regmap_update_bits(st->map, LTC4283_CONTROL_2, + LTC4283_FET_BAD_RETRY_MASK, + FIELD_PREP(LTC4283_FET_BAD_RETRY_MASK, ret)); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,external-fault-fet-off-enable")) { + if (!st->ext_fault) + return dev_err_probe(dev, -EINVAL, + "adi,external-fault-fet-off-enable set but PGIO4 not configured\n"); + ret = regmap_set_bits(st->map, LTC4283_CONFIG_3, + LTC4283_EXTFLT_TURN_OFF_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,vpower-drns-enable")) { + u32 chan = LTC4283_CHAN_DRNS - LTC4283_CHAN_ADI_1; + + __clear_bit(LTC4283_CHAN_DRNS, &st->ch_enable_mask); + /* + * Then, let's by default disable DRNS from ADC2 given that it + * is already being monitored by the VPWR channel. One can still + * enable it later on if needed. + */ + ret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(chan), + LTC4283_ADC_SELECT_MASK(chan)); + if (ret) + return ret; + + val = 1; + } else { + val = 0; + } + + ret = regmap_update_bits(st->map, LTC4283_CONFIG_3, + LTC4283_VPWR_DRNS_MASK, + FIELD_PREP(LTC4283_VPWR_DRNS_MASK, val)); + if (ret) + return ret; + + /* Make sure the ADC has 12bit resolution since we're assuming that. */ + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG_2, + LTC4283_ADC_MASK, + FIELD_PREP(LTC4283_ADC_MASK, 3)); + if (ret) + return ret; + + /* Energy reads (which are 6 byte block reads) rely on page access */ + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, LTC4283_RW_PAGE_MASK); + if (ret) + return ret; + + /* + * Make sure we are integrating power as we only support reporting + * consumed energy. + */ + return regmap_clear_bits(st->map, LTC4283_METER_CONTROL, + LTC4283_INTEGRATE_I_MASK); +} + +static const struct hwmon_channel_info * const ltc4283_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_LCRIT_ALARM | HWMON_I_CRIT_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_RESET_HISTORY | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_FAULT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | + HWMON_C_MAX | HWMON_C_MIN | HWMON_C_MIN_ALARM | + HWMON_C_MAX_ALARM | HWMON_C_CRIT_ALARM | + HWMON_C_RESET_HISTORY | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | + HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN | + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | + HWMON_P_RESET_HISTORY | HWMON_P_LABEL), + HWMON_CHANNEL_INFO(energy, + HWMON_E_ENABLE), + HWMON_CHANNEL_INFO(energy64, + HWMON_E_INPUT), + NULL +}; + +static const struct hwmon_ops ltc4283_ops = { + .read = ltc4283_read, + .write = ltc4283_write, + .is_visible = ltc4283_is_visible, + .read_string = ltc4283_read_labels, +}; + +static const struct hwmon_chip_info ltc4283_chip_info = { + .ops = <c4283_ops, + .info = ltc4283_info, +}; + +static int ltc4283_show_fault_log(void *arg, u64 *val, u32 mask) +{ + struct ltc4283_hwmon *st = arg; + long alarm; + int ret; + + ret = ltc4283_read_alarm(st, LTC4283_FAULT_LOG, mask, &alarm); + if (ret) + return ret; + + *val = alarm; + + return 0; +} + +static int ltc4283_show_in0_lcrit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_UV_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_lcrit_fault_log, + ltc4283_show_in0_lcrit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_in0_crit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_OV_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_crit_fault_log, + ltc4283_show_in0_crit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_fet_bad_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_FET_BAD_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_bad_fault_log, + ltc4283_show_fet_bad_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_fet_short_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_FET_SHORT_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_short_fault_log, + ltc4283_show_fet_short_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_curr1_crit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_OC_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_curr1_crit_fault_log, + ltc4283_show_curr1_crit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_power1_failed_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_PWR_FAIL_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_failed_fault_log, + ltc4283_show_power1_failed_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_power1_good_input_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_PGI_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_good_input_fault_log, + ltc4283_show_power1_good_input_fault_log, NULL, "%llu\n"); + +static void ltc4283_debugfs_init(struct ltc4283_hwmon *st, struct i2c_client *i2c) +{ + debugfs_create_file_unsafe("in0_crit_fault_log", 0400, i2c->debugfs, st, + <c4283_in0_crit_fault_log); + debugfs_create_file_unsafe("in0_lcrit_fault_log", 0400, i2c->debugfs, st, + <c4283_in0_lcrit_fault_log); + debugfs_create_file_unsafe("in11_fet_bad_fault_log", 0400, i2c->debugfs, st, + <c4283_fet_bad_fault_log); + debugfs_create_file_unsafe("in11_fet_short_fault_log", 0400, i2c->debugfs, st, + <c4283_fet_short_fault_log); + debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, i2c->debugfs, st, + <c4283_curr1_crit_fault_log); + debugfs_create_file_unsafe("power1_failed_fault_log", 0400, i2c->debugfs, st, + <c4283_power1_failed_fault_log); + debugfs_create_file_unsafe("power1_good_input_fault_log", 0400, i2c->debugfs, + st, <c4283_power1_good_input_fault_log); +} + +static bool ltc4283_is_word_reg(unsigned int reg) +{ + return reg >= LTC4283_SENSE && reg <= LTC4283_ADIO34_MAX; +} + +static int ltc4283_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + int ret; + + if (ltc4283_is_word_reg(reg)) + ret = i2c_smbus_read_word_swapped(client, reg); + else + ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static int ltc4283_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + + if (ltc4283_is_word_reg(reg)) + return i2c_smbus_write_word_swapped(client, reg, val); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static const struct regmap_bus ltc4283_regmap_bus = { + .reg_read = ltc4283_reg_read, + .reg_write = ltc4283_reg_write, +}; + +static bool ltc4283_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTC4283_SYSTEM_STATUS ... LTC4283_FAULT_STATUS: + return false; + case LTC4283_RESERVED_OC: + return false; + case LTC4283_RESERVED_86 ... LTC4283_RESERVED_8F: + return false; + case LTC4283_RESERVED_91 ... LTC4283_RESERVED_A1: + return false; + case LTC4283_RESERVED_A3: + return false; + case LTC4283_RESERVED_AC: + return false; + case LTC4283_POWER_PLAY_MSB ... LTC4283_POWER_PLAY_LSB: + return false; + case LTC4283_RESERVED_F1 ... LTC4283_RESERVED_FF: + return false; + default: + return true; + } +} + +static const struct regmap_config ltc4283_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xFF, + .writeable_reg = ltc4283_writable_reg, +}; + +static int ltc4283_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev, *hwmon; + struct auxiliary_device *adev; + struct ltc4283_hwmon *st; + int ret, id; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + st->client = client; + st->map = devm_regmap_init(dev, <c4283_regmap_bus, client, + <c4283_regmap_config); + if (IS_ERR(st->map)) + return dev_err_probe(dev, PTR_ERR(st->map), + "Failed to create regmap\n"); + + ret = ltc4283_setup(st, dev); + if (ret) + return ret; + + hwmon = devm_hwmon_device_register_with_info(dev, "ltc4283", st, + <c4283_chip_info, NULL); + + if (IS_ERR(hwmon)) + return PTR_ERR(hwmon); + + ltc4283_debugfs_init(st, client); + + if (!st->gpio_mask) + return 0; + + id = (client->adapter->nr << 10) | client->addr; + adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, "gpio", + &st->gpio_mask, id); + if (!adev) + return dev_err_probe(dev, -ENODEV, "Failed to add GPIO device\n"); + + return 0; +} + +static const struct of_device_id ltc4283_of_match[] = { + { .compatible = "adi,ltc4283" }, + { } +}; + +static const struct i2c_device_id ltc4283_i2c_id[] = { + { "ltc4283" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4283_i2c_id); + +static struct i2c_driver ltc4283_driver = { + .driver = { + .name = "ltc4283", + .of_match_table = ltc4283_of_match, + }, + .probe = ltc4283_probe, + .id_table = ltc4283_i2c_id, +}; +module_i2c_driver(ltc4283_driver); + +MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("LTC4283 Hot Swap Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c index 5102d86d2619..b294e86d52d1 100644 --- a/drivers/hwmon/max127.c +++ b/drivers/hwmon/max127.c @@ -312,7 +312,7 @@ static int max127_probe(struct i2c_client *client) } static const struct i2c_device_id max127_id[] = { - { "max127" }, + { .name = "max127" }, { } }; MODULE_DEVICE_TABLE(i2c, max127_id); diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 43fbb9b26b10..f8b421754220 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -592,12 +592,12 @@ static int max16065_probe(struct i2c_client *client) } static const struct i2c_device_id max16065_id[] = { - { "max16065", max16065 }, - { "max16066", max16066 }, - { "max16067", max16067 }, - { "max16068", max16068 }, - { "max16070", max16070 }, - { "max16071", max16071 }, + { .name = "max16065", .driver_data = max16065 }, + { .name = "max16066", .driver_data = max16066 }, + { .name = "max16067", .driver_data = max16067 }, + { .name = "max16068", .driver_data = max16068 }, + { .name = "max16070", .driver_data = max16070 }, + { .name = "max16071", .driver_data = max16071 }, { } }; diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 9b6d03cff4df..77ef39e8ae7e 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -366,7 +366,7 @@ static int max1619_probe(struct i2c_client *client) } static const struct i2c_device_id max1619_id[] = { - { "max1619" }, + { .name = "max1619" }, { } }; MODULE_DEVICE_TABLE(i2c, max1619_id); diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index a8197a86f559..32548f56409c 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -293,9 +293,9 @@ static int max1668_probe(struct i2c_client *client) } static const struct i2c_device_id max1668_id[] = { - { "max1668", 5 }, - { "max1805", 3 }, - { "max1989", 5 }, + { .name = "max1668", .driver_data = 5 }, + { .name = "max1805", .driver_data = 3 }, + { .name = "max1989", .driver_data = 5 }, { } }; MODULE_DEVICE_TABLE(i2c, max1668_id); diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c index f0048ff37607..9b6ab050db1b 100644 --- a/drivers/hwmon/max197.c +++ b/drivers/hwmon/max197.c @@ -321,8 +321,8 @@ static void max197_remove(struct platform_device *pdev) } static const struct platform_device_id max197_device_ids[] = { - { "max197", max197 }, - { "max199", max199 }, + { .name = "max197", .driver_data = max197 }, + { .name = "max199", .driver_data = max199 }, { } }; MODULE_DEVICE_TABLE(platform, max197_device_ids); diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c index 2f4b419b6c9e..d132be2d6487 100644 --- a/drivers/hwmon/max31730.c +++ b/drivers/hwmon/max31730.c @@ -345,7 +345,7 @@ max31730_probe(struct i2c_client *client) } static const struct i2c_device_id max31730_ids[] = { - { "max31730" }, + { .name = "max31730" }, { } }; MODULE_DEVICE_TABLE(i2c, max31730_ids); diff --git a/drivers/hwmon/max31760.c b/drivers/hwmon/max31760.c index 127e31ca3c87..8d3be064b025 100644 --- a/drivers/hwmon/max31760.c +++ b/drivers/hwmon/max31760.c @@ -555,7 +555,7 @@ static const struct of_device_id max31760_of_match[] = { MODULE_DEVICE_TABLE(of, max31760_of_match); static const struct i2c_device_id max31760_id[] = { - {"max31760"}, + { .name = "max31760" }, { } }; MODULE_DEVICE_TABLE(i2c, max31760_id); diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 4f6171a17d9f..db8a0f6f1829 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -517,7 +517,7 @@ static int max31790_probe(struct i2c_client *client) } static const struct i2c_device_id max31790_id[] = { - { "max31790" }, + { .name = "max31790" }, { } }; MODULE_DEVICE_TABLE(i2c, max31790_id); diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index 9b2e56c040df..6a7048fdd8ab 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -463,9 +463,9 @@ static struct attribute *max31827_attrs[] = { ATTRIBUTE_GROUPS(max31827); static const struct i2c_device_id max31827_i2c_ids[] = { - { "max31827", max31827 }, - { "max31828", max31828 }, - { "max31829", max31829 }, + { .name = "max31827", .driver_data = max31827 }, + { .name = "max31828", .driver_data = max31828 }, + { .name = "max31829", .driver_data = max31829 }, { } }; MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids); diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c index 4316dcdd03fc..100acf357b5f 100644 --- a/drivers/hwmon/max6620.c +++ b/drivers/hwmon/max6620.c @@ -474,7 +474,7 @@ static int max6620_probe(struct i2c_client *client) } static const struct i2c_device_id max6620_id[] = { - { "max6620" }, + { .name = "max6620" }, { } }; MODULE_DEVICE_TABLE(i2c, max6620_id); diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c index a7066f3a0bb4..e86ec6d237ca 100644 --- a/drivers/hwmon/max6621.c +++ b/drivers/hwmon/max6621.c @@ -537,7 +537,7 @@ static int max6621_probe(struct i2c_client *client) } static const struct i2c_device_id max6621_id[] = { - { MAX6621_DRV_NAME }, + { .name = MAX6621_DRV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, max6621_id); diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 163d31f17bd4..dd5f4b3b128d 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -778,7 +778,7 @@ static int max6639_resume(struct device *dev) } static const struct i2c_device_id max6639_id[] = { - {"max6639"}, + { .name = "max6639" }, { } }; diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 56b8157885bb..90c37f5647bc 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -794,9 +794,9 @@ static int max6650_probe(struct i2c_client *client) return err; if (IS_ENABLED(CONFIG_THERMAL)) { - cooling_dev = devm_thermal_of_cooling_device_register(dev, - dev->of_node, client->name, - data, &max6650_cooling_ops); + cooling_dev = devm_thermal_of_child_cooling_device_register(dev, dev->of_node, + client->name, data, + &max6650_cooling_ops); if (IS_ERR(cooling_dev)) { dev_warn(dev, "thermal cooling device register failed: %ld\n", PTR_ERR(cooling_dev)); @@ -807,8 +807,8 @@ static int max6650_probe(struct i2c_client *client) } static const struct i2c_device_id max6650_id[] = { - { "max6650", 1 }, - { "max6651", 4 }, + { .name = "max6650", .driver_data = 1 }, + { .name = "max6651", .driver_data = 4 }, { } }; MODULE_DEVICE_TABLE(i2c, max6650_id); diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index dd906cf491ca..c864093f015c 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -564,16 +564,16 @@ static int max6697_probe(struct i2c_client *client) } static const struct i2c_device_id max6697_id[] = { - { "max6581", max6581 }, - { "max6602", max6602 }, - { "max6622", max6622 }, - { "max6636", max6636 }, - { "max6689", max6689 }, - { "max6693", max6693 }, - { "max6694", max6694 }, - { "max6697", max6697 }, - { "max6698", max6698 }, - { "max6699", max6699 }, + { .name = "max6581", .driver_data = max6581 }, + { .name = "max6602", .driver_data = max6602 }, + { .name = "max6622", .driver_data = max6622 }, + { .name = "max6636", .driver_data = max6636 }, + { .name = "max6689", .driver_data = max6689 }, + { .name = "max6693", .driver_data = max6693 }, + { .name = "max6694", .driver_data = max6694 }, + { .name = "max6697", .driver_data = max6697 }, + { .name = "max6698", .driver_data = max6698 }, + { .name = "max6699", .driver_data = max6699 }, { } }; MODULE_DEVICE_TABLE(i2c, max6697_id); diff --git a/drivers/hwmon/mc34vr500.c b/drivers/hwmon/mc34vr500.c index 84458e4533d8..8cb9d5c09033 100644 --- a/drivers/hwmon/mc34vr500.c +++ b/drivers/hwmon/mc34vr500.c @@ -235,8 +235,8 @@ static int mc34vr500_probe(struct i2c_client *client) } static const struct i2c_device_id mc34vr500_id[] = { - { "mc34vr500" }, - { }, + { .name = "mc34vr500" }, + { } }; MODULE_DEVICE_TABLE(i2c, mc34vr500_id); diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index bcddf6804d3a..d6b0ebf27941 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -177,8 +177,8 @@ static int mcp3021_probe(struct i2c_client *client) } static const struct i2c_device_id mcp3021_id[] = { - { "mcp3021", mcp3021 }, - { "mcp3221", mcp3221 }, + { .name = "mcp3021", .driver_data = mcp3021 }, + { .name = "mcp3221", .driver_data = mcp3221 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp3021_id); diff --git a/drivers/hwmon/mcp9982.c b/drivers/hwmon/mcp9982.c index 26c69e3388ab..9e19e2697e25 100644 --- a/drivers/hwmon/mcp9982.c +++ b/drivers/hwmon/mcp9982.c @@ -92,19 +92,19 @@ static const struct hwmon_channel_info * const mcp9985_info[] = { HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST), + HWMON_T_CRIT_HYST | HWMON_T_FAULT), HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), NULL @@ -369,7 +369,8 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at /* * In Standby State the conversion cycle must be initated manually in - * order to read fresh temperature values and the status of the alarms. + * order to read fresh temperature values, the status of the alarms and + * fault information. */ if (!priv->run_state) { switch (type) { @@ -379,6 +380,7 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at case hwmon_temp_max_alarm: case hwmon_temp_min_alarm: case hwmon_temp_crit_alarm: + case hwmon_temp_fault: ret = regmap_write(priv->regmap, MCP9982_ONE_SHOT_ADDR, 1); if (ret) return ret; @@ -402,6 +404,11 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at } switch (type) { + /* + * Because the ALERT/THERM pin is set in Therm(Comparator) mode, + * the external diode fault status, high limit status and low + * limit status registers do not clear the bits after reading. + */ case hwmon_temp: switch (attr) { case hwmon_temp_input: @@ -511,6 +518,13 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at *val -= hyst * 1000; return 0; + case hwmon_temp_fault: + *val = regmap_test_bits(priv->regmap, MCP9982_EXT_FAULT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; default: return -EINVAL; } @@ -681,6 +695,7 @@ static umode_t mcp9982_is_visible(const void *_data, enum hwmon_sensor_types typ case hwmon_temp_max_alarm: case hwmon_temp_max_hyst: case hwmon_temp_crit_alarm: + case hwmon_temp_fault: return 0444; case hwmon_temp_min: case hwmon_temp_max: diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c index 137a90dd2075..860de6cfd8a4 100644 --- a/drivers/hwmon/mlxreg-fan.c +++ b/drivers/hwmon/mlxreg-fan.c @@ -583,8 +583,8 @@ static int mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan) pwm->fan = fan; /* Set minimal PWM speed. */ pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(MLXREG_FAN_MIN_DUTY); - pwm->cdev = devm_thermal_of_cooling_device_register(dev, NULL, mlxreg_fan_name[i], - pwm, &mlxreg_fan_cooling_ops); + pwm->cdev = devm_thermal_cooling_device_register(dev, mlxreg_fan_name[i], + pwm, &mlxreg_fan_cooling_ops); if (IS_ERR(pwm->cdev)) { dev_err(dev, "Failed to register cooling device\n"); return PTR_ERR(pwm->cdev); diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 0581770380cc..e1c36c95affb 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -177,12 +177,13 @@ superio_exit(int ioreg) #define NCT6683_CUSTOMER_ID_MSI3 0x207 #define NCT6683_CUSTOMER_ID_MSI4 0x20d #define NCT6683_CUSTOMER_ID_AMD 0x162b -#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c +#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c #define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b #define NCT6683_CUSTOMER_ID_ASROCK3 0x1631 #define NCT6683_CUSTOMER_ID_ASROCK4 0x163e #define NCT6683_CUSTOMER_ID_ASROCK5 0x1621 #define NCT6683_CUSTOMER_ID_ASROCK6 0x1633 +#define NCT6683_CUSTOMER_ID_ASROCK7 0x163d #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -1248,6 +1249,8 @@ static int nct6683_probe(struct platform_device *pdev) break; case NCT6683_CUSTOMER_ID_ASROCK6: break; + case NCT6683_CUSTOMER_ID_ASROCK7: + break; default: if (!force) return -ENODEV; diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c index ba71d776a291..07783910c058 100644 --- a/drivers/hwmon/nct6775-i2c.c +++ b/drivers/hwmon/nct6775-i2c.c @@ -93,19 +93,19 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match); static const struct i2c_device_id nct6775_i2c_id[] = { - { "nct6106", nct6106 }, - { "nct6116", nct6116 }, - { "nct6775", nct6775 }, - { "nct6776", nct6776 }, - { "nct6779", nct6779 }, - { "nct6791", nct6791 }, - { "nct6792", nct6792 }, - { "nct6793", nct6793 }, - { "nct6795", nct6795 }, - { "nct6796", nct6796 }, - { "nct6797", nct6797 }, - { "nct6798", nct6798 }, - { "nct6799", nct6799 }, + { .name = "nct6106", .driver_data = nct6106 }, + { .name = "nct6116", .driver_data = nct6116 }, + { .name = "nct6775", .driver_data = nct6775 }, + { .name = "nct6776", .driver_data = nct6776 }, + { .name = "nct6779", .driver_data = nct6779 }, + { .name = "nct6791", .driver_data = nct6791 }, + { .name = "nct6792", .driver_data = nct6792 }, + { .name = "nct6793", .driver_data = nct6793 }, + { .name = "nct6795", .driver_data = nct6795 }, + { .name = "nct6796", .driver_data = nct6796 }, + { .name = "nct6797", .driver_data = nct6797 }, + { .name = "nct6798", .driver_data = nct6798 }, + { .name = "nct6799", .driver_data = nct6799 }, { } }; MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id); diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 8c9351da12c6..10d1a41fe211 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -47,6 +47,8 @@ static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { #define REG_PWM(x) (0x60 + (x)) #define REG_SMARTFAN_EN(x) (0x64 + (x) / 2) #define SMARTFAN_EN_SHIFT(x) ((x) % 2 * 4) +#define REG_SMARTFAN_STEP_UP_TIME 0x6e +#define REG_SMARTFAN_STEP_DOWN_TIME 0x6f #define REG_VENDOR_ID 0xfd #define REG_CHIP_ID 0xfe #define REG_VERSION_ID 0xff @@ -560,6 +562,77 @@ beep_store(struct device *dev, struct device_attribute *attr, const char *buf, return err ? : count; } +static ssize_t step_time_show(struct device *dev, struct device_attribute *attr, + char *buf, bool step_up) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg, val; + int ret; + + if (step_up) + reg = REG_SMARTFAN_STEP_UP_TIME; + else + reg = REG_SMARTFAN_STEP_DOWN_TIME; + + ret = regmap_read(data->regmap, reg, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", val * 100); /* Convert from ds to ms */ +} + +static ssize_t step_up_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return step_time_show(dev, attr, buf, true); +} + +static ssize_t step_down_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return step_time_show(dev, attr, buf, false); +} + +static ssize_t step_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count, bool step_up) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + unsigned int reg; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + /* Clamp range, and convert from ms to ds */ + val = DIV_ROUND_CLOSEST(clamp_val(val, 100, 25500), 100); + + if (step_up) + reg = REG_SMARTFAN_STEP_UP_TIME; + else + reg = REG_SMARTFAN_STEP_DOWN_TIME; + + ret = regmap_write(data->regmap, reg, val); + + return ret ? : count; +} + +static ssize_t step_up_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return step_time_store(dev, attr, buf, count, true); +} + +static ssize_t step_down_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return step_time_store(dev, attr, buf, count, false); +} + static SENSOR_DEVICE_ATTR_RW(temp1_type, temp_type, 0); static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0x01, REG_TEMP_LSB); static SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, 0x31, 0); @@ -975,12 +1048,30 @@ static const struct attribute_group nct7802_auto_point_group = { .attrs = nct7802_auto_point_attrs, }; +/* 7.2.102 0x6E FANCTL Step Up Time Register */ +static SENSOR_DEVICE_ATTR_RW(step_up_time, step_up_time, 0); + +/* 7.2.103 0x6F FANCTL Step Down Time Register */ +static SENSOR_DEVICE_ATTR_RW(step_down_time, step_down_time, 0); + +static struct attribute *nct7802_step_time_attrs[] = { + &sensor_dev_attr_step_up_time.dev_attr.attr, + &sensor_dev_attr_step_down_time.dev_attr.attr, + + NULL +}; + +static const struct attribute_group nct7802_step_time_group = { + .attrs = nct7802_step_time_attrs, +}; + static const struct attribute_group *nct7802_groups[] = { &nct7802_temp_group, &nct7802_in_group, &nct7802_fan_group, &nct7802_pwm_group, &nct7802_auto_point_group, + &nct7802_step_time_group, NULL }; @@ -1193,7 +1284,7 @@ static const unsigned short nct7802_address_list[] = { }; static const struct i2c_device_id nct7802_idtable[] = { - { "nct7802" }, + { .name = "nct7802" }, { } }; MODULE_DEVICE_TABLE(i2c, nct7802_idtable); diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 2fa091720c79..976b8a008e44 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -1146,8 +1146,8 @@ static int nct7904_probe(struct i2c_client *client) } static const struct i2c_device_id nct7904_id[] = { - {"nct7904"}, - {} + { .name = "nct7904" }, + { } }; MODULE_DEVICE_TABLE(i2c, nct7904_id); diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index c8f5e695fb6d..aea0b8659f5f 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -857,8 +857,10 @@ static int npcm7xx_create_pwm_cooling(struct device *dev, snprintf(cdev->name, THERMAL_NAME_LENGTH, "%pOFn%d", child, pwm_port); - cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, - cdev->name, cdev, &npcm7xx_pwm_cool_ops); + cdev->tcdev = devm_thermal_of_child_cooling_device_register(dev, child, + cdev->name, + cdev, + &npcm7xx_pwm_cool_ops); if (IS_ERR(cdev->tcdev)) return PTR_ERR(cdev->tcdev); diff --git a/drivers/hwmon/nsa320-hwmon.c b/drivers/hwmon/nsa320-hwmon.c index 18076ba7fc14..5c99acc09677 100644 --- a/drivers/hwmon/nsa320-hwmon.c +++ b/drivers/hwmon/nsa320-hwmon.c @@ -153,6 +153,7 @@ static const struct of_device_id of_nsa320_hwmon_match[] = { { .compatible = "zyxel,nsa320-mcu", }, { }, }; +MODULE_DEVICE_TABLE(of, of_nsa320_hwmon_match); static int nsa320_hwmon_probe(struct platform_device *pdev) { @@ -197,7 +198,6 @@ static struct platform_driver nsa320_hwmon_driver = { module_platform_driver(nsa320_hwmon_driver); -MODULE_DEVICE_TABLE(of, of_nsa320_hwmon_match); MODULE_AUTHOR("Peter Schildmann <linux@schildmann.info>"); MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>"); MODULE_DESCRIPTION("NSA320 Hardware Monitoring"); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index d6b48178343d..6f82a6c49393 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -52,18 +52,18 @@ enum { }; static const struct platform_device_id ntc_thermistor_id[] = { - [NTC_B57330V2103] = { "b57330v2103", TYPE_B57330V2103 }, - [NTC_B57891S0103] = { "b57891s0103", TYPE_B57891S0103 }, - [NTC_NCP03WB473] = { "ncp03wb473", TYPE_NCPXXWB473 }, - [NTC_NCP03WF104] = { "ncp03wf104", TYPE_NCPXXWF104 }, - [NTC_NCP15WB473] = { "ncp15wb473", TYPE_NCPXXWB473 }, - [NTC_NCP15WL333] = { "ncp15wl333", TYPE_NCPXXWL333 }, - [NTC_NCP15XH103] = { "ncp15xh103", TYPE_NCPXXXH103 }, - [NTC_NCP18WB473] = { "ncp18wb473", TYPE_NCPXXWB473 }, - [NTC_NCP21WB473] = { "ncp21wb473", TYPE_NCPXXWB473 }, - [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 }, - [NTC_NCP18WM474] = { "ncp18wm474", TYPE_NCPXXWM474 }, - [NTC_LAST] = { }, + [NTC_B57330V2103] = { .name = "b57330v2103", .driver_data = TYPE_B57330V2103 }, + [NTC_B57891S0103] = { .name = "b57891s0103", .driver_data = TYPE_B57891S0103 }, + [NTC_NCP03WB473] = { .name = "ncp03wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP03WF104] = { .name = "ncp03wf104", .driver_data = TYPE_NCPXXWF104 }, + [NTC_NCP15WB473] = { .name = "ncp15wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP15WL333] = { .name = "ncp15wl333", .driver_data = TYPE_NCPXXWL333 }, + [NTC_NCP15XH103] = { .name = "ncp15xh103", .driver_data = TYPE_NCPXXXH103 }, + [NTC_NCP18WB473] = { .name = "ncp18wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP21WB473] = { .name = "ncp21wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_SSG1404001221] = { .name = "ssg1404_001221", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP18WM474] = { .name = "ncp18wm474", .driver_data = TYPE_NCPXXWM474 }, + [NTC_LAST] = { } }; MODULE_DEVICE_TABLE(platform, ntc_thermistor_id); diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index 167d2fe4d543..c0220ebfd4b6 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -285,7 +285,7 @@ static int pcf8591_read_channel(struct device *dev, int channel) } static const struct i2c_device_id pcf8591_id[] = { - { "pcf8591" }, + { .name = "pcf8591" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8591_id); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 8f4bff375ecb..c8cda160b5f8 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -113,6 +113,15 @@ config SENSORS_CRPS This driver can also be built as a module. If so, the module will be called crps. +config SENSORS_D1U74T + tristate "Murata D1U74T Power Supply" + help + If you say yes here you get hardware monitoring support for the Murata + D1U74T Power Supply. + + This driver can also be built as a module. If so, the module will + be called d1u74t. + config SENSORS_DELTA_AHE50DC_FAN tristate "Delta AHE-50DC fan control module" help @@ -161,6 +170,15 @@ config SENSORS_DPS920AB This driver can also be built as a module. If so, the module will be called dps920ab. +config SENSORS_E50SN12051 + tristate "Delta E50SN12051 Power Modules" + help + If you say yes here you get hardware monitoring support for Delta + E50SN12051 Power Modules. + + This driver can also be built as a module. If so, the module will + be called e50sn12051. + config SENSORS_INA233 tristate "Texas Instruments INA233 and compatibles" help @@ -311,6 +329,16 @@ config SENSORS_LTC4286 If you say yes here you get hardware monitoring support for Analog Devices LTC4286. +config SENSORS_LX1308 + tristate "Luxshare LX1308 DC/DC Power Module" + help + If you say yes here you get hardware monitoring support for + Luxshare LX1308, a high-efficiency 12V 860W DC/DC power module + with PMBus interface. + + This driver can also be built as a module. If so, the module will + be called lx1308. + config SENSORS_MAX15301 tristate "Maxim MAX15301" help @@ -365,6 +393,34 @@ config SENSORS_MAX20751 This driver can also be built as a module. If so, the module will be called max20751. +config SENSORS_MAX20830 + tristate "Analog Devices MAX20830" + help + If you say yes here you get hardware monitoring support for Analog + Devices MAX20830. + + This driver can also be built as a module. If so, the module will + be called max20830. + +config SENSORS_MAX20860A + tristate "Analog Devices MAX20860A" + help + If you say yes here you get hardware monitoring support for Analog + Devices MAX20860A step-down converter. + + This driver can also be built as a module. If so, the module will + be called max20860a. + +config SENSORS_MAX20860A_REGULATOR + bool "Regulator support for MAX20860A" + depends on SENSORS_MAX20860A && REGULATOR + help + If you say yes here you get regulator support for Analog Devices + MAX20860A step-down converter. + + This enables the MAX20860A to be used as a regulator device, + providing voltage control through the regulator framework. + config SENSORS_MAX31785 tristate "Maxim MAX31785 and compatibles" help @@ -456,6 +512,15 @@ config SENSORS_MP2975 This driver can also be built as a module. If so, the module will be called mp2975. +config SENSORS_MP2985 + tristate "MPS MP2985" + help + If you say yes here you get hardware monitoring support for MPS + MP2985 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2985. + config SENSORS_MP2993 tristate "MPS MP2993" help @@ -715,7 +780,7 @@ config SENSORS_XDP720 tristate "Infineon XDP720 family" help If you say yes here you get hardware monitoring support for Infineon - XDP720. + XDP720 and XDP730 Digital eFuse Controllers. This driver can also be built as a module. If so, the module will be called xdp720. diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 7129b62bc00f..ffc05f493213 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_HAC300S) += hac300s.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o +obj-$(CONFIG_SENSORS_E50SN12051) += e50sn12051.o obj-$(CONFIG_SENSORS_INA233) += ina233.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o @@ -30,12 +31,15 @@ obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o obj-$(CONFIG_SENSORS_LTC4286) += ltc4286.o +obj-$(CONFIG_SENSORS_LX1308) += lx1308.o obj-$(CONFIG_SENSORS_MAX15301) += max15301.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX16601) += max16601.o obj-$(CONFIG_SENSORS_MAX17616) += max17616.o obj-$(CONFIG_SENSORS_MAX20730) += max20730.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o +obj-$(CONFIG_SENSORS_MAX20830) += max20830.o +obj-$(CONFIG_SENSORS_MAX20860A) += max20860a.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o @@ -46,6 +50,7 @@ obj-$(CONFIG_SENSORS_MP2891) += mp2891.o obj-$(CONFIG_SENSORS_MP2925) += mp2925.o obj-$(CONFIG_SENSORS_MP29502) += mp29502.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o +obj-$(CONFIG_SENSORS_MP2985) += mp2985.o obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o obj-$(CONFIG_SENSORS_MP5920) += mp5920.o @@ -76,3 +81,4 @@ obj-$(CONFIG_SENSORS_XDPE1A2G7B) += xdpe1a2g7b.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o obj-$(CONFIG_SENSORS_CRPS) += crps.o +obj-$(CONFIG_SENSORS_D1U74T) += d1u74t.o diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c index 9f07fb4abaff..d283005d92ae 100644 --- a/drivers/hwmon/pmbus/acbel-fsg032.c +++ b/drivers/hwmon/pmbus/acbel-fsg032.c @@ -49,8 +49,8 @@ static void acbel_fsg032_init_debugfs(struct i2c_client *client) } static const struct i2c_device_id acbel_fsg032_id[] = { - { "acbel_fsg032" }, - {} + { .name = "acbel_fsg032" }, + { } }; static struct pmbus_driver_info acbel_fsg032_info = { diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 6f6ad7b20e9a..22ccb721dd51 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -21,12 +21,14 @@ #include <linux/slab.h> #include <linux/timekeeping.h> +#define ADM1266_IC_DEVICE_REV 0xAE #define ADM1266_BLACKBOX_CONFIG 0xD3 #define ADM1266_PDIO_CONFIG 0xD4 #define ADM1266_READ_STATE 0xD9 #define ADM1266_READ_BLACKBOX 0xDE #define ADM1266_SET_RTC 0xDF #define ADM1266_GPIO_CONFIG 0xE1 +#define ADM1266_POWERUP_COUNTER 0xE4 #define ADM1266_BLACKBOX_INFO 0xE6 #define ADM1266_PDIO_STATUS 0xE9 #define ADM1266_GPIO_STATUS 0xEA @@ -345,6 +347,154 @@ static int adm1266_state_read(struct seq_file *s, void *pdata) return 0; } +/* + * IC_DEVICE_REV (0xAE) returns an 8-byte block (datasheet Rev. D, Table 80): + * [2:0] firmware revision major.minor.patch + * [5:3] bootloader revision major.minor.patch + * [7:6] silicon revision two ASCII characters + */ +static int adm1266_firmware_revision_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_IC_DEVICE_REV, buf); + if (ret < 0) + return ret; + if (ret < 3) + return -EIO; + + seq_printf(s, "%u.%u.%u\n", buf[0], buf[1], buf[2]); + + return 0; +} + +/* + * POWERUP_COUNTER (0xE4) is a 2-byte little-endian non-volatile counter + * that increments on every device power cycle (datasheet Rev. D, Table + * 93). It saturates at 65535 and cannot be reset by the host. Each + * blackbox record embeds the counter value at record time, so this live + * read is mainly useful for matching a record back to its boot. + */ +static int adm1266_powerup_counter_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_POWERUP_COUNTER, buf); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + seq_printf(s, "%u\n", buf[0] | (buf[1] << 8)); + + return 0; +} + +/* + * Clearing the blackbox is required when the device is configured in + * single-recording mode (BLACKBOX_CONFIG[0] = 0): once the 32-record + * buffer is full the device stops recording until cleared. + * + * The clear is issued as a 2-byte block-write to READ_BLACKBOX with + * payload {0xFE, 0x00} per the datasheet. READ_BLACKBOX is also used + * by adm1266_nvmem_read_blackbox() to walk records one at a time; + * both paths run under pmbus_lock so the clear cannot interleave + * mid-iteration and corrupt the read sequence. + */ +static ssize_t adm1266_clear_blackbox_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct i2c_client *client = file->private_data; + u8 payload[2] = { 0xFE, 0x00 }; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_write_block_data(client, ADM1266_READ_BLACKBOX, + sizeof(payload), payload); + if (ret < 0) + return ret; + + return count; +} + +static const struct file_operations adm1266_clear_blackbox_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = adm1266_clear_blackbox_write, + .llseek = noop_llseek, +}; + +/* + * SET_RTC (0xDF) is a 6-byte block (datasheet Rev. D, Table 84): + * bytes [1:0] - fractional seconds (1/65536 s, written as zero) + * bytes [5:2] - seconds since 1970-01-01 UTC, little-endian + * + * The driver seeds it once at probe via adm1266_rtc_set(). Over a + * long uptime the chip's counter drifts away from host wall-clock, + * so expose it via debugfs: + * + * read -- returns the chip's current seconds counter, which lets + * userspace observe host-vs-chip drift. + * write -- the kernel re-reads ktime_get_real_seconds() and writes + * it to SET_RTC. The write payload is ignored; userspace + * does not get to supply its own timestamp value, so + * there is no way to push a wrong time into the chip. + * + * A small userspace agent (chrony hook, systemd-timesyncd script, + * or a periodic cron job) can write to this file to keep the + * timestamp embedded in each blackbox record aligned with + * wall-clock across long uptimes. + */ +static int adm1266_rtc_get(void *data, u64 *val) +{ + struct i2c_client *client = data; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + u32 seconds = 0; + int ret, i; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_SET_RTC, buf); + if (ret < 0) + return ret; + if (ret < 6) + return -EIO; + + for (i = 0; i < 4; i++) + seconds |= (u32)buf[2 + i] << (i * 8); + + *val = seconds; + + return 0; +} + +static int adm1266_rtc_set(void *data, u64 val) +{ + struct i2c_client *client = data; + time64_t kt = ktime_get_real_seconds(); + u8 write_buf[6] = { 0 }; + int i; + + /* User-supplied @val is ignored on purpose; the kernel owns the + * time source so userspace cannot push a wrong value into the chip. + */ + for (i = 0; i < 4; i++) + write_buf[2 + i] = (kt >> (i * 8)) & 0xFF; + + guard(pmbus_lock)(client); + return i2c_smbus_write_block_data(client, ADM1266_SET_RTC, + sizeof(write_buf), write_buf); +} +DEFINE_DEBUGFS_ATTRIBUTE(adm1266_rtc_fops, + adm1266_rtc_get, adm1266_rtc_set, "%llu\n"); + static void adm1266_init_debugfs(struct adm1266_data *data) { struct dentry *root; @@ -357,6 +507,14 @@ static void adm1266_init_debugfs(struct adm1266_data *data) debugfs_create_devm_seqfile(&data->client->dev, "sequencer_state", data->debugfs_dir, adm1266_state_read); + debugfs_create_devm_seqfile(&data->client->dev, "firmware_revision", data->debugfs_dir, + adm1266_firmware_revision_read); + debugfs_create_devm_seqfile(&data->client->dev, "powerup_counter", data->debugfs_dir, + adm1266_powerup_counter_read); + debugfs_create_file("clear_blackbox", 0200, data->debugfs_dir, data->client, + &adm1266_clear_blackbox_fops); + debugfs_create_file("rtc", 0600, data->debugfs_dir, data->client, + &adm1266_rtc_fops); } static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) @@ -446,23 +604,6 @@ static int adm1266_config_nvmem(struct adm1266_data *data) return 0; } -static int adm1266_set_rtc(struct adm1266_data *data) -{ - time64_t kt; - char write_buf[6]; - int i; - - kt = ktime_get_real_seconds(); - - memset(write_buf, 0, sizeof(write_buf)); - - for (i = 0; i < 4; i++) - write_buf[2 + i] = (kt >> (i * 8)) & 0xFF; - - return i2c_smbus_write_block_data(data->client, ADM1266_SET_RTC, sizeof(write_buf), - write_buf); -} - static int adm1266_probe(struct i2c_client *client) { struct adm1266_data *data; @@ -482,14 +623,14 @@ static int adm1266_probe(struct i2c_client *client) crc8_populate_msb(pmbus_crc_table, 0x7); mutex_init(&data->buf_mutex); - ret = adm1266_set_rtc(data); - if (ret < 0) - return ret; - ret = pmbus_do_probe(client, &data->info); if (ret) return ret; + ret = adm1266_rtc_set(client, 0); + if (ret < 0) + return ret; + ret = adm1266_config_nvmem(data); if (ret < 0) return ret; @@ -510,7 +651,7 @@ static const struct of_device_id adm1266_of_match[] = { MODULE_DEVICE_TABLE(of, adm1266_of_match); static const struct i2c_device_id adm1266_id[] = { - { "adm1266" }, + { .name = "adm1266" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1266_id); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index bc2a6a07dc3e..2e5963fb1e12 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -478,16 +478,16 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) } static const struct i2c_device_id adm1275_id[] = { - { "adm1075", adm1075 }, - { "adm1272", adm1272 }, - { "adm1273", adm1273 }, - { "adm1275", adm1275 }, - { "adm1276", adm1276 }, - { "adm1278", adm1278 }, - { "adm1281", adm1281 }, - { "adm1293", adm1293 }, - { "adm1294", adm1294 }, - { "mc09c", sq24905c }, + { .name = "adm1075", .driver_data = adm1075 }, + { .name = "adm1272", .driver_data = adm1272 }, + { .name = "adm1273", .driver_data = adm1273 }, + { .name = "adm1275", .driver_data = adm1275 }, + { .name = "adm1276", .driver_data = adm1276 }, + { .name = "adm1278", .driver_data = adm1278 }, + { .name = "adm1281", .driver_data = adm1281 }, + { .name = "adm1293", .driver_data = adm1293 }, + { .name = "adm1294", .driver_data = adm1294 }, + { .name = "mc09c", .driver_data = sq24905c }, { } }; MODULE_DEVICE_TABLE(i2c, adm1275_id); diff --git a/drivers/hwmon/pmbus/aps-379.c b/drivers/hwmon/pmbus/aps-379.c index 7d46cd647e20..3ec0940ae564 100644 --- a/drivers/hwmon/pmbus/aps-379.c +++ b/drivers/hwmon/pmbus/aps-379.c @@ -100,8 +100,8 @@ static struct pmbus_driver_info aps_379_info = { }; static const struct i2c_device_id aps_379_id[] = { - { "aps-379", 0 }, - {}, + { .name = "aps-379" }, + { } }; MODULE_DEVICE_TABLE(i2c, aps_379_id); diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index 6499556f735b..9e3dc9d29c56 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -106,9 +106,9 @@ static int pfe_pmbus_probe(struct i2c_client *client) } static const struct i2c_device_id pfe_device_id[] = { - {"pfe1100", pfe1100}, - {"pfe3000", pfe3000}, - {} + { .name = "pfe1100", .driver_data = pfe1100 }, + { .name = "pfe3000", .driver_data = pfe3000 }, + { } }; MODULE_DEVICE_TABLE(i2c, pfe_device_id); diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c index 6c3875ba37a0..e364dcb59dd4 100644 --- a/drivers/hwmon/pmbus/bpa-rs600.c +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -147,9 +147,9 @@ static struct pmbus_driver_info bpa_rs600_info = { }; static const struct i2c_device_id bpa_rs600_id[] = { - { "bpa-rs600", bpa_rs600 }, - { "bpd-rs600", bpd_rs600 }, - {}, + { .name = "bpa-rs600", .driver_data = bpa_rs600 }, + { .name = "bpd-rs600", .driver_data = bpd_rs600 }, + { } }; MODULE_DEVICE_TABLE(i2c, bpa_rs600_id); diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c index 164b33fed312..266ec8947519 100644 --- a/drivers/hwmon/pmbus/crps.c +++ b/drivers/hwmon/pmbus/crps.c @@ -10,8 +10,8 @@ #include "pmbus.h" static const struct i2c_device_id crps_id[] = { - { "intel_crps185" }, - {} + { .name = "intel_crps185" }, + { } }; MODULE_DEVICE_TABLE(i2c, crps_id); diff --git a/drivers/hwmon/pmbus/d1u74t.c b/drivers/hwmon/pmbus/d1u74t.c new file mode 100644 index 000000000000..752df3af9c1a --- /dev/null +++ b/drivers/hwmon/pmbus/d1u74t.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2026 Nexthop Systems. + */ + +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include <linux/string.h> + +#include "pmbus.h" + +static const struct i2c_device_id d1u74t_id[] = { + { "d1u74t" }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, d1u74t_id); + +static struct pmbus_driver_info d1u74t_info = { + .pages = 1, + /* PSU uses default linear data format. */ + .func[0] = PMBUS_HAVE_PIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_FAN12 | + PMBUS_HAVE_STATUS_FAN12, +}; + +static int d1u74t_probe(struct i2c_client *client) +{ + char buf[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + struct device *dev = &client->dev; + int rc; + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (rc < 0) + return dev_err_probe(dev, rc, "Failed to read PMBUS_MFR_ID\n"); + + if (rc != 9 || strncmp(buf, "Murata-PS", 9)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, + "Unsupported Manufacturer ID '%s'\n", + buf); + } + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rc < 0) + return dev_err_probe(dev, rc, + "Failed to read PMBUS_MFR_MODEL\n"); + + if (rc < 8 || strncmp(buf, "D1U74T-W", 8)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, "Model '%s' not supported\n", + buf); + } + + rc = pmbus_do_probe(client, &d1u74t_info); + if (rc) + return dev_err_probe(dev, rc, "Failed to probe\n"); + + return 0; +} + +static const struct of_device_id d1u74t_of_match[] = { + { + .compatible = "murata,d1u74t", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, d1u74t_of_match); + +static struct i2c_driver d1u74t_driver = { + .driver = { + .name = "d1u74t", + .of_match_table = d1u74t_of_match, + }, + .probe = d1u74t_probe, + .id_table = d1u74t_id, +}; + +module_i2c_driver(d1u74t_driver); + +MODULE_AUTHOR("Abdurrahman Hussain"); +MODULE_DESCRIPTION("PMBus driver for Murata D1U74T-W power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c index 3850eaea75da..2df655b20ea5 100644 --- a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c +++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c @@ -103,7 +103,7 @@ static int ahe50dc_fan_probe(struct i2c_client *client) } static const struct i2c_device_id ahe50dc_fan_id[] = { - { "ahe50dc_fan" }, + { .name = "ahe50dc_fan" }, { } }; MODULE_DEVICE_TABLE(i2c, ahe50dc_fan_id); diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c index 325111a955e6..0d2901c314c2 100644 --- a/drivers/hwmon/pmbus/dps920ab.c +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -191,8 +191,8 @@ static const struct of_device_id __maybe_unused dps920ab_of_match[] = { MODULE_DEVICE_TABLE(of, dps920ab_of_match); static const struct i2c_device_id dps920ab_device_id[] = { - { "dps920ab" }, - {} + { .name = "dps920ab" }, + { } }; MODULE_DEVICE_TABLE(i2c, dps920ab_device_id); diff --git a/drivers/hwmon/pmbus/e50sn12051.c b/drivers/hwmon/pmbus/e50sn12051.c new file mode 100644 index 000000000000..efb4d62b2603 --- /dev/null +++ b/drivers/hwmon/pmbus/e50sn12051.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for E50SN12051 + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include "pmbus.h" + +static struct pmbus_driver_info e50sn12051_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, +}; + +static const struct i2c_device_id e50sn12051_id[] = { { "e50sn12051", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, e50sn12051_id); + +static const struct of_device_id e50sn12051_of_match[] = { + { .compatible = "delta,e50sn12051" }, + {}, +}; +MODULE_DEVICE_TABLE(of, e50sn12051_of_match); + +static int e50sn12051_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &e50sn12051_info); +} + +static struct i2c_driver e50sn12051_driver = { + .driver = { + .name = "e50sn12051", + .of_match_table = e50sn12051_of_match, + }, + .probe = e50sn12051_probe, + + .id_table = e50sn12051_id, +}; + +module_i2c_driver(e50sn12051_driver); + +MODULE_AUTHOR("Kevin Chang <kevin.chang2@amd.com>"); +MODULE_DESCRIPTION("PMBus driver for E50SN12051"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c index cad4d2330003..44cf2db9364b 100644 --- a/drivers/hwmon/pmbus/fsp-3y.c +++ b/drivers/hwmon/pmbus/fsp-3y.c @@ -271,8 +271,8 @@ static int fsp3y_probe(struct i2c_client *client) } static const struct i2c_device_id fsp3y_id[] = { - {"ym2151e", ym2151e}, - {"yh5151e", yh5151e}, + { .name = "ym2151e", .driver_data = ym2151e }, + { .name = "yh5151e", .driver_data = yh5151e }, { } }; MODULE_DEVICE_TABLE(i2c, fsp3y_id); diff --git a/drivers/hwmon/pmbus/hac300s.c b/drivers/hwmon/pmbus/hac300s.c index a073db1cfe2e..761e53890ebc 100644 --- a/drivers/hwmon/pmbus/hac300s.c +++ b/drivers/hwmon/pmbus/hac300s.c @@ -112,8 +112,8 @@ static const struct of_device_id hac300s_of_match[] = { MODULE_DEVICE_TABLE(of, hac300s_of_match); static const struct i2c_device_id hac300s_id[] = { - {"hac300s", 0}, - {} + { .name = "hac300s" }, + { } }; MODULE_DEVICE_TABLE(i2c, hac300s_id); diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 6c7256d997f4..aad94bcb9ceb 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -472,10 +472,10 @@ static struct pmbus_platform_data ibm_cffps_pdata = { }; static const struct i2c_device_id ibm_cffps_id[] = { - { "ibm_cffps1", cffps1 }, - { "ibm_cffps2", cffps2 }, - { "ibm_cffps", cffps_unknown }, - {} + { .name = "ibm_cffps1", .driver_data = cffps1 }, + { .name = "ibm_cffps2", .driver_data = cffps2 }, + { .name = "ibm_cffps", .driver_data = cffps_unknown }, + { } }; MODULE_DEVICE_TABLE(i2c, ibm_cffps_id); diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c index 652087589c55..c72aa258b7c6 100644 --- a/drivers/hwmon/pmbus/ina233.c +++ b/drivers/hwmon/pmbus/ina233.c @@ -168,8 +168,8 @@ static int ina233_probe(struct i2c_client *client) } static const struct i2c_device_id ina233_id[] = { - {"ina233", 0}, - {} + { .name = "ina233" }, + { } }; MODULE_DEVICE_TABLE(i2c, ina233_id); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c index 074e0f164ee1..57c0fc16909c 100644 --- a/drivers/hwmon/pmbus/inspur-ipsps.c +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -197,8 +197,8 @@ static int ipsps_probe(struct i2c_client *client) } static const struct i2c_device_id ipsps_id[] = { - { "ipsps1" }, - {} + { .name = "ipsps1" }, + { } }; MODULE_DEVICE_TABLE(i2c, ipsps_id); diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index 46d8f334d49a..b2120fc76f40 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -126,8 +126,8 @@ static int ir35221_probe(struct i2c_client *client) } static const struct i2c_device_id ir35221_id[] = { - {"ir35221"}, - {} + { .name = "ir35221" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir35221_id); diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c index 34ce15fc708b..0dce4c3f666f 100644 --- a/drivers/hwmon/pmbus/ir36021.c +++ b/drivers/hwmon/pmbus/ir36021.c @@ -51,8 +51,8 @@ static int ir36021_probe(struct i2c_client *client) } static const struct i2c_device_id ir36021_id[] = { - { "ir36021" }, - {}, + { .name = "ir36021" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir36021_id); diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c index 7b4188e8bf48..47ce88e9d13a 100644 --- a/drivers/hwmon/pmbus/ir38064.c +++ b/drivers/hwmon/pmbus/ir38064.c @@ -53,11 +53,11 @@ static int ir38064_probe(struct i2c_client *client) } static const struct i2c_device_id ir38064_id[] = { - {"ir38060"}, - {"ir38064"}, - {"ir38164"}, - {"ir38263"}, - {} + { .name = "ir38060" }, + { .name = "ir38064" }, + { .name = "ir38164" }, + { .name = "ir38263" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir38064_id); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c index 43674c64841d..1694b96d7abf 100644 --- a/drivers/hwmon/pmbus/irps5401.c +++ b/drivers/hwmon/pmbus/irps5401.c @@ -44,8 +44,8 @@ static int irps5401_probe(struct i2c_client *client) } static const struct i2c_device_id irps5401_id[] = { - {"irps5401"}, - {} + { .name = "irps5401" }, + { } }; MODULE_DEVICE_TABLE(i2c, irps5401_id); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 21d047b577a4..2f7f825bfb69 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -409,54 +409,54 @@ static int isl68137_probe(struct i2c_client *client) } static const struct i2c_device_id raa_dmpvr_id[] = { - {"isl68137", raa_dmpvr1_2rail}, - {"isl68220", raa_dmpvr2_2rail}, - {"isl68221", raa_dmpvr2_3rail}, - {"isl68222", raa_dmpvr2_2rail}, - {"isl68223", raa_dmpvr2_2rail}, - {"isl68224", raa_dmpvr2_3rail}, - {"isl68225", raa_dmpvr2_2rail}, - {"isl68226", raa_dmpvr2_3rail}, - {"isl68227", raa_dmpvr2_1rail}, - {"isl68229", raa_dmpvr2_3rail}, - {"isl68233", raa_dmpvr2_2rail}, - {"isl68239", raa_dmpvr2_3rail}, - - {"isl69222", raa_dmpvr2_2rail}, - {"isl69223", raa_dmpvr2_3rail}, - {"isl69224", raa_dmpvr2_2rail}, - {"isl69225", raa_dmpvr2_2rail}, - {"isl69227", raa_dmpvr2_3rail}, - {"isl69228", raa_dmpvr2_3rail}, - {"isl69234", raa_dmpvr2_2rail}, - {"isl69236", raa_dmpvr2_2rail}, - {"isl69239", raa_dmpvr2_3rail}, - {"isl69242", raa_dmpvr2_2rail}, - {"isl69243", raa_dmpvr2_1rail}, - {"isl69247", raa_dmpvr2_2rail}, - {"isl69248", raa_dmpvr2_2rail}, - {"isl69254", raa_dmpvr2_2rail}, - {"isl69255", raa_dmpvr2_2rail}, - {"isl69256", raa_dmpvr2_2rail}, - {"isl69259", raa_dmpvr2_2rail}, - {"isl69260", raa_dmpvr2_2rail}, - {"isl69268", raa_dmpvr2_2rail}, - {"isl69269", raa_dmpvr2_3rail}, - {"isl69298", raa_dmpvr2_2rail}, - - {"raa228000", raa_dmpvr2_hv}, - {"raa228004", raa_dmpvr2_hv}, - {"raa228006", raa_dmpvr2_hv}, - {"raa228228", raa_dmpvr2_2rail_nontc}, - {"raa228244", raa_dmpvr2_2rail_nontc}, - {"raa228246", raa_dmpvr2_2rail_nontc}, - {"raa228942", raa_dmpvr2_2rail_nontc}, - {"raa228943", raa_dmpvr2_2rail_nontc}, - {"raa229001", raa_dmpvr2_2rail}, - {"raa229004", raa_dmpvr2_2rail}, - {"raa229141", raa_dmpvr2_2rail_pmbus}, - {"raa229621", raa_dmpvr2_2rail}, - {} + { .name = "isl68137", .driver_data = raa_dmpvr1_2rail }, + { .name = "isl68220", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68221", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68222", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68223", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68224", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68225", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68226", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68227", .driver_data = raa_dmpvr2_1rail }, + { .name = "isl68229", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68233", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68239", .driver_data = raa_dmpvr2_3rail }, + + { .name = "isl69222", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69223", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69224", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69225", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69227", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69228", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69234", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69236", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69239", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69242", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69243", .driver_data = raa_dmpvr2_1rail }, + { .name = "isl69247", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69248", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69254", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69255", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69256", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69259", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69260", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69268", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69269", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69298", .driver_data = raa_dmpvr2_2rail }, + + { .name = "raa228000", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228004", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228006", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228228", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228244", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228246", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228942", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228943", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa229001", .driver_data = raa_dmpvr2_2rail }, + { .name = "raa229004", .driver_data = raa_dmpvr2_2rail }, + { .name = "raa229141", .driver_data = raa_dmpvr2_2rail_pmbus }, + { .name = "raa229621", .driver_data = raa_dmpvr2_2rail }, + { } }; MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index dd7275a67a0a..2be50cf1bbde 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -132,23 +132,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 10742, - .b = 1552, + .m = 5456, + .b = 2118, .R = -2, }, [PSC_CURRENT_IN_L] = { - .m = 5456, - .b = 2118, + .m = 10742, + .b = 1552, .R = -2, }, [PSC_POWER] = { - .m = 1204, - .b = 8524, + .m = 612, + .b = 11202, .R = -3, }, [PSC_POWER_L] = { - .m = 612, - .b = 11202, + .m = 1204, + .b = 8524, .R = -3, }, [PSC_TEMPERATURE] = { @@ -167,23 +167,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 10753, - .b = -1200, + .m = 5405, + .b = -600, .R = -2, }, [PSC_CURRENT_IN_L] = { - .m = 5405, - .b = -600, + .m = 10753, + .b = -1200, .R = -2, }, [PSC_POWER] = { - .m = 1204, - .b = -6000, + .m = 605, + .b = -8000, .R = -3, }, [PSC_POWER_L] = { - .m = 605, - .b = -8000, + .m = 1204, + .b = -6000, .R = -3, }, [PSC_TEMPERATURE] = { @@ -202,23 +202,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 15076, - .b = -504, + .m = 7645, + .b = 100, .R = -2, }, [PSC_CURRENT_IN_L] = { - .m = 7645, - .b = 100, + .m = 15076, + .b = -504, .R = -2, }, [PSC_POWER] = { - .m = 1701, - .b = -4000, + .m = 861, + .b = -965, .R = -3, }, [PSC_POWER_L] = { - .m = 861, - .b = -965, + .m = 1701, + .b = -4000, .R = -3, }, [PSC_TEMPERATURE] = { @@ -442,11 +442,11 @@ static const struct regulator_desc lm25066_reg_desc[] = { #endif static const struct i2c_device_id lm25066_id[] = { - {"lm25056", lm25056}, - {"lm25066", lm25066}, - {"lm5064", lm5064}, - {"lm5066", lm5066}, - {"lm5066i", lm5066i}, + { .name = "lm25056", .driver_data = lm25056 }, + { .name = "lm25066", .driver_data = lm25066 }, + { .name = "lm5064", .driver_data = lm5064 }, + { .name = "lm5066", .driver_data = lm5066 }, + { .name = "lm5066i", .driver_data = lm5066i }, { } }; MODULE_DEVICE_TABLE(i2c, lm25066_id); @@ -519,18 +519,20 @@ static int lm25066_probe(struct i2c_client *client) info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m; info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b; info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R; - info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; - info->R[PSC_POWER] = coeff[PSC_POWER].R; if (config & LM25066_DEV_SETUP_CL) { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m; info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].R; info->m[PSC_POWER] = coeff[PSC_POWER_L].m; info->b[PSC_POWER] = coeff[PSC_POWER_L].b; + info->R[PSC_POWER] = coeff[PSC_POWER_L].R; } else { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m; info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; info->m[PSC_POWER] = coeff[PSC_POWER].m; info->b[PSC_POWER] = coeff[PSC_POWER].b; + info->R[PSC_POWER] = coeff[PSC_POWER].R; } /* diff --git a/drivers/hwmon/pmbus/lt3074.c b/drivers/hwmon/pmbus/lt3074.c index 3704dbe7b54a..ed932ddb4f77 100644 --- a/drivers/hwmon/pmbus/lt3074.c +++ b/drivers/hwmon/pmbus/lt3074.c @@ -95,8 +95,8 @@ static int lt3074_probe(struct i2c_client *client) } static const struct i2c_device_id lt3074_id[] = { - { "lt3074", 0 }, - {} + { .name = "lt3074" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt3074_id); diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c index 9d6d50f39bd6..f8971a786f25 100644 --- a/drivers/hwmon/pmbus/lt7182s.c +++ b/drivers/hwmon/pmbus/lt7182s.c @@ -168,8 +168,8 @@ static int lt7182s_probe(struct i2c_client *client) } static const struct i2c_device_id lt7182s_id[] = { - { "lt7182s" }, - {} + { .name = "lt7182s" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt7182s_id); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index d69a5e675e80..10877b0867fd 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -538,36 +538,36 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { - {"lt7170", lt7170}, - {"lt7171", lt7171}, - {"ltc2972", ltc2972}, - {"ltc2974", ltc2974}, - {"ltc2975", ltc2975}, - {"ltc2977", ltc2977}, - {"ltc2978", ltc2978}, - {"ltc2979", ltc2979}, - {"ltc2980", ltc2980}, - {"ltc3880", ltc3880}, - {"ltc3882", ltc3882}, - {"ltc3883", ltc3883}, - {"ltc3884", ltc3884}, - {"ltc3886", ltc3886}, - {"ltc3887", ltc3887}, - {"ltc3889", ltc3889}, - {"ltc7132", ltc7132}, - {"ltc7841", ltc7841}, - {"ltc7880", ltc7880}, - {"ltm2987", ltm2987}, - {"ltm4664", ltm4664}, - {"ltm4673", ltm4673}, - {"ltm4675", ltm4675}, - {"ltm4676", ltm4676}, - {"ltm4677", ltm4677}, - {"ltm4678", ltm4678}, - {"ltm4680", ltm4680}, - {"ltm4686", ltm4686}, - {"ltm4700", ltm4700}, - {} + { .name = "lt7170", .driver_data = lt7170 }, + { .name = "lt7171", .driver_data = lt7171 }, + { .name = "ltc2972", .driver_data = ltc2972 }, + { .name = "ltc2974", .driver_data = ltc2974 }, + { .name = "ltc2975", .driver_data = ltc2975 }, + { .name = "ltc2977", .driver_data = ltc2977 }, + { .name = "ltc2978", .driver_data = ltc2978 }, + { .name = "ltc2979", .driver_data = ltc2979 }, + { .name = "ltc2980", .driver_data = ltc2980 }, + { .name = "ltc3880", .driver_data = ltc3880 }, + { .name = "ltc3882", .driver_data = ltc3882 }, + { .name = "ltc3883", .driver_data = ltc3883 }, + { .name = "ltc3884", .driver_data = ltc3884 }, + { .name = "ltc3886", .driver_data = ltc3886 }, + { .name = "ltc3887", .driver_data = ltc3887 }, + { .name = "ltc3889", .driver_data = ltc3889 }, + { .name = "ltc7132", .driver_data = ltc7132 }, + { .name = "ltc7841", .driver_data = ltc7841 }, + { .name = "ltc7880", .driver_data = ltc7880 }, + { .name = "ltm2987", .driver_data = ltm2987 }, + { .name = "ltm4664", .driver_data = ltm4664 }, + { .name = "ltm4673", .driver_data = ltm4673 }, + { .name = "ltm4675", .driver_data = ltm4675 }, + { .name = "ltm4676", .driver_data = ltm4676 }, + { .name = "ltm4677", .driver_data = ltm4677 }, + { .name = "ltm4678", .driver_data = ltm4678 }, + { .name = "ltm4680", .driver_data = ltm4680 }, + { .name = "ltm4686", .driver_data = ltm4686 }, + { .name = "ltm4700", .driver_data = ltm4700 }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index 824c16a75e2c..0219d03adb03 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -143,7 +143,7 @@ static int ltc3815_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc3815_id[] = { - {"ltc3815"}, + { .name = "ltc3815" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc3815_id); diff --git a/drivers/hwmon/pmbus/lx1308.c b/drivers/hwmon/pmbus/lx1308.c new file mode 100644 index 000000000000..0c453393796b --- /dev/null +++ b/drivers/hwmon/pmbus/lx1308.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define LX1308_MFR_IOUT_OCP3_FAULT 0xBE +#define LX1308_MFR_IOUT_OCP3_WARN 0xBF + +/* + * Decode a Linear11-encoded word to an integer value. + * Linear11 format: bits[15:11] = signed 5-bit exponent, + * bits[10:0] = signed 11-bit mantissa. Result = mant * 2^exp. + */ +static inline int linear11_to_int(u16 word) +{ + s16 exp = ((s16)word) >> 11; + s16 mant = ((s16)((word & 0x7ff) << 5)) >> 5; + + return (exp >= 0) ? (int)((u32)mant << exp) : (mant >> -exp); +} + +static int lx1308_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + /* + * The LX1308 OCP3 registers (slow OCP, 60ms delay) use a + * manufacturer-specific U8.0 format. Read the byte value N and present + * it as a Linear11 word with exponent 0. + */ + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT); + if (ret < 0) + break; + ret &= 0x7FF; + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN); + if (ret < 0) + break; + ret &= 0x7FF; + break; + + /* + * The following registers are not implemented by the LX1308. Return + * -ENXIO to suppress the corresponding sysfs attributes. + */ + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_PIN_MAX: + case PMBUS_MFR_POUT_MAX: + case PMBUS_MFR_MAX_TEMP_1: + ret = -ENXIO; + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int lx1308_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_IOUT_OC_FAULT_LIMIT: + /* + * Decode Linear11 word from pmbus_core back to a plain integer + * and write as the U8.0 byte the device expects. + */ + ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT, + clamp_val(linear11_to_int(word), 0, 255)); + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN, + clamp_val(linear11_to_int(word), 0, 255)); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static struct pmbus_driver_info lx1308_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_STATUS_INPUT, + + .read_word_data = lx1308_read_word_data, + .write_word_data = lx1308_write_word_data, +}; + +static const struct of_device_id lx1308_of_match[] = { + { .compatible = "luxshare,lx1308" }, + { } +}; + +MODULE_DEVICE_TABLE(of, lx1308_of_match); + +static const struct i2c_device_id lx1308_id[] = { + { "lx1308" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lx1308_id); + +static int lx1308_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + int ret; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read manufacturer id\n"); + buf[ret] = '\0'; + + if (ret != 12 || strncmp(buf, "LUXSHARE", 8)) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer ID '%s'\n", buf); + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Manufacturer Model\n"); + buf[ret] = '\0'; + + for (mid = lx1308_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, buf, strlen(mid->name))) + break; + } + if (!mid->name[0]) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer Model '%s'\n", buf); + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_REVISION, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Manufacturer Revision\n"); + buf[ret] = '\0'; + + if (ret != 12 || buf[0] != 'V') + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer Revision '%s'\n", buf); + return pmbus_do_probe(client, &lx1308_info); +} + +static struct i2c_driver lx1308_driver = { + .driver = { + .name = "lx1308", + .of_match_table = lx1308_of_match, + }, + .probe = lx1308_probe, + .id_table = lx1308_id, +}; + +module_i2c_driver(lx1308_driver); + +MODULE_AUTHOR("Brian Chiang <chiang.brian@inventec.com>"); +MODULE_DESCRIPTION("PMBus driver for Luxshare LX1308"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c index d5810b88ea8d..4c36f1ea27ee 100644 --- a/drivers/hwmon/pmbus/max15301.c +++ b/drivers/hwmon/pmbus/max15301.c @@ -23,10 +23,10 @@ #include "pmbus.h" static const struct i2c_device_id max15301_id[] = { - { "bmr461" }, - { "max15301" }, - { "max15303" }, - {} + { .name = "bmr461" }, + { .name = "max15301" }, + { .name = "max15303" }, + { } }; MODULE_DEVICE_TABLE(i2c, max15301_id); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index eb84915c2a83..351a1f535999 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -91,8 +91,8 @@ static int max16064_probe(struct i2c_client *client) } static const struct i2c_device_id max16064_id[] = { - {"max16064"}, - {} + { .name = "max16064" }, + { } }; MODULE_DEVICE_TABLE(i2c, max16064_id); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index 36dc13424d92..3dd1f6fd003b 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -263,11 +263,11 @@ static void max16601_remove(void *_data) } static const struct i2c_device_id max16601_id[] = { - {"max16508", max16508}, - {"max16600", max16600}, - {"max16601", max16601}, - {"max16602", max16602}, - {} + { .name = "max16508", .driver_data = max16508 }, + { .name = "max16600", .driver_data = max16600 }, + { .name = "max16601", .driver_data = max16601 }, + { .name = "max16602", .driver_data = max16602 }, + { } }; MODULE_DEVICE_TABLE(i2c, max16601_id); diff --git a/drivers/hwmon/pmbus/max17616.c b/drivers/hwmon/pmbus/max17616.c index 1d4a0ddb95bb..744fa5aefe93 100644 --- a/drivers/hwmon/pmbus/max17616.c +++ b/drivers/hwmon/pmbus/max17616.c @@ -46,7 +46,7 @@ static int max17616_probe(struct i2c_client *client) } static const struct i2c_device_id max17616_id[] = { - { "max17616" }, + { .name = "max17616" }, { } }; MODULE_DEVICE_TABLE(i2c, max17616_id); diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index fe03164788df..4031f894e6ae 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -751,11 +751,11 @@ static int max20730_probe(struct i2c_client *client) } static const struct i2c_device_id max20730_id[] = { - { "max20710", max20710 }, - { "max20730", max20730 }, - { "max20734", max20734 }, - { "max20743", max20743 }, - { }, + { .name = "max20710", .driver_data = max20710 }, + { .name = "max20730", .driver_data = max20730 }, + { .name = "max20734", .driver_data = max20734 }, + { .name = "max20743", .driver_data = max20743 }, + { } }; MODULE_DEVICE_TABLE(i2c, max20730_id); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index ac8c43122133..e85676433dca 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -32,8 +32,8 @@ static int max20751_probe(struct i2c_client *client) } static const struct i2c_device_id max20751_id[] = { - {"max20751"}, - {} + { .name = "max20751" }, + { } }; MODULE_DEVICE_TABLE(i2c, max20751_id); diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c new file mode 100644 index 000000000000..cb2c23672166 --- /dev/null +++ b/drivers/hwmon/pmbus/max20830.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Analog Devices MAX20830 + * + * Copyright (C) 2026 Analog Devices, Inc. + */ + +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/string.h> +#include "pmbus.h" + +#define MAX20830_IC_DEVICE_ID_LENGTH 9 + +static struct pmbus_driver_info max20830_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_TEMP | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, +}; + +static int max20830_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = {}; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA) && + !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -ENODEV; + + /* + * Use i2c_smbus_read_block_data() if supported, otherwise fall back + * to i2c_smbus_read_i2c_block_data() to support I2C controllers + * which do not support SMBus block reads. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + /* Reads 9 Data bytes from MAX20830 */ + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read IC_DEVICE_ID\n"); + } else { + /* Reads 1 length byte + 9 Data bytes from MAX20830 */ + ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, + MAX20830_IC_DEVICE_ID_LENGTH + 1, + buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read IC_DEVICE_ID\n"); + /* + * Moves data forward, removing the length byte, this is to + * match the format of i2c_smbus_read_block_data(). + * Also adjust return value to reflect length byte removal. + */ + memmove(buf, buf + 1, MAX20830_IC_DEVICE_ID_LENGTH); + ret = ret - 1; + } + + /* + * MAX20830 IC_DEVICE_ID sends string data "MAX20830\0". + * Return value should at least be 9 bytes of data. + */ + if (ret < MAX20830_IC_DEVICE_ID_LENGTH) + return dev_err_probe(&client->dev, -ENODEV, + "IC_DEVICE_ID too short: expected at least 9 bytes, got %d\n", + ret); + + /* 9 bytes of data, buf[0]-buf[7] = "MAX20830", buf[8] = '\0' */ + buf[MAX20830_IC_DEVICE_ID_LENGTH - 1] = '\0'; + if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1)) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported device: '%s'\n", buf); + + return pmbus_do_probe(client, &max20830_info); +} + +static const struct i2c_device_id max20830_id[] = { + {"max20830"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, max20830_id); + +static const struct of_device_id max20830_of_match[] = { + { .compatible = "adi,max20830" }, + { } +}; +MODULE_DEVICE_TABLE(of, max20830_of_match); + +static struct i2c_driver max20830_driver = { + .driver = { + .name = "max20830", + .of_match_table = max20830_of_match, + }, + .probe = max20830_probe, + .id_table = max20830_id, +}; + +module_i2c_driver(max20830_driver); + +MODULE_AUTHOR("Alexis Czezar Torreno <alexisczezar.torreno@analog.com>"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX20830"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max20860a.c b/drivers/hwmon/pmbus/max20860a.c new file mode 100644 index 000000000000..905f916f6c08 --- /dev/null +++ b/drivers/hwmon/pmbus/max20860a.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Analog Devices MAX20860A + * + * SPDX-FileCopyrightText: Copyright Hewlett Packard Enterprise Development LP + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regulator/driver.h> +#include "pmbus.h" + +#if IS_ENABLED(CONFIG_SENSORS_MAX20860A_REGULATOR) +static const struct regulator_desc max20860a_reg_desc[] = { + PMBUS_REGULATOR_ONE("vout"), +}; +#endif + +static struct pmbus_driver_info max20860a_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_INPUT, +#if IS_ENABLED(CONFIG_SENSORS_MAX20860A_REGULATOR) + .num_regulators = 1, + .reg_desc = max20860a_reg_desc, +#endif +}; + +static int max20860a_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &max20860a_info); +} + +static const struct i2c_device_id max20860a_id[] = { + {"max20860a"}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max20860a_id); + +static const struct of_device_id max20860a_of_match[] = { + { .compatible = "adi,max20860a" }, + {} +}; +MODULE_DEVICE_TABLE(of, max20860a_of_match); + +static struct i2c_driver max20860a_driver = { + .driver = { + .name = "max20860a", + .of_match_table = max20860a_of_match, + }, + .probe = max20860a_probe, + .id_table = max20860a_id, +}; + +module_i2c_driver(max20860a_driver); + +MODULE_AUTHOR("Syed Arif <arif.syed@hpe.com>"); +MODULE_AUTHOR("Sanman Pradhan <psanman@juniper.net>"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX20860A"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 3caa76bcbeb5..bdcce810a479 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -447,10 +447,10 @@ static int max31785_probe(struct i2c_client *client) } static const struct i2c_device_id max31785_id[] = { - { "max31785" }, - { "max31785a" }, - { "max31785b" }, - { }, + { .name = "max31785" }, + { .name = "max31785a" }, + { .name = "max31785b" }, + { } }; MODULE_DEVICE_TABLE(i2c, max31785_id); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index cc96bb22f8f5..74876d2207fb 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -18,6 +18,7 @@ enum chips { adpm12160, adpm12200, + adpm12250, max34440, max34441, max34446, @@ -97,7 +98,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, break; case PMBUS_VIRT_READ_IOUT_AVG: if (data->id != max34446 && data->id != max34451 && - data->id != adpm12160 && data->id != adpm12200) + data->id != adpm12160 && data->id != adpm12200 && + data->id != adpm12250) return -ENXIO; ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); @@ -182,7 +184,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page, ret = pmbus_write_word_data(client, page, MAX34440_MFR_IOUT_PEAK, 0); if (!ret && (data->id == max34446 || data->id == max34451 || - data->id == adpm12160 || data->id == adpm12200)) + data->id == adpm12160 || data->id == adpm12200 || + data->id == adpm12250)) ret = pmbus_write_word_data(client, page, MAX34446_MFR_IOUT_AVG, 0); @@ -399,6 +402,40 @@ static struct pmbus_driver_info max34440_info[] = { .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, + [adpm12250] = { + .pages = 19, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 125, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 125, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_IN] = 250, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = -1, + .m[PSC_CURRENT_OUT] = 250, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = -1, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + /* absent func below [18] are not for monitoring */ + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_STATUS_IOUT, + .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT, + .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, + .func[14] = PMBUS_HAVE_IOUT, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, [max34440] = { .pages = 14, .format[PSC_VOLTAGE_IN] = direct, @@ -635,7 +672,8 @@ static int max34440_probe(struct i2c_client *client) rv = max34451_set_supported_funcs(client, data); if (rv) return rv; - } else if (data->id == adpm12160 || data->id == adpm12200) { + } else if (data->id == adpm12160 || data->id == adpm12200 || + data->id == adpm12250) { data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; } @@ -644,15 +682,16 @@ static int max34440_probe(struct i2c_client *client) } static const struct i2c_device_id max34440_id[] = { - {"adpm12160", adpm12160}, - {"adpm12200", adpm12200}, - {"max34440", max34440}, - {"max34441", max34441}, - {"max34446", max34446}, - {"max34451", max34451}, - {"max34460", max34460}, - {"max34461", max34461}, - {} + { .name = "adpm12160", .driver_data = adpm12160 }, + { .name = "adpm12200", .driver_data = adpm12200 }, + { .name = "adpm12250", .driver_data = adpm12250 }, + { .name = "max34440", .driver_data = max34440 }, + { .name = "max34441", .driver_data = max34441 }, + { .name = "max34446", .driver_data = max34446 }, + { .name = "max34451", .driver_data = max34451 }, + { .name = "max34460", .driver_data = max34460 }, + { .name = "max34461", .driver_data = max34461 }, + { } }; MODULE_DEVICE_TABLE(i2c, max34440_id); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index b3a2a7492bbf..212b28fa4bf2 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -171,7 +171,7 @@ static int max8688_probe(struct i2c_client *client) } static const struct i2c_device_id max8688_id[] = { - {"max8688"}, + { .name = "max8688" }, { } }; diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c index e83c70a3583f..3d6621e36fd3 100644 --- a/drivers/hwmon/pmbus/mp2856.c +++ b/drivers/hwmon/pmbus/mp2856.c @@ -54,9 +54,9 @@ static const int mp2856_max_phases[][MP2856_PAGE_NUM] = { }; static const struct i2c_device_id mp2856_id[] = { - {"mp2856", mp2856}, - {"mp2857", mp2857}, - {} + { .name = "mp2856", .driver_data = mp2856 }, + { .name = "mp2857", .driver_data = mp2857 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2856_id); diff --git a/drivers/hwmon/pmbus/mp2869.c b/drivers/hwmon/pmbus/mp2869.c index 4647892e5112..76c6017507b3 100644 --- a/drivers/hwmon/pmbus/mp2869.c +++ b/drivers/hwmon/pmbus/mp2869.c @@ -632,20 +632,20 @@ static int mp2869_probe(struct i2c_client *client) } static const struct i2c_device_id mp2869_id[] = { - {"mp2869", 0}, - {"mp29608", 1}, - {"mp29612", 2}, - {"mp29816", 3}, - {} + { .name = "mp2869" }, + { .name = "mp29608" }, + { .name = "mp29612" }, + { .name = "mp29816" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2869_id); -static const struct of_device_id __maybe_unused mp2869_of_match[] = { - {.compatible = "mps,mp2869", .data = (void *)0}, - {.compatible = "mps,mp29608", .data = (void *)1}, - {.compatible = "mps,mp29612", .data = (void *)2}, - {.compatible = "mps,mp29816", .data = (void *)3}, - {} +static const struct of_device_id mp2869_of_match[] = { + { .compatible = "mps,mp2869" }, + { .compatible = "mps,mp29608" }, + { .compatible = "mps,mp29612" }, + { .compatible = "mps,mp29816" }, + { } }; MODULE_DEVICE_TABLE(of, mp2869_of_match); diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c index 772a623ca7d0..c5f35daa3fe1 100644 --- a/drivers/hwmon/pmbus/mp2888.c +++ b/drivers/hwmon/pmbus/mp2888.c @@ -378,8 +378,8 @@ static int mp2888_probe(struct i2c_client *client) } static const struct i2c_device_id mp2888_id[] = { - {"mp2888"}, - {} + { .name = "mp2888" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2888_id); diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c index f8f4c91ec23c..316c1fc0d6cc 100644 --- a/drivers/hwmon/pmbus/mp2891.c +++ b/drivers/hwmon/pmbus/mp2891.c @@ -572,7 +572,7 @@ static int mp2891_probe(struct i2c_client *client) } static const struct i2c_device_id mp2891_id[] = { - { "mp2891" }, + { .name = "mp2891" }, { } }; MODULE_DEVICE_TABLE(i2c, mp2891_id); diff --git a/drivers/hwmon/pmbus/mp2925.c b/drivers/hwmon/pmbus/mp2925.c index ad094842cf2d..0a58b1ffd791 100644 --- a/drivers/hwmon/pmbus/mp2925.c +++ b/drivers/hwmon/pmbus/mp2925.c @@ -305,9 +305,9 @@ static int mp2925_probe(struct i2c_client *client) } static const struct i2c_device_id mp2925_id[] = { - {"mp2925"}, - {"mp2929"}, - {} + { .name = "mp2925" }, + { .name = "mp2929" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2925_id); diff --git a/drivers/hwmon/pmbus/mp29502.c b/drivers/hwmon/pmbus/mp29502.c index 7241373f1557..afc5e8c07e25 100644 --- a/drivers/hwmon/pmbus/mp29502.c +++ b/drivers/hwmon/pmbus/mp29502.c @@ -642,8 +642,8 @@ static int mp29502_probe(struct i2c_client *client) } static const struct i2c_device_id mp29502_id[] = { - {"mp29502", 0}, - {} + { .name = "mp29502", .driver_data = 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp29502_id); diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index d0bc47b12cb0..dca7e2fbcb44 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -1082,10 +1082,10 @@ static const struct of_device_id mp2975_of_match[] = { MODULE_DEVICE_TABLE(of, mp2975_of_match); static const struct i2c_device_id mp2975_id[] = { - {"mp2971", (kernel_ulong_t)&mp2975_ddinfo[mp2971]}, - {"mp2973", (kernel_ulong_t)&mp2975_ddinfo[mp2973]}, - {"mp2975", (kernel_ulong_t)&mp2975_ddinfo[mp2975]}, - {} + { .name = "mp2971", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2971] }, + { .name = "mp2973", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2973] }, + { .name = "mp2975", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2975] }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2975_id); diff --git a/drivers/hwmon/pmbus/mp2985.c b/drivers/hwmon/pmbus/mp2985.c new file mode 100644 index 000000000000..b0a4796ca8ce --- /dev/null +++ b/drivers/hwmon/pmbus/mp2985.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2985) + * + * Copyright (C) 2026 MPS + */ + +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +/* + * Vender specific register READ_PIN_EST(0x93), READ_IIN_EST(0x8E), + * MFR_VR_MULTI_CONFIG_R1(0x0D) and MFR_VR_MULTI_CONFIG_R2(0x1D). + * The READ_PIN_EST is used to read pin telemetry, the READ_IIN_EST + * is used to read iin telemetry and the MFR_VR_MULTI_CONFIG_R1, + * MFR_VR_MULTI_CONFIG_R2 are used to obtain vid scale. + */ +#define READ_PIN_EST 0x93 +#define READ_IIN_EST 0x8E +#define MFR_VR_MULTI_CONFIG_R1 0x0D +#define MFR_VR_MULTI_CONFIG_R2 0x1D + +#define MP2985_VOUT_DIV 64 +#define MP2985_VOUT_OVUV_UINT 125 +#define MP2985_VOUT_OVUV_DIV 64 + +#define MP2985_PAGE_NUM 2 + +#define MP2985_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +#define MP2985_RAIL2_FUNC (PMBUS_HAVE_PIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +struct mp2985_data { + struct pmbus_driver_info info; + int vout_scale[MP2985_PAGE_NUM]; + int vid_offset[MP2985_PAGE_NUM]; +}; + +#define to_mp2985_data(x) container_of(x, struct mp2985_data, info) + +static u16 mp2985_linear_exp_transfer(u16 word, u16 expect_exponent) +{ + s16 exponent, mantissa, target_exponent; + + exponent = ((s16)word) >> 11; + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; + target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11; + + /* + * The MP2985 does not support negtive limit value, if a negtive + * limit value is written, the limit value will become to 0. And + * the maximum positive limit value is limitted to 0x3FF. + */ + if (mantissa < 0) { + mantissa = 0; + } else { + if (exponent > target_exponent) { + mantissa = (1023 >> (exponent - target_exponent)) >= mantissa ? + mantissa << (exponent - target_exponent) : + 0x3FF; + } else { + mantissa = clamp_val(mantissa >> (target_exponent - exponent), + 0, 0x3FF); + } + } + + return mantissa | ((expect_exponent << 11) & 0xf800); +} + +static int mp2985_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * The MP2985 does not follow standard PMBus protocol completely, + * and the calculation of vout in this driver is based on direct + * format. As a result, the format of vout is enforced to direct. + */ + ret = PB_VOUT_MODE_DIRECT; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int mp2985_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2985_data *data = to_mp2985_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + /* + * The MP2985 supports three vout mode, direct, linear11 and vid mode. + * In vid mode, the MP2985 vout telemetry has 49 vid step offset, but + * PMBUS_VOUT_OV_FAULT_LIMIT and PMBUS_VOUT_UV_FAULT_LIMIT do not take + * this into consideration, their resolution are 1.953125mV/LSB, as a + * result, format[PSC_VOLTAGE_OUT] can not be set to vid mode directly. + * Adding extra vid_offset variable for vout telemetry. + */ + ret = clamp_val(DIV_ROUND_CLOSEST(((ret & GENMASK(11, 0)) + + data->vid_offset[page]) * + data->vout_scale[page], MP2985_VOUT_DIV), + 0, 0x7FFF); + break; + case PMBUS_READ_IIN: + /* + * The MP2985 has standard PMBUS_READ_IIN register(0x89), but this is + * not used to read the input current of per rail. The input current + * is read through the vender redefined register READ_IIN_EST(0x8E). + */ + ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST); + break; + case PMBUS_READ_PIN: + /* + * The MP2985 has standard PMBUS_READ_PIN register(0x97), but this + * is not used to read the input power of per rail. The input power + * of per rail is read through the vender redefined register + * READ_PIN_EST(0x93). + */ + ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * MP2985_VOUT_OVUV_UINT, + MP2985_VOUT_OVUV_DIV); + break; + case PMBUS_STATUS_WORD: + case PMBUS_READ_VIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * These register is not explicitly handled by the driver, + * as a result, return -ENODATA directly. + */ + ret = -ENODATA; + break; + default: + /* + * The MP2985 do not support other telemetry and limit value + * reading, so, return -EINVAL directly. + */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2985_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + /* + * The PMBUS_VIN_OV_FAULT_LIMIT, PMBUS_VIN_OV_WARN_LIMIT, + * PMBUS_VIN_UV_WARN_LIMIT and PMBUS_VIN_UV_FAULT_LIMIT + * of MP2985 is linear11 format, and the exponent is a + * constant value(5'b11101), so the exponent of word + * parameter should be converted to 5'b11101(0x1D). + */ + ret = pmbus_write_word_data(client, page, reg, + mp2985_linear_exp_transfer(word, 0x1D)); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + /* + * The bit0-bit11 is the limit value, and bit12-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(11, 0)) | + clamp_val(DIV_ROUND_CLOSEST(word * MP2985_VOUT_OVUV_DIV, + MP2985_VOUT_OVUV_UINT), 0, 0xFFF)); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT of + * MP2985 is linear11 format, and the exponent is a + * constant value(5'b00000), so the exponent of word + * parameter should be converted to 5'b00000. + */ + ret = pmbus_write_word_data(client, page, reg, + mp2985_linear_exp_transfer(word, 0x00)); + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + /* + * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT + * of MP2985 is linear11 format, and the exponent can not be + * changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + mp2985_linear_exp_transfer(word, + FIELD_GET(GENMASK(15, 11), + ret))); + break; + default: + /* + * The MP2985 do not support other limit value configuration, + * so, return -EINVAL directly. + */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mp2985_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2985_data *data = to_mp2985_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + /* + * The MP2985 supports three vout mode. If PMBUS_VOUT_MODE + * bit5 is 1, it is vid mode. If PMBUS PMBUS_VOUT_MODE bit4 + * is 1, it is linear11 mode, the vout scale is 1.953125mv/LSB. + * If PMBUS PMBUS_VOUT_MODE bit6 is 1, it is direct mode, the + * vout scale is 1mv/LSB. In vid mode, the MP2985 vout telemetry + * has 49 vid step offset. + */ + if (FIELD_GET(BIT(5), ret)) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, page == 0 ? + MFR_VR_MULTI_CONFIG_R1 : + MFR_VR_MULTI_CONFIG_R2); + if (ret < 0) + return ret; + + if (page == 0) { + if (FIELD_GET(BIT(4), ret)) + data->vout_scale[page] = 320; + else + data->vout_scale[page] = 640; + } else { + if (FIELD_GET(BIT(3), ret)) + data->vout_scale[page] = 320; + else + data->vout_scale[page] = 640; + } + + data->vid_offset[page] = 49; + + /* + * For vid mode, the MP2985 should be changed to page 2 + * to obtain vout scale value, this may confuse the PMBus + * core. To avoid this, switch back to the previous page + * again. + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + } else if (FIELD_GET(BIT(4), ret)) { + data->vout_scale[page] = 125; + data->vid_offset[page] = 0; + } else { + data->vout_scale[page] = 64; + data->vid_offset[page] = 0; + } + + return 0; +} + +static int mp2985_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + ret = mp2985_identify_vout_scale(client, info, 0); + if (ret < 0) + return ret; + + return mp2985_identify_vout_scale(client, info, 1); +} + +static struct pmbus_driver_info mp2985_info = { + .pages = MP2985_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .func[0] = MP2985_RAIL1_FUNC, + .func[1] = MP2985_RAIL2_FUNC, + .read_word_data = mp2985_read_word_data, + .read_byte_data = mp2985_read_byte_data, + .write_word_data = mp2985_write_word_data, + .identify = mp2985_identify, +}; + +static int mp2985_probe(struct i2c_client *client) +{ + struct mp2985_data *data; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2985_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2985_info, sizeof(mp2985_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp2985_id[] = { + {"mp2985", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2985_id); + +static const struct of_device_id __maybe_unused mp2985_of_match[] = { + {.compatible = "mps,mp2985"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2985_of_match); + +static struct i2c_driver mp2985_driver = { + .driver = { + .name = "mp2985", + .of_match_table = mp2985_of_match, + }, + .probe = mp2985_probe, + .id_table = mp2985_id, +}; + +module_i2c_driver(mp2985_driver); + +MODULE_AUTHOR("Wensheng Wang <wenswang@yeah.net>"); +MODULE_DESCRIPTION("PMBus driver for MPS MP2985 device"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c index 81c84fc8ed47..3a6a6c55a1f1 100644 --- a/drivers/hwmon/pmbus/mp2993.c +++ b/drivers/hwmon/pmbus/mp2993.c @@ -233,7 +233,7 @@ static int mp2993_probe(struct i2c_client *client) } static const struct i2c_device_id mp2993_id[] = { - { "mp2993" }, + { .name = "mp2993" }, { } }; MODULE_DEVICE_TABLE(i2c, mp2993_id); diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c index 319ae2721bcf..b803f3ddf8ea 100644 --- a/drivers/hwmon/pmbus/mp5920.c +++ b/drivers/hwmon/pmbus/mp5920.c @@ -66,7 +66,7 @@ static const struct of_device_id mp5920_of_match[] = { MODULE_DEVICE_TABLE(of, mp5920_of_match); static const struct i2c_device_id mp5920_id[] = { - { "mp5920" }, + { .name = "mp5920" }, { } }; diff --git a/drivers/hwmon/pmbus/mp5926.c b/drivers/hwmon/pmbus/mp5926.c index f0d1b30c7013..a40647472b16 100644 --- a/drivers/hwmon/pmbus/mp5926.c +++ b/drivers/hwmon/pmbus/mp5926.c @@ -157,8 +157,8 @@ static int mp5926_probe(struct i2c_client *client) } static const struct i2c_device_id mp5926_id[] = { - { "mp5926", 0 }, - {} + { .name = "mp5926", .driver_data = 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp5926_id); diff --git a/drivers/hwmon/pmbus/mp5990.c b/drivers/hwmon/pmbus/mp5990.c index 9a4ee79712cf..f8a5d1b42ff7 100644 --- a/drivers/hwmon/pmbus/mp5990.c +++ b/drivers/hwmon/pmbus/mp5990.c @@ -148,8 +148,8 @@ static struct pmbus_driver_info mp5998_info = { }; static const struct i2c_device_id mp5990_id[] = { - {"mp5990", mp5990}, - {"mp5998", mp5998}, + { .name = "mp5990", .driver_data = mp5990 }, + { .name = "mp5998", .driver_data = mp5998 }, { } }; MODULE_DEVICE_TABLE(i2c, mp5990_id); diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c index 42ca6748777a..d9049d326c80 100644 --- a/drivers/hwmon/pmbus/mp9941.c +++ b/drivers/hwmon/pmbus/mp9941.c @@ -291,7 +291,7 @@ static int mp9941_probe(struct i2c_client *client) } static const struct i2c_device_id mp9941_id[] = { - { "mp9941" }, + { .name = "mp9941" }, { } }; MODULE_DEVICE_TABLE(i2c, mp9941_id); diff --git a/drivers/hwmon/pmbus/mp9945.c b/drivers/hwmon/pmbus/mp9945.c index 34822e0de812..199480d85515 100644 --- a/drivers/hwmon/pmbus/mp9945.c +++ b/drivers/hwmon/pmbus/mp9945.c @@ -215,8 +215,8 @@ static int mp9945_probe(struct i2c_client *client) } static const struct i2c_device_id mp9945_id[] = { - {"mp9945"}, - {} + { .name = "mp9945" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp9945_id); diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index 8f10e37a7a76..f49610adff89 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -145,9 +145,9 @@ static const struct of_device_id mpq7932_of_match[] = { MODULE_DEVICE_TABLE(of, mpq7932_of_match); static const struct i2c_device_id mpq7932_id[] = { - { "mpq2286", }, - { "mpq7932", }, - { }, + { .name = "mpq2286" }, + { .name = "mpq7932" }, + { } }; MODULE_DEVICE_TABLE(i2c, mpq7932_id); diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 87bd039c77b9..bbde55028290 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -110,11 +110,11 @@ static struct pmbus_driver_info mpq8785_info = { }; static const struct i2c_device_id mpq8785_id[] = { - { "mpm3695", mpm3695 }, - { "mpm3695-25", mpm3695_25 }, - { "mpm82504", mpm82504 }, - { "mpq8785", mpq8785 }, - { }, + { .name = "mpm3695", .driver_data = mpm3695 }, + { .name = "mpm3695-25", .driver_data = mpm3695_25 }, + { .name = "mpm82504", .driver_data = mpm82504 }, + { .name = "mpq8785", .driver_data = mpq8785 }, + { } }; MODULE_DEVICE_TABLE(i2c, mpq8785_id); diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c index aa98284bbdd8..9056dc387e87 100644 --- a/drivers/hwmon/pmbus/pim4328.c +++ b/drivers/hwmon/pmbus/pim4328.c @@ -39,15 +39,15 @@ struct pim4328_data { #define PIM4328_MFR_READ_STATUS 0xd0 static const struct i2c_device_id pim4328_id[] = { - {"bmr455", pim4328}, - {"pim4006", pim4006}, - {"pim4106", pim4006}, - {"pim4206", pim4006}, - {"pim4306", pim4006}, - {"pim4328", pim4328}, - {"pim4406", pim4006}, - {"pim4820", pim4820}, - {} + { .name = "bmr455", .driver_data = pim4328 }, + { .name = "pim4006", .driver_data = pim4006 }, + { .name = "pim4106", .driver_data = pim4006 }, + { .name = "pim4206", .driver_data = pim4006 }, + { .name = "pim4306", .driver_data = pim4006 }, + { .name = "pim4328", .driver_data = pim4328 }, + { .name = "pim4406", .driver_data = pim4006 }, + { .name = "pim4820", .driver_data = pim4820 }, + { } }; MODULE_DEVICE_TABLE(i2c, pim4328_id); diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c index 569b61dc1a32..d5ab085a2273 100644 --- a/drivers/hwmon/pmbus/pli1209bc.c +++ b/drivers/hwmon/pmbus/pli1209bc.c @@ -117,8 +117,8 @@ static int pli1209bc_probe(struct i2c_client *client) } static const struct i2c_device_id pli1209bc_id[] = { - {"pli1209bc"}, - {} + { .name = "pli1209bc" }, + { } }; MODULE_DEVICE_TABLE(i2c, pli1209bc_id); diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c index c96c0aecb920..613654b15b4a 100644 --- a/drivers/hwmon/pmbus/pm6764tr.c +++ b/drivers/hwmon/pmbus/pm6764tr.c @@ -48,8 +48,8 @@ static int pm6764tr_probe(struct i2c_client *client) } static const struct i2c_device_id pm6764tr_id[] = { - {"pm6764tr"}, - {} + { .name = "pm6764tr" }, + { } }; MODULE_DEVICE_TABLE(i2c, pm6764tr_id); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index d1844c7a51ee..7a7eac90d62e 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -213,36 +213,40 @@ static const struct pmbus_device_info pmbus_info_one_status = { * Use driver_data to set the number of pages supported by the chip. */ static const struct i2c_device_id pmbus_id[] = { - {"adp4000", (kernel_ulong_t)&pmbus_info_one}, - {"bmr310", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr453", (kernel_ulong_t)&pmbus_info_one}, - {"bmr454", (kernel_ulong_t)&pmbus_info_one}, - {"bmr456", (kernel_ulong_t)&pmbus_info_one}, - {"bmr457", (kernel_ulong_t)&pmbus_info_one}, - {"bmr458", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr480", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr490", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr491", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr492", (kernel_ulong_t)&pmbus_info_one}, - {"dps460", (kernel_ulong_t)&pmbus_info_one_skip}, - {"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip}, - {"dps800", (kernel_ulong_t)&pmbus_info_one_skip}, - {"max20796", (kernel_ulong_t)&pmbus_info_one}, - {"mdt040", (kernel_ulong_t)&pmbus_info_one}, - {"ncp4200", (kernel_ulong_t)&pmbus_info_one}, - {"ncp4208", (kernel_ulong_t)&pmbus_info_one}, - {"pdt003", (kernel_ulong_t)&pmbus_info_one}, - {"pdt006", (kernel_ulong_t)&pmbus_info_one}, - {"pdt012", (kernel_ulong_t)&pmbus_info_one}, - {"pmbus", (kernel_ulong_t)&pmbus_info_zero}, - {"sgd009", (kernel_ulong_t)&pmbus_info_one_skip}, - {"tps40400", (kernel_ulong_t)&pmbus_info_one}, - {"tps544b20", (kernel_ulong_t)&pmbus_info_one}, - {"tps544b25", (kernel_ulong_t)&pmbus_info_one}, - {"tps544c20", (kernel_ulong_t)&pmbus_info_one}, - {"tps544c25", (kernel_ulong_t)&pmbus_info_one}, - {"udt020", (kernel_ulong_t)&pmbus_info_one}, - {} + { .name = "adp4000", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr310", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr316", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr321", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr350", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr351", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr453", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr454", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr456", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr457", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr458", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr480", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr490", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr491", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr492", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "dps460", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "dps650ab", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "dps800", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "max20796", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "mdt040", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "ncp4200", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "ncp4208", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt003", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt006", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt012", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pmbus", .driver_data = (kernel_ulong_t)&pmbus_info_zero }, + { .name = "sgd009", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "tps40400", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544b20", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544b25", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544c20", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544c25", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "udt020", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { } }; MODULE_DEVICE_TABLE(i2c, pmbus_id); diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index 24c1f961c766..6ba2b3e0e565 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -130,10 +130,10 @@ static int pxe1610_probe(struct i2c_client *client) } static const struct i2c_device_id pxe1610_id[] = { - {"pxe1610"}, - {"pxe1110"}, - {"pxm1310"}, - {} + { .name = "pxe1610" }, + { .name = "pxe1110" }, + { .name = "pxm1310" }, + { } }; MODULE_DEVICE_TABLE(i2c, pxe1610_id); diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c index a368cfa9d45a..ecac405d7536 100644 --- a/drivers/hwmon/pmbus/q54sj108a2.c +++ b/drivers/hwmon/pmbus/q54sj108a2.c @@ -270,10 +270,10 @@ static const struct file_operations q54sj108a2_fops = { }; static const struct i2c_device_id q54sj108a2_id[] = { - { "q54sj108a2", q54sj108a2 }, - { "q54sn120a1", q54sj108a2 }, - { "q54sw120a7", q54sj108a2 }, - { }, + { .name = "q54sj108a2", .driver_data = q54sj108a2 }, + { .name = "q54sn120a1", .driver_data = q54sj108a2 }, + { .name = "q54sw120a7", .driver_data = q54sj108a2 }, + { } }; MODULE_DEVICE_TABLE(i2c, q54sj108a2_id); diff --git a/drivers/hwmon/pmbus/stef48h28.c b/drivers/hwmon/pmbus/stef48h28.c index 4bde2215697c..8e48dd3ba74b 100644 --- a/drivers/hwmon/pmbus/stef48h28.c +++ b/drivers/hwmon/pmbus/stef48h28.c @@ -48,8 +48,8 @@ static int stef48h28_probe(struct i2c_client *client) } static const struct i2c_device_id stef48h28_id[] = { - {"stef48h28"}, - {} + { .name = "stef48h28" }, + { } }; MODULE_DEVICE_TABLE(i2c, stef48h28_id); diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c index 5cb905ed8ae5..b5ce7f975eea 100644 --- a/drivers/hwmon/pmbus/stpddc60.c +++ b/drivers/hwmon/pmbus/stpddc60.c @@ -18,9 +18,9 @@ #define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6 static const struct i2c_device_id stpddc60_id[] = { - {"stpddc60"}, - {"bmr481"}, - {} + { .name = "stpddc60" }, + { .name = "bmr481" }, + { } }; MODULE_DEVICE_TABLE(i2c, stpddc60_id); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index d902d39f49f4..803926633370 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -195,8 +195,8 @@ static int tda38640_probe(struct i2c_client *client) } static const struct i2c_device_id tda38640_id[] = { - {"tda38640"}, - {} + { .name = "tda38640" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda38640_id); diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c index 05c6288ecafc..9d318e6509ab 100644 --- a/drivers/hwmon/pmbus/tps25990.c +++ b/drivers/hwmon/pmbus/tps25990.c @@ -387,8 +387,8 @@ static const struct pmbus_driver_info tps25990_base_info = { }; static const struct i2c_device_id tps25990_i2c_id[] = { - { "tps25990" }, - {} + { .name = "tps25990" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id); diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c index 7c9fedaa068c..f7be075dc707 100644 --- a/drivers/hwmon/pmbus/tps40422.c +++ b/drivers/hwmon/pmbus/tps40422.c @@ -31,8 +31,8 @@ static int tps40422_probe(struct i2c_client *client) } static const struct i2c_device_id tps40422_id[] = { - {"tps40422"}, - {} + { .name = "tps40422" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps40422_id); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 94258e8cfd90..31e54608b3c9 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -291,15 +291,15 @@ static int tps53679_probe(struct i2c_client *client) } static const struct i2c_device_id tps53679_id[] = { - {"bmr474", tps53676}, - {"tps53647", tps53647}, - {"tps53667", tps53667}, - {"tps53676", tps53676}, - {"tps53679", tps53679}, - {"tps53681", tps53681}, - {"tps53685", tps53685}, - {"tps53688", tps53688}, - {} + { .name = "bmr474", .driver_data = tps53676 }, + { .name = "tps53647", .driver_data = tps53647 }, + { .name = "tps53667", .driver_data = tps53667 }, + { .name = "tps53676", .driver_data = tps53676 }, + { .name = "tps53679", .driver_data = tps53679 }, + { .name = "tps53681", .driver_data = tps53681 }, + { .name = "tps53685", .driver_data = tps53685 }, + { .name = "tps53688", .driver_data = tps53688 }, + { } }; MODULE_DEVICE_TABLE(i2c, tps53679_id); diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c index 44d7a6df1dbd..4222ff355e02 100644 --- a/drivers/hwmon/pmbus/tps546d24.c +++ b/drivers/hwmon/pmbus/tps546d24.c @@ -42,8 +42,8 @@ static int tps546d24_probe(struct i2c_client *client) } static const struct i2c_device_id tps546d24_id[] = { - {"tps546d24"}, - {} + { .name = "tps546d24" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps546d24_id); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 9b5d34a110ba..f76c2913c9ca 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -143,14 +143,14 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) } static const struct i2c_device_id ucd9000_id[] = { - {"ucd9000", ucd9000}, - {"ucd90120", ucd90120}, - {"ucd90124", ucd90124}, - {"ucd90160", ucd90160}, - {"ucd90320", ucd90320}, - {"ucd9090", ucd9090}, - {"ucd90910", ucd90910}, - {} + { .name = "ucd9000", .driver_data = ucd9000 }, + { .name = "ucd90120", .driver_data = ucd90120 }, + { .name = "ucd90124", .driver_data = ucd90124 }, + { .name = "ucd90160", .driver_data = ucd90160 }, + { .name = "ucd90320", .driver_data = ucd90320 }, + { .name = "ucd9090", .driver_data = ucd9090 }, + { .name = "ucd90910", .driver_data = ucd90910 }, + { } }; MODULE_DEVICE_TABLE(i2c, ucd9000_id); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index f68adaf4a110..5e07bba111a1 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -22,15 +22,15 @@ enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246, ucd9248 }; static const struct i2c_device_id ucd9200_id[] = { - {"ucd9200", ucd9200}, - {"ucd9220", ucd9220}, - {"ucd9222", ucd9222}, - {"ucd9224", ucd9224}, - {"ucd9240", ucd9240}, - {"ucd9244", ucd9244}, - {"ucd9246", ucd9246}, - {"ucd9248", ucd9248}, - {} + { .name = "ucd9200", .driver_data = ucd9200 }, + { .name = "ucd9220", .driver_data = ucd9220 }, + { .name = "ucd9222", .driver_data = ucd9222 }, + { .name = "ucd9224", .driver_data = ucd9224 }, + { .name = "ucd9240", .driver_data = ucd9240 }, + { .name = "ucd9244", .driver_data = ucd9244 }, + { .name = "ucd9246", .driver_data = ucd9246 }, + { .name = "ucd9248", .driver_data = ucd9248 }, + { } }; MODULE_DEVICE_TABLE(i2c, ucd9200_id); diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c index 660bbfe16e1e..494dbe45ebc6 100644 --- a/drivers/hwmon/pmbus/xdp710.c +++ b/drivers/hwmon/pmbus/xdp710.c @@ -110,7 +110,7 @@ static const struct of_device_id xdp710_of_match[] = { }; static const struct i2c_device_id xdp710_id[] = { - {"xdp710"}, + { .name = "xdp710" }, { } }; MODULE_DEVICE_TABLE(i2c, xdp710_id); diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c index 8729a771f216..5a162084f338 100644 --- a/drivers/hwmon/pmbus/xdp720.c +++ b/drivers/hwmon/pmbus/xdp720.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Hardware monitoring driver for Infineon XDP720 Digital eFuse Controller + * Hardware monitoring driver for Infineon XDP720 / XDP730 Digital + * eFuse Controllers. + * + * Both parts share the same PMBus register map and direct-format + * coefficients; they differ in the GIMON gain step exposed via + * the TELEMETRY_AVG register and in the VDD_VIN pin number. * * Copyright (c) 2026 Infineon Technologies. All rights reserved. */ @@ -12,16 +17,30 @@ #include <linux/of_device.h> #include <linux/bitops.h> #include <linux/math64.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> #include "pmbus.h" /* * The IMON resistor required to generate the system overcurrent protection. * Arbitrary default Rimon value: 2k Ohm */ -#define XDP720_DEFAULT_RIMON 2000000000 /* 2k ohm */ +#define XDP720_DEFAULT_RIMON 2000000000U /* 2k ohm */ #define XDP720_TELEMETRY_AVG 0xE9 +#define XDP720_TELEMETRY_AVG_GIMON BIT(10) /* high/low GIMON select */ + +/* Chip identifiers carried in OF match-data and i2c_device_id->driver_data. */ +enum xdp720_chip_id { + CHIP_XDP720 = 0, + CHIP_XDP730, +}; + +struct xdp720_data { + enum xdp720_chip_id id; + struct pmbus_driver_info info; +}; -static struct pmbus_driver_info xdp720_info = { +static const struct pmbus_driver_info xdp720_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = direct, .format[PSC_VOLTAGE_OUT] = direct, @@ -56,16 +75,18 @@ static struct pmbus_driver_info xdp720_info = { static int xdp720_probe(struct i2c_client *client) { - struct pmbus_driver_info *info; + struct xdp720_data *data; int ret; u32 rimon; int gimon; - info = devm_kmemdup(&client->dev, &xdp720_info, sizeof(*info), - GFP_KERNEL); - if (!info) + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; + data->id = (enum xdp720_chip_id)(uintptr_t)i2c_get_match_data(client); + data->info = xdp720_info; + ret = devm_regulator_get_enable(&client->dev, "vdd-vin"); if (ret) return dev_err_probe(&client->dev, ret, @@ -77,37 +98,48 @@ static int xdp720_probe(struct i2c_client *client) return ret; } - ret >>= 10; /* 10th bit of TELEMETRY_AVG REG for GIMON Value */ - ret &= GENMASK(0, 0); - if (ret == 1) - gimon = 18200; /* output gain 18.2 microA/A */ - else - gimon = 9100; /* output gain 9.1 microA/A */ + /* Bit 10 of TELEMETRY_AVG selects the GIMON gain step in microA/A */ + switch (data->id) { + case CHIP_XDP720: + gimon = (ret & XDP720_TELEMETRY_AVG_GIMON) ? 18200 : 9100; + dev_info(&client->dev, "Initialised XDP720 instance\n"); + break; + case CHIP_XDP730: + gimon = (ret & XDP720_TELEMETRY_AVG_GIMON) ? 20000 : 10000; + dev_info(&client->dev, "Initialised XDP730 instance\n"); + break; + default: + return -EINVAL; + } - if (of_property_read_u32(client->dev.of_node, - "infineon,rimon-micro-ohms", &rimon)) - rimon = XDP720_DEFAULT_RIMON; /* Default if not set via DT */ + if (device_property_read_u32(&client->dev, + "infineon,rimon-micro-ohms", &rimon)) + rimon = XDP720_DEFAULT_RIMON; /* Default if not in FW */ if (rimon == 0) return -EINVAL; /* Adapt the current and power scale for each instance */ - info->m[PSC_CURRENT_OUT] = DIV64_U64_ROUND_CLOSEST((u64) - info->m[PSC_CURRENT_OUT] * rimon * gimon, 1000000000000ULL); - info->m[PSC_POWER] = DIV64_U64_ROUND_CLOSEST((u64) - info->m[PSC_POWER] * rimon * gimon, 1000000000000000ULL); - - return pmbus_do_probe(client, info); + data->info.m[PSC_CURRENT_OUT] = DIV64_U64_ROUND_CLOSEST((u64) + data->info.m[PSC_CURRENT_OUT] * rimon * gimon, + 1000000000000ULL); + data->info.m[PSC_POWER] = DIV64_U64_ROUND_CLOSEST((u64) + data->info.m[PSC_POWER] * rimon * gimon, + 1000000000000000ULL); + + return pmbus_do_probe(client, &data->info); } static const struct of_device_id xdp720_of_match[] = { - { .compatible = "infineon,xdp720" }, + { .compatible = "infineon,xdp720", .data = (void *)CHIP_XDP720 }, + { .compatible = "infineon,xdp730", .data = (void *)CHIP_XDP730 }, {} }; MODULE_DEVICE_TABLE(of, xdp720_of_match); static const struct i2c_device_id xdp720_id[] = { - { "xdp720" }, - {} + { .name = "xdp720", .driver_data = CHIP_XDP720 }, + { .name = "xdp730", .driver_data = CHIP_XDP730 }, + { } }; MODULE_DEVICE_TABLE(i2c, xdp720_id); @@ -123,6 +155,6 @@ static struct i2c_driver xdp720_driver = { module_i2c_driver(xdp720_driver); MODULE_AUTHOR("Ashish Yadav <ashish.yadav@infineon.com>"); -MODULE_DESCRIPTION("PMBus driver for Infineon XDP720 Digital eFuse Controller"); +MODULE_DESCRIPTION("PMBus driver for Infineon XDP720/XDP730 Digital eFuse Controllers"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index f3aa6339d60d..ed93e17ce8d5 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -164,10 +164,10 @@ static int xdpe122_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe122_id[] = { - {"xdpe11280"}, - {"xdpe12254"}, - {"xdpe12284"}, - {} + { .name = "xdpe11280" }, + { .name = "xdpe12254" }, + { .name = "xdpe12284" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe122_id); diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c index 67a3d5fe1daf..b557d2971d5f 100644 --- a/drivers/hwmon/pmbus/xdpe152c4.c +++ b/drivers/hwmon/pmbus/xdpe152c4.c @@ -44,9 +44,9 @@ static int xdpe152_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe152_id[] = { - {"xdpe152c4"}, - {"xdpe15284"}, - {} + { .name = "xdpe152c4" }, + { .name = "xdpe15284" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe152_id); diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c index 1755e3522ede..971e7b73752e 100644 --- a/drivers/hwmon/pmbus/xdpe1a2g7b.c +++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c @@ -87,9 +87,9 @@ static int xdpe1a2g7b_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe1a2g7b_id[] = { - { "xdpe1a2g5b" }, - { "xdpe1a2g7b" }, - {} + { .name = "xdpe1a2g5b" }, + { .name = "xdpe1a2g7b" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe1a2g7b_id); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 97be69630cfb..5db2a7818d16 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -250,28 +250,28 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, } static const struct i2c_device_id zl6100_id[] = { - {"bmr450", zl2005}, - {"bmr451", zl2005}, - {"bmr462", zl2008}, - {"bmr463", zl2008}, - {"bmr464", zl2008}, - {"bmr465", zls4009}, - {"bmr466", zls1003}, - {"bmr467", zls4009}, - {"bmr469", zl8802}, - {"zl2004", zl2004}, - {"zl2005", zl2005}, - {"zl2006", zl2006}, - {"zl2008", zl2008}, - {"zl2105", zl2105}, - {"zl2106", zl2106}, - {"zl6100", zl6100}, - {"zl6105", zl6105}, - {"zl8802", zl8802}, - {"zl9101", zl9101}, - {"zl9117", zl9117}, - {"zls1003", zls1003}, - {"zls4009", zls4009}, + { .name = "bmr450", .driver_data = zl2005 }, + { .name = "bmr451", .driver_data = zl2005 }, + { .name = "bmr462", .driver_data = zl2008 }, + { .name = "bmr463", .driver_data = zl2008 }, + { .name = "bmr464", .driver_data = zl2008 }, + { .name = "bmr465", .driver_data = zls4009 }, + { .name = "bmr466", .driver_data = zls1003 }, + { .name = "bmr467", .driver_data = zls4009 }, + { .name = "bmr469", .driver_data = zl8802 }, + { .name = "zl2004", .driver_data = zl2004 }, + { .name = "zl2005", .driver_data = zl2005 }, + { .name = "zl2006", .driver_data = zl2006 }, + { .name = "zl2008", .driver_data = zl2008 }, + { .name = "zl2105", .driver_data = zl2105 }, + { .name = "zl2106", .driver_data = zl2106 }, + { .name = "zl6100", .driver_data = zl6100 }, + { .name = "zl6105", .driver_data = zl6105 }, + { .name = "zl8802", .driver_data = zl8802 }, + { .name = "zl9101", .driver_data = zl9101 }, + { .name = "zl9117", .driver_data = zl9117 }, + { .name = "zls1003", .driver_data = zls1003 }, + { .name = "zls4009", .driver_data = zls4009 }, { } }; MODULE_DEVICE_TABLE(i2c, zl6100_id); diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c index 06a2c56016d1..6e211601ea26 100644 --- a/drivers/hwmon/powr1220.c +++ b/drivers/hwmon/powr1220.c @@ -306,8 +306,8 @@ static int powr1220_probe(struct i2c_client *client) } static const struct i2c_device_id powr1220_ids[] = { - { "powr1014", powr1014, }, - { "powr1220", powr1220, }, + { .name = "powr1014", .driver_data = powr1014 }, + { .name = "powr1220", .driver_data = powr1220 }, { } }; diff --git a/drivers/hwmon/pt5161l.c b/drivers/hwmon/pt5161l.c index 89d4da8aa4c0..2b408a69b085 100644 --- a/drivers/hwmon/pt5161l.c +++ b/drivers/hwmon/pt5161l.c @@ -617,8 +617,8 @@ static const struct acpi_device_id __maybe_unused pt5161l_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, pt5161l_acpi_match); static const struct i2c_device_id pt5161l_id[] = { - { "pt5161l" }, - {} + { .name = "pt5161l" }, + { } }; MODULE_DEVICE_TABLE(i2c, pt5161l_id); diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 37269db2de84..e6a567d58579 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -685,8 +685,9 @@ static int pwm_fan_probe(struct platform_device *pdev) ctx->pwm_fan_state = ctx->pwm_fan_max_state; if (IS_ENABLED(CONFIG_THERMAL)) { - cdev = devm_thermal_of_cooling_device_register(dev, - dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev, dev->of_node, + "pwm-fan", ctx, + &pwm_fan_cooling_ops); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); dev_err(dev, diff --git a/drivers/hwmon/qnap-mcu-hwmon.c b/drivers/hwmon/qnap-mcu-hwmon.c index e86e64c4d391..c1c1e9d6f340 100644 --- a/drivers/hwmon/qnap-mcu-hwmon.c +++ b/drivers/hwmon/qnap-mcu-hwmon.c @@ -337,9 +337,9 @@ static int qnap_mcu_hwmon_probe(struct platform_device *pdev) * levels and only succeed with either no or correct cooling levels. */ if (IS_ENABLED(CONFIG_THERMAL) && hwm->fan_cooling_levels) { - cdev = devm_thermal_of_cooling_device_register(dev, - to_of_node(hwm->fan_node), "qnap-mcu-hwmon", - hwm, &qnap_mcu_hwmon_cooling_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev, to_of_node(hwm->fan_node), + "qnap-mcu-hwmon", hwm, + &qnap_mcu_hwmon_cooling_ops); if (IS_ERR(cdev)) return dev_err_probe(dev, PTR_ERR(cdev), "Failed to register qnap-mcu-hwmon as cooling device\n"); diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index a2938881ccd2..7b1d2829f012 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -5,9 +5,9 @@ * Based on firmware/raspberrypi.c by Noralf Trønnes * * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com> + * Copyright (C) 2026 Shubham Chakraborty <chakrabortyshubham66@gmail.com> */ #include <linux/device.h> -#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/hwmon.h> #include <linux/module.h> @@ -21,10 +21,18 @@ struct rpi_hwmon_data { struct device *hwmon_dev; struct rpi_firmware *fw; + u32 valid_inputs; u32 last_throttled; struct delayed_work get_values_poll_work; }; +static const char * const rpi_hwmon_labels[] = { + "core", + "sdram_c", + "sdram_i", + "sdram_p", +}; + static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) { u32 new_uv, old_uv, value; @@ -56,6 +64,22 @@ static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0); } +static int rpi_firmware_get_voltage(struct rpi_hwmon_data *data, u32 id, + long *val) +{ + struct rpi_firmware_get_voltage_request packet = + RPI_FIRMWARE_GET_VOLTAGE_REQUEST(id); + int ret; + + ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_VOLTAGE, + &packet, sizeof(packet)); + if (ret) + return ret; + + *val = le32_to_cpu(packet.value) / 1000; + return 0; +} + static void get_values_poll(struct work_struct *work) { struct rpi_hwmon_data *data; @@ -72,24 +96,106 @@ static void get_values_poll(struct work_struct *work) schedule_delayed_work(&data->get_values_poll_work, 2 * HZ); } +static void rpi_hwmon_cancel_poll_work(void *res) +{ + struct rpi_hwmon_data *data = res; + + disable_delayed_work_sync(&data->get_values_poll_work); +} + static int rpi_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct rpi_hwmon_data *data = dev_get_drvdata(dev); - *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT); + if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + switch (channel) { + case 0: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_CORE, + val); + case 1: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_C, + val); + case 2: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_I, + val); + case 3: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_P, + val); + default: + return -EOPNOTSUPP; + } + case hwmon_in_lcrit_alarm: + if (channel == 0) { + *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT); + return 0; + } + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } + } + + return -EOPNOTSUPP; +} + +static int rpi_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + if (type == hwmon_in && attr == hwmon_in_label) { + if (channel >= ARRAY_SIZE(rpi_hwmon_labels)) + return -EOPNOTSUPP; + + *str = rpi_hwmon_labels[channel]; + return 0; + } + + return -EOPNOTSUPP; +} + +static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct rpi_hwmon_data *data = _data; + + if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + if (!(data->valid_inputs & BIT(channel))) + return 0; + return 0444; + case hwmon_in_lcrit_alarm: + if (channel == 0) + return 0444; + return 0; + default: + return 0; + } + } + return 0; } static const struct hwmon_channel_info * const rpi_info[] = { HWMON_CHANNEL_INFO(in, - HWMON_I_LCRIT_ALARM), + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT_ALARM, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), NULL }; static const struct hwmon_ops rpi_hwmon_ops = { - .visible = 0444, + .is_visible = rpi_is_visible, .read = rpi_read, + .read_string = rpi_read_string, }; static const struct hwmon_chip_info rpi_chip_info = { @@ -101,6 +207,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpi_hwmon_data *data; + long voltage; int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -110,6 +217,26 @@ static int rpi_hwmon_probe(struct platform_device *pdev) /* Parent driver assure that firmware is correct */ data->fw = dev_get_drvdata(dev->parent); + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_CORE, + &voltage); + if (!ret) + data->valid_inputs |= BIT(0); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_C, + &voltage); + if (!ret) + data->valid_inputs |= BIT(1); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_I, + &voltage); + if (!ret) + data->valid_inputs |= BIT(2); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_P, + &voltage); + if (!ret) + data->valid_inputs |= BIT(3); + data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt", data, &rpi_chip_info, @@ -117,8 +244,8 @@ static int rpi_hwmon_probe(struct platform_device *pdev) if (IS_ERR(data->hwmon_dev)) return PTR_ERR(data->hwmon_dev); - ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work, - get_values_poll); + INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll); + ret = devm_add_action_or_reset(dev, rpi_hwmon_cancel_poll_work, data); if (ret) return ret; platform_set_drvdata(pdev, data); @@ -159,6 +286,7 @@ static struct platform_driver rpi_hwmon_driver = { module_platform_driver(rpi_hwmon_driver); MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); +MODULE_AUTHOR("Shubham Chakraborty <chakrabortyshubham66@gmail.com>"); MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:raspberrypi-hwmon"); diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index c5b2488c4c7f..c28f8625cd3a 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -221,8 +221,8 @@ static int sbtsi_probe(struct i2c_client *client) } static const struct i2c_device_id sbtsi_id[] = { - {"sbtsi"}, - {} + { .name = "sbtsi" }, + { } }; MODULE_DEVICE_TABLE(i2c, sbtsi_id); diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c index 105131c4acf7..591f5f572fe4 100644 --- a/drivers/hwmon/sg2042-mcu.c +++ b/drivers/hwmon/sg2042-mcu.c @@ -333,7 +333,7 @@ static int sg2042_mcu_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id sg2042_mcu_id[] = { - { "sg2042-hwmon-mcu" }, + { .name = "sg2042-hwmon-mcu" }, { } }; MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 3d55047e9baf..101cebbe68e4 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1036,11 +1036,11 @@ static void sht15_remove(struct platform_device *pdev) } static const struct platform_device_id sht15_device_ids[] = { - { "sht10", sht10 }, - { "sht11", sht11 }, - { "sht15", sht15 }, - { "sht71", sht71 }, - { "sht75", sht75 }, + { .name = "sht10", .driver_data = sht10 }, + { .name = "sht11", .driver_data = sht11 }, + { .name = "sht15", .driver_data = sht15 }, + { .name = "sht71", .driver_data = sht71 }, + { .name = "sht75", .driver_data = sht75 }, { } }; MODULE_DEVICE_TABLE(platform, sht15_device_ids); diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index 627d35070a42..085492669eeb 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -275,9 +275,9 @@ static int sht21_probe(struct i2c_client *client) /* Device ID table */ static const struct i2c_device_id sht21_id[] = { - { "sht20" }, - { "sht21" }, - { "sht25" }, + { .name = "sht20" }, + { .name = "sht21" }, + { .name = "sht25" }, { } }; MODULE_DEVICE_TABLE(i2c, sht21_id); diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index 08306ccb6d0b..c2f6b73aa7f3 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -931,10 +931,10 @@ static int sht3x_probe(struct i2c_client *client) /* device ID table */ static const struct i2c_device_id sht3x_ids[] = { - {"sht3x", sht3x}, - {"sts3x", sts3x}, - {"sht85", sht3x}, - {} + { .name = "sht3x", .driver_data = sht3x }, + { .name = "sts3x", .driver_data = sts3x }, + { .name = "sht85", .driver_data = sht3x }, + { } }; MODULE_DEVICE_TABLE(i2c, sht3x_ids); diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index 5abe1227e109..9cace0e8acda 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -424,8 +424,8 @@ static int sht4x_probe(struct i2c_client *client) } static const struct i2c_device_id sht4x_id[] = { - { "sht4x" }, - { }, + { .name = "sht4x" }, + { } }; MODULE_DEVICE_TABLE(i2c, sht4x_id); diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index 2ac906e8e173..362a73cf661b 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -257,9 +257,9 @@ static int shtc1_probe(struct i2c_client *client) /* device ID table */ static const struct i2c_device_id shtc1_id[] = { - { "shtc1", shtc1 }, - { "shtw1", shtc1 }, - { "shtc3", shtc3 }, + { .name = "shtc1", .driver_data = shtc1 }, + { .name = "shtw1", .driver_data = shtc1 }, + { .name = "shtc3", .driver_data = shtc3 }, { } }; MODULE_DEVICE_TABLE(i2c, shtc1_id); diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 21103af4e139..1429b66e09b0 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -618,7 +618,7 @@ static int smsc47m192_probe(struct i2c_client *client) } static const struct i2c_device_id smsc47m192_id[] = { - { "smsc47m192" }, + { .name = "smsc47m192" }, { } }; MODULE_DEVICE_TABLE(i2c, smsc47m192_id); diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index 5da44571b6a0..cc40661cab21 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -746,7 +746,7 @@ static int spd5118_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id spd5118_i2c_id[] = { - { "spd5118" }, + { .name = "spd5118" }, { } }; MODULE_DEVICE_TABLE(i2c, spd5118_i2c_id); diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index f9e8b2869164..ce23681f7981 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -72,7 +72,7 @@ static const int stts751_intervals[] = { }; static const struct i2c_device_id stts751_id[] = { - { "stts751" }, + { .name = "stts751" }, { } }; diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c index 39fe5836f237..4af9f0e2c0bc 100644 --- a/drivers/hwmon/tc654.c +++ b/drivers/hwmon/tc654.c @@ -541,8 +541,9 @@ static int tc654_probe(struct i2c_client *client) if (IS_ENABLED(CONFIG_THERMAL)) { struct thermal_cooling_device *cdev; - cdev = devm_thermal_of_cooling_device_register(dev, dev->of_node, client->name, - hwmon_dev, &tc654_fan_cool_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev, dev->of_node, + client->name, hwmon_dev, + &tc654_fan_cool_ops); return PTR_ERR_OR_ZERO(cdev); } @@ -550,9 +551,9 @@ static int tc654_probe(struct i2c_client *client) } static const struct i2c_device_id tc654_id[] = { - {"tc654"}, - {"tc655"}, - {} + { .name = "tc654" }, + { .name = "tc655" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc654_id); diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c index 7fb7b50ad1ad..e9113519be53 100644 --- a/drivers/hwmon/tc74.c +++ b/drivers/hwmon/tc74.c @@ -151,8 +151,8 @@ static int tc74_probe(struct i2c_client *client) } static const struct i2c_device_id tc74_id[] = { - { "tc74" }, - {} + { .name = "tc74" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc74_id); diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 0cbdb91698b1..b385ef64af75 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -407,8 +407,8 @@ static int thmc50_probe(struct i2c_client *client) } static const struct i2c_device_id thmc50_id[] = { - { "adm1022", adm1022 }, - { "thmc50", thmc50 }, + { .name = "adm1022", .driver_data = adm1022 }, + { .name = "thmc50", .driver_data = thmc50 }, { } }; MODULE_DEVICE_TABLE(i2c, thmc50_id); diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 3aa1a3fbeaa9..6bd1bed3cdb8 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -15,7 +15,8 @@ #include <linux/jiffies.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #define DRIVER_NAME "tmp102" @@ -313,7 +314,7 @@ static int tmp102_probe(struct i2c_client *client) if (!tmp102) return -ENOMEM; - of_property_read_string(dev->of_node, "label", &tmp102->label); + device_property_read_string(dev, "label", &tmp102->label); i2c_set_clientdata(client, tmp102); @@ -395,12 +396,12 @@ static int tmp102_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); static const struct i2c_device_id tmp102_id[] = { - { "tmp102" }, + { .name = "tmp102" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp102_id); -static const struct of_device_id __maybe_unused tmp102_of_match[] = { +static const struct of_device_id tmp102_of_match[] = { { .compatible = "ti,tmp102" }, { }, }; @@ -408,7 +409,7 @@ MODULE_DEVICE_TABLE(of, tmp102_of_match); static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, - .driver.of_match_table = of_match_ptr(tmp102_of_match), + .driver.of_match_table = tmp102_of_match, .driver.pm = pm_sleep_ptr(&tmp102_dev_pm_ops), .probe = tmp102_probe, .id_table = tmp102_id, diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c index 221bba8a215d..f13d24767575 100644 --- a/drivers/hwmon/tmp103.c +++ b/drivers/hwmon/tmp103.c @@ -194,7 +194,7 @@ static int tmp103_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume); static const struct i2c_device_id tmp103_id[] = { - { "tmp103" }, + { .name = "tmp103" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp103_id); diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index 3ea5f6485744..1c4a58855e2d 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -537,10 +537,10 @@ static int tmp108_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); static const struct i2c_device_id tmp108_i2c_ids[] = { - { "p3t1035", (unsigned long)&p3t1035_data }, - { "p3t1085", (unsigned long)&tmp108_data }, - { "tmp108", (unsigned long)&tmp108_data }, - {} + { .name = "p3t1035", .driver_data = (unsigned long)&p3t1035_data }, + { .name = "p3t1085", .driver_data = (unsigned long)&tmp108_data }, + { .name = "tmp108", .driver_data = (unsigned long)&tmp108_data }, + { } }; MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids); diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 07f596581c6e..ca0ff525ea29 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -90,11 +90,11 @@ static const u8 TMP432_STATUS_REG[] = { */ static const struct i2c_device_id tmp401_id[] = { - { "tmp401", tmp401 }, - { "tmp411", tmp411 }, - { "tmp431", tmp431 }, - { "tmp432", tmp432 }, - { "tmp435", tmp435 }, + { .name = "tmp401", .driver_data = tmp401 }, + { .name = "tmp411", .driver_data = tmp411 }, + { .name = "tmp431", .driver_data = tmp431 }, + { .name = "tmp432", .driver_data = tmp432 }, + { .name = "tmp435", .driver_data = tmp435 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -560,7 +560,7 @@ static int tmp401_init_client(struct tmp401_data *data) return ret; } - ret = of_property_read_u32(data->client->dev.of_node, "ti,n-factor", &nfactor); + ret = of_property_read_s32(data->client->dev.of_node, "ti,n-factor", &nfactor); if (!ret) { if (data->kind == tmp401) { dev_err(&data->client->dev, "ti,tmp401 does not support n-factor correction\n"); diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 2ea9d3e9553d..ed00ccfdd7b3 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -56,11 +56,11 @@ static const u8 TMP421_TEMP_LSB[MAX_CHANNELS] = { 0x10, 0x11, 0x12, 0x13 }; #define TMP442_DEVICE_ID 0x42 static const struct i2c_device_id tmp421_id[] = { - { "tmp421", 2 }, - { "tmp422", 3 }, - { "tmp423", 4 }, - { "tmp441", 2 }, - { "tmp442", 3 }, + { .name = "tmp421", .driver_data = 2 }, + { .name = "tmp422", .driver_data = 3 }, + { .name = "tmp423", .driver_data = 4 }, + { .name = "tmp441", .driver_data = 2 }, + { .name = "tmp442", .driver_data = 3 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp421_id); diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c index 98f2576d94c6..c3e031044d1e 100644 --- a/drivers/hwmon/tmp464.c +++ b/drivers/hwmon/tmp464.c @@ -65,8 +65,8 @@ static const u8 TMP464_THERM2_LIMIT[MAX_CHANNELS] = { #define TMP468_DEVICE_ID 0x0468 static const struct i2c_device_id tmp464_id[] = { - { "tmp464", TMP464_NUM_CHANNELS }, - { "tmp468", TMP468_NUM_CHANNELS }, + { .name = "tmp464", .driver_data = TMP464_NUM_CHANNELS }, + { .name = "tmp468", .driver_data = TMP468_NUM_CHANNELS }, { } }; MODULE_DEVICE_TABLE(i2c, tmp464_id); diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c index 5acbfd7d088d..b160aa045f89 100644 --- a/drivers/hwmon/tmp513.c +++ b/drivers/hwmon/tmp513.c @@ -611,8 +611,8 @@ static int tmp51x_init(struct tmp51x_data *data) } static const struct i2c_device_id tmp51x_id[] = { - { "tmp512", TMP512_MAX_CHANNELS }, - { "tmp513", TMP513_MAX_CHANNELS }, + { .name = "tmp512", .driver_data = TMP512_MAX_CHANNELS }, + { .name = "tmp513", .driver_data = TMP513_MAX_CHANNELS }, { } }; MODULE_DEVICE_TABLE(i2c, tmp51x_id); diff --git a/drivers/hwmon/tsc1641.c b/drivers/hwmon/tsc1641.c index 2b5d34bab146..fc53cd5bb6e0 100644 --- a/drivers/hwmon/tsc1641.c +++ b/drivers/hwmon/tsc1641.c @@ -721,7 +721,7 @@ static int tsc1641_probe(struct i2c_client *client) } static const struct i2c_device_id tsc1641_id[] = { - { "tsc1641", 0 }, + { .name = "tsc1641" }, { } }; MODULE_DEVICE_TABLE(i2c, tsc1641_id); diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index a5c03ed59c1f..ec421201049d 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -65,28 +65,28 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct via_cputemp_data *data = dev_get_drvdata(dev); - u32 eax, edx; + u64 val; int err; - err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); + err = rdmsrq_safe_on_cpu(data->id, data->msr_temp, &val); if (err) return -EAGAIN; - return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000); + return sprintf(buf, "%lu\n", ((unsigned long)val & 0xffffff) * 1000); } static ssize_t cpu0_vid_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct via_cputemp_data *data = dev_get_drvdata(dev); - u32 eax, edx; + u64 val; int err; - err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx); + err = rdmsrq_safe_on_cpu(data->id, data->msr_vid, &val); if (err) return -EAGAIN; - return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm)); + return sprintf(buf, "%d\n", vid_from_reg(~(val >> 32) & 0x7f, data->vrm)); } static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, SHOW_TEMP); @@ -112,7 +112,7 @@ static int via_cputemp_probe(struct platform_device *pdev) struct via_cputemp_data *data; struct cpuinfo_x86 *c = &cpu_data(pdev->id); int err; - u32 eax, edx; + u64 val; data = devm_kzalloc(&pdev->dev, sizeof(struct via_cputemp_data), GFP_KERNEL); @@ -143,7 +143,7 @@ static int via_cputemp_probe(struct platform_device *pdev) } /* test if we can access the TEMPERATURE MSR */ - err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); + err = rdmsrq_safe_on_cpu(data->id, data->msr_temp, &val); if (err) { dev_err(&pdev->dev, "Unable to access TEMPERATURE MSR, giving up\n"); diff --git a/drivers/hwmon/w83773g.c b/drivers/hwmon/w83773g.c index 401a28f55f93..54224dac10ae 100644 --- a/drivers/hwmon/w83773g.c +++ b/drivers/hwmon/w83773g.c @@ -34,7 +34,7 @@ static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 }; /* this is the number of sensors in the device */ static const struct i2c_device_id w83773_id[] = { - { "w83773g" }, + { .name = "w83773g" }, { } }; diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index f664c2152a6d..c40f84e53469 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1559,10 +1559,10 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) } static const struct i2c_device_id w83781d_ids[] = { - { "w83781d", w83781d, }, - { "w83782d", w83782d, }, - { "w83783s", w83783s, }, - { "as99127f", as99127f }, + { .name = "w83781d", .driver_data = w83781d }, + { .name = "w83782d", .driver_data = w83782d }, + { .name = "w83783s", .driver_data = w83783s }, + { .name = "as99127f", .driver_data = as99127f }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, w83781d_ids); diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 996e36951f9d..4a777430af5c 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -333,7 +333,7 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev); static void w83791d_init_client(struct i2c_client *client); static const struct i2c_device_id w83791d_id[] = { - { "w83791d" }, + { .name = "w83791d" }, { } }; MODULE_DEVICE_TABLE(i2c, w83791d_id); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index b0b5f60eea53..f715c79389a5 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -296,7 +296,7 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev); static void w83792d_init_client(struct i2c_client *client); static const struct i2c_device_id w83792d_id[] = { - { "w83792d" }, + { .name = "w83792d" }, { } }; MODULE_DEVICE_TABLE(i2c, w83792d_id); diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 24772cfbecb3..b1f906f06ab4 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -291,7 +291,7 @@ static void w83793_update_nonvolatile(struct device *dev); static struct w83793_data *w83793_update_device(struct device *dev); static const struct i2c_device_id w83793_id[] = { - { "w83793" }, + { .name = "w83793" }, { } }; MODULE_DEVICE_TABLE(i2c, w83793_id); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 5174db69db5e..c5ce0bf1b08e 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -2243,8 +2243,8 @@ static void w83795_remove(struct i2c_client *client) static const struct i2c_device_id w83795_id[] = { - { "w83795g", w83795g }, - { "w83795adg", w83795adg }, + { .name = "w83795g", .driver_data = w83795g }, + { .name = "w83795adg", .driver_data = w83795adg }, { } }; MODULE_DEVICE_TABLE(i2c, w83795_id); diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index df77b53a1b2f..e42506a3bcbe 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -74,7 +74,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); */ static const struct i2c_device_id w83l785ts_id[] = { - { "w83l785ts" }, + { .name = "w83l785ts" }, { } }; MODULE_DEVICE_TABLE(i2c, w83l785ts_id); diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index 1d9109ca1585..a72397083cc4 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -751,7 +751,7 @@ w83l786ng_probe(struct i2c_client *client) } static const struct i2c_device_id w83l786ng_id[] = { - { "w83l786ng" }, + { .name = "w83l786ng" }, { } }; MODULE_DEVICE_TABLE(i2c, w83l786ng_id); diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index ce71dcddfca2..0c8698c8fc5e 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -706,7 +706,7 @@ static int __init catu_init(void) { int ret; - ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver, THIS_MODULE); + ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver); tmc_etr_set_catu_ops(&etr_catu_buf_ops); return ret; } diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 46f247f73cf6..94ee24afdc6d 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1694,8 +1694,9 @@ static void __exit coresight_exit(void) module_init(coresight_init); module_exit(coresight_exit); -int coresight_init_driver(const char *drv, struct amba_driver *amba_drv, - struct platform_driver *pdev_drv, struct module *owner) +int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv, + struct platform_driver *pdev_drv, struct module *owner, + const char *mod_name) { int ret; @@ -1705,7 +1706,7 @@ int coresight_init_driver(const char *drv, struct amba_driver *amba_drv, return ret; } - ret = __platform_driver_register(pdev_drv, owner); + ret = __platform_driver_register(pdev_drv, owner, mod_name); if (!ret) return 0; @@ -1713,7 +1714,7 @@ int coresight_init_driver(const char *drv, struct amba_driver *amba_drv, amba_driver_unregister(amba_drv); return ret; } -EXPORT_SYMBOL_GPL(coresight_init_driver); +EXPORT_SYMBOL_GPL(coresight_init_driver_with_owner); void coresight_remove_driver(struct amba_driver *amba_drv, struct platform_driver *pdev_drv) diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 629614278e46..3a806c1d50ea 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -757,8 +757,7 @@ static struct platform_driver debug_platform_driver = { static int __init debug_init(void) { - return coresight_init_driver("debug", &debug_driver, &debug_platform_driver, - THIS_MODULE); + return coresight_init_driver("debug", &debug_driver, &debug_platform_driver); } static void __exit debug_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 3f56ceccd8c9..0abc11f0690c 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -412,8 +412,7 @@ static struct amba_driver dynamic_funnel_driver = { static int __init funnel_init(void) { - return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver, - THIS_MODULE); + return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver); } static void __exit funnel_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 07fc04f53b88..2f382de357ee 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -418,8 +418,7 @@ static struct amba_driver dynamic_replicator_driver = { static int __init replicator_init(void) { - return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver, - THIS_MODULE); + return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver); } static void __exit replicator_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index aca6cec7885a..4e860519a73f 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -1050,7 +1050,7 @@ static struct platform_driver stm_platform_driver = { static int __init stm_init(void) { - return coresight_init_driver("stm", &stm_driver, &stm_platform_driver, THIS_MODULE); + return coresight_init_driver("stm", &stm_driver, &stm_platform_driver); } static void __exit stm_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index c89fe996af23..bc5a133ada3e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -1046,7 +1046,7 @@ static struct platform_driver tmc_platform_driver = { static int __init tmc_init(void) { - return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver, THIS_MODULE); + return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver); } static void __exit tmc_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index 96a25877b824..9e8de4323d28 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -344,7 +344,7 @@ static struct platform_driver itnoc_driver = { static int __init tnoc_init(void) { - return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver, THIS_MODULE); + return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver); } static void __exit tnoc_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index eaf7210af648..8464edbba2d4 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -1563,8 +1563,7 @@ static struct platform_driver static_tpdm_driver = { static int __init tpdm_init(void) { - return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver, - THIS_MODULE); + return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver); } static void __exit tpdm_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index b8560b140e0f..7b029d2eb389 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -310,7 +310,7 @@ static struct platform_driver tpiu_platform_driver = { static int __init tpiu_init(void) { - return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver, THIS_MODULE); + return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver); } static void __exit tpiu_exit(void) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8c935f867a37..3123ab75600b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -417,7 +417,7 @@ config I2C_ASPEED config I2C_AT91 tristate "Atmel AT91 I2C Two-Wire interface (TWI)" - depends on ARCH_MICROCHIP || COMPILE_TEST + depends on ARCH_MICROCHIP || MCHP_LAN966X_PCI || COMPILE_TEST help This supports the use of the I2C interface on Atmel AT91 processors. @@ -794,6 +794,7 @@ config I2C_K1 tristate "SpacemiT K1 I2C adapter" depends on ARCH_SPACEMIT || COMPILE_TEST depends on OF + default ARCH_SPACEMIT help This option enables support for the I2C interface on the SpacemiT K1 platform. @@ -850,6 +851,17 @@ config I2C_LS2X This driver can also be built as a module. If so, the module will be called i2c-ls2x. +config I2C_LS2X_V2 + tristate "Loongson-2 Fast Speed I2C adapter" + depends on LOONGARCH || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes to this option, support will be included for the + I2C interface on the Loongson-2K0300 SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-ls2x-v2. + config I2C_MLXBF tristate "Mellanox BlueField I2C controller" depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 547123ab351f..3755c54b3d82 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_I2C_KEBA) += i2c-keba.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o +obj-$(CONFIG_I2C_LS2X_V2) += i2c-ls2x-v2.o obj-$(CONFIG_I2C_MESON) += i2c-meson.o obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c index 9d8838bbd938..d4f287b31fd7 100644 --- a/drivers/i2c/busses/i2c-bcm-kona.c +++ b/drivers/i2c/busses/i2c-bcm-kona.c @@ -427,7 +427,7 @@ static int bcm_kona_i2c_write_fifo_single(struct bcm_kona_i2c_dev *dev, return -EREMOTEIO; } - /* Check if a timeout occured */ + /* Check if a timeout occurred */ if (!time_left) { dev_err(dev->device, "completion timed out\n"); return -EREMOTEIO; diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 0fb728ade92e..da8770182a18 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -1635,6 +1635,25 @@ static void cdns_i2c_remove(struct platform_device *pdev) reset_control_assert(id->reset); } +/** + * cdns_i2c_shutdown - Prepare I2C controller for system shutdown + * @pdev: Handle to the platform device structure + * + * Mark the adapter as suspended and reset the controller to ensure a clean + * handoff during system reboot or kexec. + */ +static void cdns_i2c_shutdown(struct platform_device *pdev) +{ + struct cdns_i2c *id = platform_get_drvdata(pdev); + + /* Mark the adapter as suspended to prevent further I2C transfers */ + i2c_mark_adapter_suspended(&id->adap); + + /* Reset the controller state if active - clocks are disabled when suspended */ + if (!pm_runtime_status_suspended(&pdev->dev)) + cdns_i2c_master_reset(&id->adap); +} + static struct platform_driver cdns_i2c_drv = { .driver = { .name = DRIVER_NAME, @@ -1643,6 +1662,7 @@ static struct platform_driver cdns_i2c_drv = { }, .probe = cdns_i2c_probe, .remove = cdns_i2c_remove, + .shutdown = cdns_i2c_shutdown, }; module_platform_driver(cdns_i2c_drv); diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index a773ba082321..66c23535656b 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -117,7 +117,7 @@ /* timeout for pm runtime autosuspend */ #define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */ -#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100 +#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100000 struct davinci_i2c_dev { struct device *dev; diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 4dc57fd56170..e4dfa2ec58bb 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -633,6 +633,14 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev) abort_needed = (raw_intr_stats & DW_IC_INTR_MST_ON_HOLD) || (ic_stats & DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY); + + /* + * If we are in target mode and there is activity, we should also + * trigger an abort to clear the internal state machines. + */ + if (dev->mode == DW_IC_SLAVE && (ic_stats & DW_IC_STATUS_SLAVE_ACTIVITY)) + abort_needed = true; + if (abort_needed) { if (!(enable & DW_IC_ENABLE_ENABLE)) { regmap_write(dev->map, DW_IC_ENABLE, DW_IC_ENABLE_ENABLE); @@ -958,8 +966,8 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) * registered to the device core and immediate resume in case bus has * registered I2C slaves that do I2C transfers in their probe. */ - ACQUIRE(pm_runtime_noresume, pm)(dev->dev); - ret = ACQUIRE_ERR(pm_runtime_noresume, &pm); + PM_RUNTIME_ACQUIRE(dev->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; @@ -1028,5 +1036,29 @@ EXPORT_GPL_DEV_PM_OPS(i2c_dw_dev_pm_ops) = { RUNTIME_PM_OPS(i2c_dw_runtime_suspend, i2c_dw_runtime_resume, NULL) }; +void i2c_dw_shutdown(struct dw_i2c_dev *dev) +{ + unsigned int con; + + /* + * We only need to handle shutdown for target mode to ensure + * we NACK any incoming controller requests. Controller mode cleanup + * is handled after each transfer in i2c_dw_xfer(). + */ + if (dev->mode != DW_IC_SLAVE) + return; + + /* + * To quickly NACK the controller during shutdown, we set the target + * disable bit while the controller is still enabled. + */ + regmap_read(dev->map, DW_IC_CON, &con); + con |= DW_IC_CON_SLAVE_DISABLE; + regmap_write(dev->map, DW_IC_CON, con); + + i2c_dw_disable(dev); +} +EXPORT_SYMBOL_GPL(i2c_dw_shutdown); + MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 9d8d104cc391..c71aa2dd368d 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -417,6 +417,7 @@ static inline void i2c_dw_configure(struct dw_i2c_dev *dev) int i2c_dw_probe(struct dw_i2c_dev *dev); int i2c_dw_init(struct dw_i2c_dev *dev); +void i2c_dw_shutdown(struct dw_i2c_dev *dev); void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode); #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index de929b91d5ea..7a301c8b604e 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -785,18 +785,25 @@ __i2c_dw_xfer_one_part(struct dw_i2c_dev *dev, struct i2c_msg *msgs, size_t num) * IC_RAW_INTR_STAT.MASTER_ON_HOLD holding SCL low. Check if * controller is still ACTIVE before disabling I2C. */ - if (i2c_dw_is_controller_active(dev)) - dev_err(dev->dev, "controller active\n"); - - /* - * We must disable the adapter before returning and signaling the end - * of the current transfer. Otherwise the hardware might continue - * generating interrupts which in turn causes a race condition with - * the following transfer. Needs some more investigation if the - * additional interrupts are a hardware bug or this driver doesn't - * handle them correctly yet. - */ - __i2c_dw_disable_nowait(dev); + if (i2c_dw_is_controller_active(dev)) { + /* + * If the controller is still active after the timeout, attempt a + * bus recovery to clear any potentially locked state. + */ + dev_err(dev->dev, "controller active after xfer, recovering\n"); + i2c_recover_bus(&dev->adapter); + i2c_dw_init(dev); + } else { + /* + * We must disable the adapter before returning and signaling the end + * of the current transfer. Otherwise the hardware might continue + * generating interrupts which in turn causes a race condition with + * the following transfer. Needs some more investigation if the + * additional interrupts are a hardware bug or this driver doesn't + * handle them correctly yet. + */ + __i2c_dw_disable_nowait(dev); + } if (dev->msg_err) return dev->msg_err; diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index f21f9877c040..468287922363 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -308,58 +308,72 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev) static const struct pci_device_id i2c_designware_pci_ids[] = { /* Medfield */ - { PCI_VDEVICE(INTEL, 0x0817), medfield }, - { PCI_VDEVICE(INTEL, 0x0818), medfield }, - { PCI_VDEVICE(INTEL, 0x0819), medfield }, - { PCI_VDEVICE(INTEL, 0x082C), medfield }, - { PCI_VDEVICE(INTEL, 0x082D), medfield }, - { PCI_VDEVICE(INTEL, 0x082E), medfield }, + { PCI_VDEVICE(INTEL, 0x0817), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x0818), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x0819), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082C), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082D), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082E), .driver_data = medfield }, /* Merrifield */ - { PCI_VDEVICE(INTEL, 0x1195), merrifield }, - { PCI_VDEVICE(INTEL, 0x1196), merrifield }, + { PCI_VDEVICE(INTEL, 0x1195), .driver_data = merrifield }, + { PCI_VDEVICE(INTEL, 0x1196), .driver_data = merrifield }, /* Baytrail */ - { PCI_VDEVICE(INTEL, 0x0F41), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F42), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F43), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F44), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F45), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F46), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F47), baytrail }, + { PCI_VDEVICE(INTEL, 0x0F41), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F42), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F43), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F44), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F45), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F46), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F47), .driver_data = baytrail }, /* Haswell */ - { PCI_VDEVICE(INTEL, 0x9c61), haswell }, - { PCI_VDEVICE(INTEL, 0x9c62), haswell }, + { PCI_VDEVICE(INTEL, 0x9c61), .driver_data = haswell }, + { PCI_VDEVICE(INTEL, 0x9c62), .driver_data = haswell }, /* Braswell / Cherrytrail */ - { PCI_VDEVICE(INTEL, 0x22C1), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C2), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C3), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C4), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C5), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C6), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C7), cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C1), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C2), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C3), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C4), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C5), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C6), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C7), .driver_data = cherrytrail }, /* Elkhart Lake (PSE I2C) */ - { PCI_VDEVICE(INTEL, 0x4bb9), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bba), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbb), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbc), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbd), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bb9), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bba), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbb), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbc), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbd), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbe), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbf), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bc0), .driver_data = elkhartlake }, /* AMD NAVI */ - { PCI_VDEVICE(ATI, 0x7314), navi_amd }, - { PCI_VDEVICE(ATI, 0x73a4), navi_amd }, - { PCI_VDEVICE(ATI, 0x73e4), navi_amd }, - { PCI_VDEVICE(ATI, 0x73c4), navi_amd }, - { PCI_VDEVICE(ATI, 0x7444), navi_amd }, - { PCI_VDEVICE(ATI, 0x7464), navi_amd }, - {} + { PCI_VDEVICE(ATI, 0x7314), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73a4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73e4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73c4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x7444), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x7464), .driver_data = navi_amd }, + { } }; MODULE_DEVICE_TABLE(pci, i2c_designware_pci_ids); +static void i2c_dw_pci_shutdown(struct pci_dev *pdev) +{ + struct dw_i2c_dev *i_dev; + + i_dev = pci_get_drvdata(pdev); + if (!i_dev) + return; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + i2c_dw_shutdown(i_dev); +} + static struct pci_driver dw_i2c_driver = { .name = DRIVER_NAME, .probe = i2c_dw_pci_probe, .remove = i2c_dw_pci_remove, + .shutdown = i2c_dw_pci_shutdown, .driver = { .pm = pm_ptr(&i2c_dw_dev_pm_ops), }, diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 3351c4a9ef11..6d6e81242f74 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -279,6 +279,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { { "INT3432", 0 }, { "INT3433", 0 }, { "INTC10EF", 0 }, + { "LECA0003", 0 }, {} }; MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); @@ -289,9 +290,23 @@ static const struct platform_device_id dw_i2c_platform_ids[] = { }; MODULE_DEVICE_TABLE(platform, dw_i2c_platform_ids); +static void dw_i2c_plat_shutdown(struct platform_device *pdev) +{ + struct dw_i2c_dev *i_dev; + + i_dev = platform_get_drvdata(pdev); + if (!i_dev) + return; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + i2c_dw_shutdown(i_dev); +} + static struct platform_driver dw_i2c_driver = { .probe = dw_i2c_plat_probe, .remove = dw_i2c_plat_remove, + .shutdown = dw_i2c_plat_shutdown, .driver = { .name = "i2c_designware", .of_match_table = dw_i2c_of_match, diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index f83238868802..8c67ab4f2aad 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -169,11 +169,11 @@ static DEFINE_MUTEX(pch_mutex); #define PCI_DEVICE_ID_ML7831_I2C 0x8817 static const struct pci_device_id pch_pcidev_id[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), 1, }, - {0,} + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), .driver_data = 2 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), .driver_data = 1 }, + { } }; MODULE_DEVICE_TABLE(pci, pch_pcidev_id); diff --git a/drivers/i2c/busses/i2c-icy.c b/drivers/i2c/busses/i2c-icy.c index febcb6f01d4d..55496e48ccd1 100644 --- a/drivers/i2c/busses/i2c-icy.c +++ b/drivers/i2c/busses/i2c-icy.c @@ -193,8 +193,8 @@ static void icy_remove(struct zorro_dev *z) } static const struct zorro_device_id icy_zorro_tbl[] = { - { ZORRO_ID(VMC, 15, 0), }, - { 0 } + { .id = ZORRO_ID(VMC, 15, 0) }, + { } }; MODULE_DEVICE_TABLE(zorro, icy_zorro_tbl); diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index a01c23696481..cd4da50c4dd9 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -1383,55 +1383,66 @@ static int lpi2c_imx_init_recovery_info(struct lpi2c_imx_struct *lpi2c_imx, return 0; } -static void dma_exit(struct device *dev, struct lpi2c_imx_dma *dma) -{ - if (dma->chan_rx) - dma_release_channel(dma->chan_rx); - - if (dma->chan_tx) - dma_release_channel(dma->chan_tx); - - devm_kfree(dev, dma); -} - static int lpi2c_dma_init(struct device *dev, dma_addr_t phy_addr) { struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); struct lpi2c_imx_dma *dma; + void *group; int ret; - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) + /* + * Open a devres group so that all resources allocated within + * this function can be released together if DMA init fails but + * probe continues in PIO mode. + */ + group = devres_open_group(dev, NULL, GFP_KERNEL); + if (!group) return -ENOMEM; + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + goto release_group; + } + dma->phy_addr = phy_addr; /* Prepare for TX DMA: */ - dma->chan_tx = dma_request_chan(dev, "tx"); + dma->chan_tx = devm_dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { ret = PTR_ERR(dma->chan_tx); if (ret != -ENODEV && ret != -EPROBE_DEFER) dev_err(dev, "can't request DMA tx channel (%d)\n", ret); - dma->chan_tx = NULL; - goto dma_exit; + goto release_group; } /* Prepare for RX DMA: */ - dma->chan_rx = dma_request_chan(dev, "rx"); + dma->chan_rx = devm_dma_request_chan(dev, "rx"); if (IS_ERR(dma->chan_rx)) { ret = PTR_ERR(dma->chan_rx); if (ret != -ENODEV && ret != -EPROBE_DEFER) dev_err(dev, "can't request DMA rx channel (%d)\n", ret); - dma->chan_rx = NULL; - goto dma_exit; + goto release_group; } + /* + * DMA init succeeded. Remove the group marker but keep all resources + * bound to the device, they will be freed at device removal. + */ + devres_remove_group(dev, group); + lpi2c_imx->can_use_dma = true; lpi2c_imx->dma = dma; return 0; -dma_exit: - dma_exit(dev, dma); +release_group: + /* + * DMA init failed. Release ALL resources allocated inside this + * group (dma memory, TX channel if already acquired, etc.) so + * that a successful PIO-mode probe does not hold unused resources + * for the entire device lifetime. + */ + devres_release_group(dev, group); return ret; } diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index a208fefd3c3b..28313d0fad37 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1892,9 +1892,15 @@ static void i2c_imx_remove(struct platform_device *pdev) static int i2c_imx_runtime_suspend(struct device *dev) { struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + return ret; clk_disable(i2c_imx->clk); - return pinctrl_pm_select_sleep_state(dev); + + return 0; } static int i2c_imx_runtime_resume(struct device *dev) @@ -1907,10 +1913,13 @@ static int i2c_imx_runtime_resume(struct device *dev) return ret; ret = clk_enable(i2c_imx->clk); - if (ret) + if (ret) { dev_err(dev, "can't enable I2C clock, ret=%d\n", ret); + pinctrl_pm_select_sleep_state(dev); + return ret; + } - return ret; + return 0; } static int i2c_imx_suspend(struct device *dev) diff --git a/drivers/i2c/busses/i2c-ls2x-v2.c b/drivers/i2c/busses/i2c-ls2x-v2.c new file mode 100644 index 000000000000..517760d70169 --- /dev/null +++ b/drivers/i2c/busses/i2c-ls2x-v2.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Loongson-2K0300 I2C controller driver + * + * Copyright (C) 2025-2026 Loongson Technology Corporation Limited + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/units.h> + +/* Loongson-2 fast I2C offset registers */ +#define LOONGSON2_I2C_CR1 0x00 /* I2C control 1 register */ +#define LOONGSON2_I2C_CR2 0x04 /* I2C control 2 register */ +#define LOONGSON2_I2C_OAR 0x08 /* I2C slave address register */ +#define LOONGSON2_I2C_DR 0x10 /* I2C data register */ +#define LOONGSON2_I2C_SR1 0x14 /* I2C status 1 register */ +#define LOONGSON2_I2C_SR2 0x18 /* I2C status 2 register */ +#define LOONGSON2_I2C_CCR 0x1c /* I2C clock control register */ +#define LOONGSON2_I2C_TRISE 0x20 /* I2C trise register */ +#define LOONGSON2_I2C_FLTR 0x24 + +/* Bitfields of I2C control 1 register */ +#define LOONGSON2_I2C_CR1_PE BIT(0) /* Peripheral enable */ +#define LOONGSON2_I2C_CR1_START BIT(8) /* Start generation */ +#define LOONGSON2_I2C_CR1_STOP BIT(9) /* Stop generation */ +#define LOONGSON2_I2C_CR1_ACK BIT(10) /* Acknowledge enable */ +#define LOONGSON2_I2C_CR1_POS BIT(11) /* Acknowledge/PEC Position (for data reception) */ + +#define LOONGSON2_I2C_CR1_OP_MASK (LOONGSON2_I2C_CR1_START | LOONGSON2_I2C_CR1_STOP) + +/* Bitfields of I2C control 2 register */ +#define LOONGSON2_I2C_CR2_FREQ GENMASK(5, 0) /* APB Clock Frequency in MHz */ +#define LOONGSON2_I2C_CR2_ITERREN BIT(8) /* Fault-Class Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITEVTEN BIT(9) /* Event-Based Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITBUFEN BIT(10) /* Cache-Class Interrupt Enable */ + +#define LOONGSON2_I2C_CR2_INT_MASK \ + (LOONGSON2_I2C_CR2_ITBUFEN | LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN) + +/* Bitfields of I2C status 1 register */ +#define LOONGSON2_I2C_SR1_SB BIT(0) /* Start bit (Master mode) */ +#define LOONGSON2_I2C_SR1_ADDR BIT(1) /* Address sent (master mode) */ +#define LOONGSON2_I2C_SR1_BTF BIT(2) /* Byte transfer finished */ +#define LOONGSON2_I2C_SR1_RXNE BIT(6) /* Data register not empty (receivers) */ +#define LOONGSON2_I2C_SR1_TXE BIT(7) /* Data register empty (transmitters) */ +#define LOONGSON2_I2C_SR1_BERR BIT(8) /* Bus error */ +#define LOONGSON2_I2C_SR1_ARLO BIT(9) /* Arbitration lost (master mode) */ +#define LOONGSON2_I2C_SR1_AF BIT(10) /* Acknowledge failure */ + +#define LOONGSON2_I2C_SR1_ITEVTEN_MASK \ + (LOONGSON2_I2C_SR1_BTF | LOONGSON2_I2C_SR1_ADDR | LOONGSON2_I2C_SR1_SB) +#define LOONGSON2_I2C_SR1_ITBUFEN_MASK (LOONGSON2_I2C_SR1_TXE | LOONGSON2_I2C_SR1_RXNE) +#define LOONGSON2_I2C_SR1_ITERREN_MASK \ + (LOONGSON2_I2C_SR1_AF | LOONGSON2_I2C_SR1_ARLO | LOONGSON2_I2C_SR1_BERR) + +/* Bitfields of I2C status 2 register */ +#define LOONGSON2_I2C_SR2_MSL BIT(0) /* Master/slave */ +#define LOONGSON2_I2C_SR2_BUSY BIT(1) /* Bus busy */ +#define LOONGSON2_I2C_SR2_TRA BIT(2) /* Transmitter/receiver */ +#define LOONGSON2_I2C_SR2_GENCALL BIT(4) /* General call address (Slave mode) */ + +/* Bitfields of I2C clock control register */ +#define LOONGSON2_I2C_CCR_CCR GENMASK(11, 0) +#define LOONGSON2_I2C_CCR_DUTY BIT(14) +#define LOONGSON2_I2C_CCR_FS BIT(15) + +/* Bitfields of I2C trise register */ +#define LOONGSON2_I2C_TRISE_SCL GENMASK(5, 0) + +#define LOONGSON2_I2C_FREE_SLEEP_US 10 +#define LOONGSON2_I2C_FREE_TIMEOUT_US (2 * USEC_PER_MSEC) + +/** + * struct loongson2_i2c_msg - client specific data + * @buf: data buffer + * @count: number of bytes to be transferred + * @result: result of the transfer + * @addr: 8-bit slave addr, including r/w bit + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct loongson2_i2c_msg { + u8 *buf; + u32 count; + int result; + u8 addr; + bool stop; +}; + +/** + * struct loongson2_i2c_priv - private data of the controller + * @adapter: I2C adapter for this controller + * @complete: completion of I2C message + * @clk: hw i2c clock + * @regmap: regmap of the I2C device + * @parent_rate_MHz: I2C clock parent rate + * @msg: I2C transfer information + */ +struct loongson2_i2c_priv { + struct i2c_adapter adapter; + struct completion complete; + struct clk *clk; + struct regmap *regmap; + unsigned long parent_rate_MHz; + struct loongson2_i2c_msg msg; +}; + +static void loongson2_i2c_disable_irq(struct loongson2_i2c_priv *priv) +{ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_INT_MASK, 0); +} + +static void loongson2_i2c_read_msg(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + u32 rbuf; + + regmap_read(priv->regmap, LOONGSON2_I2C_DR, &rbuf); + *msg->buf++ = rbuf; + msg->count--; +} + +static void loongson2_i2c_write_msg(struct loongson2_i2c_priv *priv, u8 byte) +{ + regmap_write(priv->regmap, LOONGSON2_I2C_DR, byte); +} + +static void loongson2_i2c_terminate_xfer(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + loongson2_i2c_disable_irq(priv); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_write(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + if (msg->count) { + loongson2_i2c_write_msg(priv, *msg->buf++); + if (!--msg->count) + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITBUFEN, 0); + } else { + loongson2_i2c_terminate_xfer(priv); + } +} + +static void loongson2_i2c_handle_rx_addr(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 0: + loongson2_i2c_terminate_xfer(priv); + break; + case 1: + /* Enable NACK and reset POS (Acknowledge position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, + LOONGSON2_I2C_CR1_ACK | LOONGSON2_I2C_CR1_POS, 0); + /* Set STOP or RepSTART */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + break; + case 2: + /* Enable NACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + /* Set POS (NACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, + LOONGSON2_I2C_CR1_POS); + break; + + default: + /* Enable ACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, + LOONGSON2_I2C_CR1_ACK); + /* Reset POS (ACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, 0); + break; + } +} + +static void loongson2_i2c_isr_error(u32 status, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct loongson2_i2c_msg *msg = &priv->msg; + + /* Arbitration lost */ + if (status & LOONGSON2_I2C_SR1_ARLO) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_ARLO, 0); + msg->result = -EAGAIN; + goto out; + } + + /* + * Acknowledge failure: + * In master transmitter mode a Stop must be generated by software. + */ + if (status & LOONGSON2_I2C_SR1_AF) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_STOP, + LOONGSON2_I2C_CR1_STOP); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_AF, 0); + msg->result = -EIO; + goto out; + } + + /* Bus error */ + if (status & LOONGSON2_I2C_SR1_BERR) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_BERR, 0); + msg->result = -EIO; + goto out; + } + +out: + loongson2_i2c_disable_irq(priv); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_read(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 1: + loongson2_i2c_disable_irq(priv); + loongson2_i2c_read_msg(priv); + complete(&priv->complete); + break; + case 2: + case 3: + /* + * For 2-byte/3-byte reception and for N-byte reception with N > 3, we have to + * wait for byte transferred finished event before reading data. + * Just disable buffer interrupt in order to avoid another system preemption due + * to RX not empty event. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, 0); + break; + default: + /* + * For N byte reception with N > 3 we directly read data register + * until N-2 data. + */ + loongson2_i2c_read_msg(priv); + break; + } +} + +static void loongson2_i2c_handle_rx_done(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 2: + /* + * The STOP/START bit has to be set before reading the last two bytes. + * After that, we could read the last two bytes. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + + for (unsigned int i = msg->count; i > 0; i--) + loongson2_i2c_read_msg(priv); + + loongson2_i2c_disable_irq(priv); + + complete(&priv->complete); + break; + case 3: + /* + * In order to generate the NACK after the last received data byte, enable NACK + * before reading N-2 data. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + loongson2_i2c_read_msg(priv); + break; + default: + loongson2_i2c_read_msg(priv); + break; + } +} + +static irqreturn_t loongson2_i2c_isr_event(int irq, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct device *dev = regmap_get_device(priv->regmap); + struct loongson2_i2c_msg *msg = &priv->msg; + u32 status, ien, event, cr2, possible_status; + + regmap_read(priv->regmap, LOONGSON2_I2C_SR1, &status); + if (status & LOONGSON2_I2C_SR1_ITERREN_MASK) { + loongson2_i2c_isr_error(status, data); + return IRQ_NONE; + } + + regmap_read(priv->regmap, LOONGSON2_I2C_CR2, &cr2); + ien = cr2 & LOONGSON2_I2C_CR2_INT_MASK; + + /* Update possible_status if buffer interrupt is enabled */ + possible_status = LOONGSON2_I2C_SR1_ITEVTEN_MASK; + if (ien & LOONGSON2_I2C_CR2_ITBUFEN) + possible_status |= LOONGSON2_I2C_SR1_ITBUFEN_MASK; + + event = status & possible_status; + if (!event) { + dev_dbg(dev, "spurious evt IRQ (status=0x%08x, ien=0x%08x)\n", status, ien); + return IRQ_NONE; + } + + /* Start condition generated */ + if (event & LOONGSON2_I2C_SR1_SB) + loongson2_i2c_write_msg(priv, msg->addr); + + /* I2C Address sent */ + if (event & LOONGSON2_I2C_SR1_ADDR) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_addr(priv); + /* Clear ADDR flag */ + regmap_read(priv->regmap, LOONGSON2_I2C_SR2, &status); + /* Enable buffer interrupts for RX/TX not empty events */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, + LOONGSON2_I2C_CR2_ITBUFEN); + } + + /* TX empty */ + if ((event & LOONGSON2_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD)) + loongson2_i2c_handle_write(priv); + + /* RX not empty */ + if ((event & LOONGSON2_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD)) + loongson2_i2c_handle_read(priv); + + /* + * The BTF (Byte Transfer finished) event occurs when: + * - in reception: a new byte is received in the shift register + * but the previous byte has not been read yet from data register + * - in transmission: a new byte should be sent but the data register + * has not been written yet + */ + if (event & LOONGSON2_I2C_SR1_BTF) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_done(priv); + else + loongson2_i2c_handle_write(priv); + } + + return IRQ_HANDLED; +} + +static int loongson2_i2c_xfer_msg(struct loongson2_i2c_priv *priv, struct i2c_msg *msg, + bool is_stop) +{ + struct loongson2_i2c_msg *l_msg = &priv->msg; + unsigned long timeout; + + l_msg->addr = i2c_8bit_addr_from_msg(msg); + l_msg->buf = msg->buf; + l_msg->count = msg->len; + l_msg->stop = is_stop; + l_msg->result = 0; + + reinit_completion(&priv->complete); + + /* Enable events and errors interrupts */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN); + + timeout = wait_for_completion_timeout(&priv->complete, priv->adapter.timeout); + if (!timeout) + return -ETIMEDOUT; + + return l_msg->result; +} + +static int loongson2_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct loongson2_i2c_priv *priv = i2c_get_adapdata(i2c_adap); + struct device *dev = regmap_get_device(priv->regmap); + unsigned int status; + int ret; + + /* Wait I2C bus free */ + ret = regmap_read_poll_timeout(priv->regmap, LOONGSON2_I2C_SR2, status, + !(status & LOONGSON2_I2C_SR2_BUSY), + LOONGSON2_I2C_FREE_SLEEP_US, + LOONGSON2_I2C_FREE_TIMEOUT_US); + if (ret) { + dev_dbg(dev, "The I2C bus is busy now.\n"); + return ret; + } + + /* Start generation */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_START, + LOONGSON2_I2C_CR1_START); + + for (unsigned int i = 0; i < num; i++) { + ret = loongson2_i2c_xfer_msg(priv, &msgs[i], i == num - 1); + if (ret < 0) + return ret; + } + + return num; +} + +static u32 loongson2_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm loongson2_i2c_algo = { + .xfer = loongson2_i2c_xfer, + .functionality = loongson2_i2c_func, +}; + +static int loongson2_i2c_adjust_bus_speed(struct loongson2_i2c_priv *priv) +{ + struct device *dev = regmap_get_device(priv->regmap); + struct i2c_timings i2c_t; + u32 val, freq_MHz, ccr; + + i2c_parse_fw_timings(dev, &i2c_t, true); + priv->parent_rate_MHz = clk_get_rate(priv->clk); + + if (i2c_t.bus_freq_hz == I2C_MAX_STANDARD_MODE_FREQ) { + /* Select Standard mode */ + ccr = 0; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 2); + } else if (i2c_t.bus_freq_hz == I2C_MAX_FAST_MODE_FREQ) { + /* Select Fast mode */ + ccr = LOONGSON2_I2C_CCR_FS; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 3); + } else { + return dev_err_probe(dev, -EINVAL, "Unsupported speed (%uHz)\n", i2c_t.bus_freq_hz); + } + + FIELD_MODIFY(LOONGSON2_I2C_CCR_CCR, &ccr, val); + regmap_write(priv->regmap, LOONGSON2_I2C_CCR, ccr); + + freq_MHz = DIV_ROUND_UP(priv->parent_rate_MHz, HZ_PER_MHZ); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_FREQ, + FIELD_GET(LOONGSON2_I2C_CR2_FREQ, freq_MHz)); + + regmap_update_bits(priv->regmap, LOONGSON2_I2C_TRISE, LOONGSON2_I2C_TRISE_SCL, + LOONGSON2_I2C_TRISE_SCL); + + /* Enable I2C */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_PE, + LOONGSON2_I2C_CR1_PE); + + return 0; +} + +static const struct regmap_config loongson2_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_I2C_TRISE, +}; + +static int loongson2_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson2_i2c_priv *priv; + struct i2c_adapter *adap; + void __iomem *base; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, &loongson2_i2c_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), "Failed to init regmap.\n"); + + priv->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock.\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adap = &priv->adapter; + adap->retries = 5; + adap->nr = pdev->id; + adap->dev.parent = dev; + adap->owner = THIS_MODULE; + adap->algo = &loongson2_i2c_algo; + adap->timeout = 2 * HZ; + device_set_node(&adap->dev, dev_fwnode(dev)); + i2c_set_adapdata(adap, priv); + strscpy(adap->name, pdev->name); + init_completion(&priv->complete); + platform_set_drvdata(pdev, priv); + + ret = loongson2_i2c_adjust_bus_speed(priv); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, loongson2_i2c_isr_event, IRQF_SHARED, pdev->name, priv); + if (ret) + return ret; + + return devm_i2c_add_adapter(dev, adap); +} + +static const struct of_device_id loongson2_i2c_id_table[] = { + { .compatible = "loongson,ls2k0300-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, loongson2_i2c_id_table); + +static struct platform_driver loongson2_i2c_driver = { + .driver = { + .name = "loongson2-i2c-v2", + .of_match_table = loongson2_i2c_id_table, + }, + .probe = loongson2_i2c_probe, +}; +module_platform_driver(loongson2_i2c_driver); + +MODULE_DESCRIPTION("Loongson-2K0300 I2C bus driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 08c9091a1e35..4e07babea9c3 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -108,6 +108,14 @@ enum mxs_i2c_devtype { * @cmd_complete: completion object for transaction wait * @cmd_err: error code for last transaction * @adapter: i2c subsystem adapter node + * @timing0: I2C TIMING0 register value + * @timing1: I2C TIMING1 register value + * @timing2: I2C TIMING2 register value + * @dmach: DMA channel + * @pio_data: PIO data for DMA + * @addr_data: address data for DMA + * @sg_io: scatterlist for I/O + * @dma_read: flag indicating DMA read */ struct mxs_i2c_dev { struct device *dev; diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index f3ccfbbc4bea..01e440b6585d 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -660,8 +660,8 @@ static void cci_remove(struct platform_device *pdev) if (cci->master[i].cci) { i2c_del_adapter(&cci->master[i].adap); of_node_put(cci->master[i].adap.dev.of_node); + cci_halt(cci, i); } - cci_halt(cci, i); } disable_irq(cci->irq); diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index a482a4c60744..d2f5055b0b10 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -110,7 +110,6 @@ struct geni_i2c_dev { struct clk *core_clk; u32 clk_freq_out; const struct geni_i2c_clk_fld *clk_fld; - int suspended; void *dma_buf; size_t xfer_len; dma_addr_t dma_addr; @@ -1143,7 +1142,6 @@ static int geni_i2c_probe(struct platform_device *pdev) if (ret) goto err_dma; - gi2c->suspended = 1; pm_runtime_set_suspended(gi2c->se.dev); pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY); pm_runtime_use_autosuspend(gi2c->se.dev); @@ -1200,9 +1198,6 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) if (ret) { enable_irq(gi2c->irq); return ret; - - } else { - gi2c->suspended = 1; } clk_disable_unprepare(gi2c->core_clk); @@ -1228,7 +1223,6 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) goto out_clk_disable; enable_irq(gi2c->irq); - gi2c->suspended = 0; return 0; @@ -1243,21 +1237,25 @@ out_icc_disable: static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret; i2c_mark_adapter_suspended(&gi2c->adap); - if (!gi2c->suspended) { - geni_i2c_runtime_suspend(dev); - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_enable(dev); - } - return 0; + ret = pm_runtime_force_suspend(dev); + if (ret) + i2c_mark_adapter_resumed(&gi2c->adap); + + return ret; } static int __maybe_unused geni_i2c_resume_noirq(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; i2c_mark_adapter_resumed(&gi2c->adap); return 0; diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index 9e3595b3623e..6d2ebf67dd62 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -725,8 +725,10 @@ static int riic_i2c_resume_noirq(struct device *dev) return ret; ret = pm_runtime_force_resume(dev); - if (ret) + if (ret) { + reset_control_assert(riic->rstc); return ret; + } ret = riic_init_hw(riic); if (ret) { diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index a19c3d251804..3d0638c2bc51 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -431,24 +431,23 @@ static int sis630_setup(struct pci_dev *sis630_dev) in acpi io space and read acpi base addr */ if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, &b)) { - dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n"); - retval = -ENODEV; + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't read bios ctl reg\n"); goto exit; } /* if ACPI already enabled , do nothing */ if (!(b & 0x80) && pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) { - dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n"); - retval = -ENODEV; + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't enable ACPI\n"); goto exit; } /* Determine the ACPI base address */ if (pci_read_config_word(sis630_dev, SIS630_ACPI_BASE_REG, &acpi_base)) { - dev_err(&sis630_dev->dev, - "Error: Can't determine ACPI base address\n"); - retval = -ENODEV; + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't determine ACPI base address\n"); goto exit; } @@ -469,11 +468,10 @@ static int sis630_setup(struct pci_dev *sis630_dev) /* Everything is happy, let's grab the memory and set things up. */ if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name)) { - dev_err(&sis630_dev->dev, - "I/O Region 0x%04x-0x%04x for SMBus already in use.\n", - smbus_base + SMB_STS, - smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); - retval = -EBUSY; + retval = dev_err_probe(&sis630_dev->dev, -EBUSY, + "I/O Region 0x%04x-0x%04x for SMBus already in use.\n", + smbus_base + SMB_STS, + smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); goto exit; } @@ -511,12 +509,9 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) { int ret; - if (sis630_setup(dev)) { - dev_err(&dev->dev, - "SIS630 compatible bus not detected, " - "module not inserted.\n"); - return -ENODEV; - } + if (sis630_setup(dev)) + return dev_err_probe(&dev->dev, -ENODEV, + "Compatible bus not detected, module not inserted.\n"); /* set up the sysfs linkage to our parent device */ sis630_adapter.dev.parent = &dev->dev; diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index 77529dda6fcd..eee41dc9d706 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -245,23 +245,19 @@ static int sis96x_probe(struct pci_dev *dev, u16 ww = 0; int retval; - if (sis96x_smbus_base) { - dev_err(&dev->dev, "Only one device supported.\n"); - return -EBUSY; - } + if (sis96x_smbus_base) + return dev_err_probe(&dev->dev, -EBUSY, "Only one device supported.\n"); pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); - if (PCI_CLASS_SERIAL_SMBUS != ww) { - dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww); - return -ENODEV; - } + if (ww != PCI_CLASS_SERIAL_SMBUS) + return dev_err_probe(&dev->dev, -ENODEV, + "Unsupported device class 0x%04x!\n", ww); sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR); - if (!sis96x_smbus_base) { - dev_err(&dev->dev, "SiS96x SMBus base address " - "not initialized!\n"); - return -EINVAL; - } + if (!sis96x_smbus_base) + return dev_err_probe(&dev->dev, -EINVAL, + "SiS96x SMBus base address not initialized!\n"); + dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n", sis96x_smbus_base); @@ -272,9 +268,9 @@ static int sis96x_probe(struct pci_dev *dev, /* Everything is happy, let's grab the memory and set things up. */ if (!request_region(sis96x_smbus_base, SMB_IOSIZE, sis96x_driver.name)) { - dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x " - "already in use!\n", sis96x_smbus_base, - sis96x_smbus_base + SMB_IOSIZE - 1); + dev_err_probe(&dev->dev, -EINVAL, + "SMBus registers 0x%04x-0x%04x already in use!\n", + sis96x_smbus_base, sis96x_smbus_base + SMB_IOSIZE - 1); sis96x_smbus_base = 0; return -EINVAL; @@ -287,7 +283,7 @@ static int sis96x_probe(struct pci_dev *dev, "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base); if ((retval = i2c_add_adapter(&sis96x_adapter))) { - dev_err(&dev->dev, "Couldn't register adapter!\n"); + dev_err_probe(&dev->dev, retval, "Couldn't register adapter!\n"); release_region(sis96x_smbus_base, SMB_IOSIZE); sis96x_smbus_base = 0; } diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index 1b490525d8dd..7b321a956fca 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -469,11 +469,10 @@ static int sprd_i2c_clk_init(struct sprd_i2c *i2c_dev) i2c_dev->adap.nr, i2c_dev->src_clk); i2c_dev->clk = devm_clk_get(i2c_dev->dev, "enable"); - if (IS_ERR(i2c_dev->clk)) { - dev_err(i2c_dev->dev, "i2c%d can't get the enable clock\n", - i2c_dev->adap.nr); - return PTR_ERR(i2c_dev->clk); - } + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(i2c_dev->dev, PTR_ERR(i2c_dev->clk), + "i2c%d can't get the enable clock\n", + i2c_dev->adap.nr); return 0; } @@ -548,13 +547,13 @@ static int sprd_i2c_probe(struct platform_device *pdev) IRQF_NO_SUSPEND | IRQF_ONESHOT, pdev->name, i2c_dev); if (ret) { - dev_err(&pdev->dev, "failed to request irq %d\n", i2c_dev->irq); + dev_err_probe(&pdev->dev, ret, "failed to request irq %d\n", i2c_dev->irq); goto err_rpm_put; } ret = i2c_add_numbered_adapter(&i2c_dev->adap); if (ret) { - dev_err(&pdev->dev, "add adapter failed\n"); + dev_err_probe(&pdev->dev, ret, "add adapter failed\n"); goto err_rpm_put; } diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 751ea421caaf..3f89c2145741 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -775,17 +775,15 @@ static int st_i2c_of_get_deglitch(struct device_node *np, ret = of_property_read_u32(np, "st,i2c-min-scl-pulse-width-us", &i2c_dev->scl_min_width_us); - if ((ret == -ENODATA) || (ret == -EOVERFLOW)) { - dev_err(i2c_dev->dev, "st,i2c-min-scl-pulse-width-us invalid\n"); - return ret; - } + if ((ret == -ENODATA) || (ret == -EOVERFLOW)) + return dev_err_probe(i2c_dev->dev, ret, + "st,i2c-min-scl-pulse-width-us invalid\n"); ret = of_property_read_u32(np, "st,i2c-min-sda-pulse-width-us", &i2c_dev->sda_min_width_us); - if ((ret == -ENODATA) || (ret == -EOVERFLOW)) { - dev_err(i2c_dev->dev, "st,i2c-min-sda-pulse-width-us invalid\n"); - return ret; - } + if ((ret == -ENODATA) || (ret == -EOVERFLOW)) + return dev_err_probe(i2c_dev->dev, ret, + "st,i2c-min-sda-pulse-width-us invalid\n"); return 0; } @@ -808,16 +806,13 @@ static int st_i2c_probe(struct platform_device *pdev) return PTR_ERR(i2c_dev->base); i2c_dev->irq = irq_of_parse_and_map(np, 0); - if (!i2c_dev->irq) { - dev_err(&pdev->dev, "IRQ missing or invalid\n"); - return -EINVAL; - } + if (!i2c_dev->irq) + return dev_err_probe(&pdev->dev, -EINVAL, "IRQ missing or invalid\n"); i2c_dev->clk = of_clk_get_by_name(np, "ssc"); - if (IS_ERR(i2c_dev->clk)) { - dev_err(&pdev->dev, "Unable to request clock\n"); - return PTR_ERR(i2c_dev->clk); - } + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk), + "Unable to request clock\n"); i2c_dev->mode = I2C_MODE_STANDARD; ret = of_property_read_u32(np, "clock-frequency", &clk_rate); @@ -829,10 +824,9 @@ static int st_i2c_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, i2c_dev->irq, NULL, st_i2c_isr_thread, IRQF_ONESHOT, pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request irq %i\n", i2c_dev->irq); pinctrl_pm_select_default_state(i2c_dev->dev); /* In case idle state available, select it */ diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c index becf8977979f..064e47d6c96f 100644 --- a/drivers/i2c/busses/i2c-stm32.c +++ b/drivers/i2c/busses/i2c-stm32.c @@ -39,7 +39,7 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, dma_sconfig.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); if (ret < 0) { - dev_err(dev, "can't configure tx channel\n"); + dev_err_probe(dev, ret, "can't configure tx channel\n"); goto fail_tx; } @@ -60,7 +60,7 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, dma_sconfig.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); if (ret < 0) { - dev_err(dev, "can't configure rx channel\n"); + dev_err_probe(dev, ret, "can't configure rx channel\n"); goto fail_rx; } diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c index b3d56d0aa9d0..44e8b04962bb 100644 --- a/drivers/i2c/busses/i2c-stm32f4.c +++ b/drivers/i2c/busses/i2c-stm32f4.c @@ -163,11 +163,9 @@ static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev) * to hardware limitation */ if (freq < STM32F4_I2C_MIN_STANDARD_FREQ || - freq > STM32F4_I2C_MAX_FREQ) { - dev_err(i2c_dev->dev, - "bad parent clk freq for standard mode\n"); - return -EINVAL; - } + freq > STM32F4_I2C_MAX_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "bad parent clk freq for standard mode\n"); } else { /* * To be as close as possible to 400 kHz, the parent clk @@ -175,11 +173,9 @@ static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev) * maximum value of 46 MHz due to hardware limitation */ if (freq < STM32F4_I2C_MIN_FAST_FREQ || - freq > STM32F4_I2C_MAX_FREQ) { - dev_err(i2c_dev->dev, - "bad parent clk freq for fast mode\n"); - return -EINVAL; - } + freq > STM32F4_I2C_MAX_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "bad parent clk freq for fast mode\n"); } cr2 |= STM32F4_I2C_CR2_FREQ(freq); @@ -772,22 +768,19 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) return PTR_ERR(i2c_dev->base); irq_event = irq_of_parse_and_map(np, 0); - if (!irq_event) { - dev_err(&pdev->dev, "IRQ event missing or invalid\n"); - return -EINVAL; - } + if (!irq_event) + return dev_err_probe(&pdev->dev, -EINVAL, + "IRQ event missing or invalid\n"); irq_error = irq_of_parse_and_map(np, 1); - if (!irq_error) { - dev_err(&pdev->dev, "IRQ error missing or invalid\n"); - return -EINVAL; - } + if (!irq_error) + return dev_err_probe(&pdev->dev, -EINVAL, + "IRQ error missing or invalid\n"); i2c_dev->clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(i2c_dev->clk)) { - dev_err(&pdev->dev, "Failed to enable clock\n"); - return PTR_ERR(i2c_dev->clk); - } + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk), + "Failed to enable clock\n"); rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(rst)) @@ -807,19 +800,15 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq_event, stm32f4_i2c_isr_event, 0, pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq event %i\n", - irq_event); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request irq event %i\n", irq_event); ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0, pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq error %i\n", - irq_error); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request irq error %i\n", irq_error); ret = stm32f4_i2c_hw_config(i2c_dev); if (ret) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 53d9df70ebe4..16c6e61c7e11 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -481,28 +481,22 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, int ret = 0; specs = stm32f7_get_specs(setup->speed_freq); - if (specs == ERR_PTR(-EINVAL)) { - dev_err(i2c_dev->dev, "speed out of bound {%d}\n", - setup->speed_freq); - return -EINVAL; - } + if (specs == ERR_PTR(-EINVAL)) + return dev_err_probe(i2c_dev->dev, -EINVAL, "speed out of bound {%d}\n", + setup->speed_freq); if ((setup->rise_time > specs->rise_max) || - (setup->fall_time > specs->fall_max)) { - dev_err(i2c_dev->dev, - "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", - setup->rise_time, specs->rise_max, - setup->fall_time, specs->fall_max); - return -EINVAL; - } + (setup->fall_time > specs->fall_max)) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", + setup->rise_time, specs->rise_max, + setup->fall_time, specs->fall_max); i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk); - if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) { - dev_err(i2c_dev->dev, - "DNF out of bound %d/%d\n", - i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk); - return -EINVAL; - } + if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "DNF out of bound %d/%d\n", i2c_dev->dnf * i2cclk, + STM32F7_I2C_DNF_MAX * i2cclk); /* Analog and Digital Filters */ af_delay_min = @@ -567,8 +561,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, } if (list_empty(&solutions)) { - dev_err(i2c_dev->dev, "no Prescaler solution\n"); - ret = -EPERM; + ret = dev_err_probe(i2c_dev->dev, -EPERM, "no Prescaler solution\n"); goto exit; } @@ -624,8 +617,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, } if (!s) { - dev_err(i2c_dev->dev, "no solution at all\n"); - ret = -EPERM; + ret = dev_err_probe(i2c_dev->dev, -EPERM, "no solution at all\n"); goto exit; } @@ -674,11 +666,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, i2c_parse_fw_timings(i2c_dev->dev, t, false); - if (t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) { - dev_err(i2c_dev->dev, "Invalid bus speed (%i>%i)\n", - t->bus_freq_hz, I2C_MAX_FAST_MODE_PLUS_FREQ); - return -EINVAL; - } + if (t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, "Invalid bus speed (%i>%i)\n", + t->bus_freq_hz, I2C_MAX_FAST_MODE_PLUS_FREQ); setup->speed_freq = t->bus_freq_hz; i2c_dev->setup.rise_time = t->scl_rise_ns; @@ -686,20 +676,21 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, i2c_dev->dnf_dt = t->digital_filter_width_ns; setup->clock_src = clk_get_rate(i2c_dev->clk); - if (!setup->clock_src) { - dev_err(i2c_dev->dev, "clock rate is 0\n"); - return -EINVAL; - } + if (!setup->clock_src) + return dev_err_probe(i2c_dev->dev, -EINVAL, "clock rate is 0\n"); if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter")) i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT; + i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node, + "i2c-analog-filter"); + do { ret = stm32f7_i2c_compute_timing(i2c_dev, setup, &i2c_dev->timing); if (ret) { - dev_err(i2c_dev->dev, - "failed to compute I2C timings.\n"); + dev_err_probe(i2c_dev->dev, ret, + "failed to compute I2C timings.\n"); if (setup->speed_freq <= I2C_MAX_STANDARD_MODE_FREQ) break; setup->speed_freq = @@ -710,13 +701,8 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, } } while (ret); - if (ret) { - dev_err(i2c_dev->dev, "Impossible to compute I2C timings.\n"); - return ret; - } - - i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node, - "i2c-analog-filter"); + if (ret) + return dev_err_probe(i2c_dev->dev, ret, "Impossible to compute I2C timings.\n"); dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n", setup->speed_freq, setup->clock_src); @@ -2175,10 +2161,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) return -ENOMEM; setup = of_device_get_match_data(&pdev->dev); - if (!setup) { - dev_err(&pdev->dev, "Can't get device data\n"); - return -ENODEV; - } + if (!setup) + return dev_err_probe(&pdev->dev, -ENODEV, "Can't get device data\n"); i2c_dev->setup = *setup; i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -2279,7 +2263,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) ret = dev_pm_set_wake_irq(i2c_dev->dev, irq_event); if (ret) { - dev_err(i2c_dev->dev, "Failed to set wake up irq\n"); + dev_err_probe(i2c_dev->dev, ret, "Failed to set wake up irq\n"); goto clr_wakeup_capable; } } @@ -2305,9 +2289,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (i2c_dev->smbus_mode) { ret = stm32f7_i2c_enable_smbus_host(i2c_dev); if (ret) { - dev_err(i2c_dev->dev, - "failed to enable SMBus Host-Notify protocol (%d)\n", - ret); + dev_err_probe(i2c_dev->dev, ret, + "failed to enable SMBus Host-Notify protocol\n"); goto i2c_adapter_remove; } } @@ -2315,9 +2298,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (of_property_read_bool(pdev->dev.of_node, "smbus-alert")) { ret = stm32f7_i2c_enable_smbus_alert(i2c_dev); if (ret) { - dev_err(i2c_dev->dev, - "failed to enable SMBus alert protocol (%d)\n", - ret); + dev_err_probe(i2c_dev->dev, ret, + "failed to enable SMBus alert protocol\n"); goto i2c_disable_smbus_host; } } diff --git a/drivers/i2c/busses/i2c-sun6i-p2wi.c b/drivers/i2c/busses/i2c-sun6i-p2wi.c index fb5280b8cf7f..dffbe776a195 100644 --- a/drivers/i2c/busses/i2c-sun6i-p2wi.c +++ b/drivers/i2c/busses/i2c-sun6i-p2wi.c @@ -194,22 +194,16 @@ static int p2wi_probe(struct platform_device *pdev) int ret; of_property_read_u32(np, "clock-frequency", &clk_freq); - if (clk_freq > P2WI_MAX_FREQ) { - dev_err(dev, - "required clock-frequency (%u Hz) is too high (max = 6MHz)", - clk_freq); - return -EINVAL; - } + if (clk_freq > P2WI_MAX_FREQ) + return dev_err_probe(dev, -EINVAL, + "required clock-frequency (%u Hz) is too high (max = 6MHz)", + clk_freq); - if (clk_freq == 0) { - dev_err(dev, "clock-frequency is set to 0 in DT\n"); - return -EINVAL; - } + if (clk_freq == 0) + return dev_err_probe(dev, -EINVAL, "clock-frequency is set to 0 in DT\n"); - if (of_get_child_count(np) > 1) { - dev_err(dev, "P2WI only supports one target device\n"); - return -EINVAL; - } + if (of_get_child_count(np) > 1) + return dev_err_probe(dev, -EINVAL, "P2WI only supports one target device\n"); p2wi = devm_kzalloc(dev, sizeof(struct p2wi), GFP_KERNEL); if (!p2wi) @@ -226,11 +220,9 @@ static int p2wi_probe(struct platform_device *pdev) childnp = of_get_next_available_child(np, NULL); if (childnp) { ret = of_property_read_u32(childnp, "reg", &target_addr); - if (ret) { - dev_err(dev, "invalid target address on node %pOF\n", - childnp); - return -EINVAL; - } + if (ret) + return dev_err_probe(dev, -EINVAL, + "invalid target address on node %pOF\n", childnp); p2wi->target_addr = target_addr; } @@ -245,26 +237,20 @@ static int p2wi_probe(struct platform_device *pdev) return irq; p2wi->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(p2wi->clk)) { - ret = PTR_ERR(p2wi->clk); - dev_err(dev, "failed to enable clk: %d\n", ret); - return ret; - } + if (IS_ERR(p2wi->clk)) + return dev_err_probe(dev, PTR_ERR(p2wi->clk), + "failed to enable clk\n"); parent_clk_freq = clk_get_rate(p2wi->clk); p2wi->rstc = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(p2wi->rstc)) { - dev_err(dev, "failed to retrieve reset controller: %pe\n", - p2wi->rstc); - return PTR_ERR(p2wi->rstc); - } + if (IS_ERR(p2wi->rstc)) + return dev_err_probe(dev, PTR_ERR(p2wi->rstc), + "failed to retrieve reset controller\n"); ret = reset_control_deassert(p2wi->rstc); - if (ret) { - dev_err(dev, "failed to deassert reset line: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to deassert reset line\n"); init_completion(&p2wi->complete); p2wi->adapter.dev.parent = dev; @@ -276,8 +262,7 @@ static int p2wi_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, p2wi_interrupt, 0, pdev->name, p2wi); if (ret) { - dev_err(dev, "can't register interrupt handler irq%d: %d\n", - irq, ret); + dev_err_probe(dev, ret, "can't register interrupt handler irq%d\n", irq); goto err_reset_assert; } diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 479a1667e88d..9c281b4905eb 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -164,6 +164,7 @@ struct tegra_i2c_regs { unsigned int master_reset_cntrl; unsigned int mst_fifo_control; unsigned int mst_fifo_status; + unsigned int fairness_arb; unsigned int sw_mutex; }; @@ -272,6 +273,7 @@ static const struct tegra_i2c_regs tegra264_i2c_regs = { .master_reset_cntrl = 0x0a8, .mst_fifo_control = 0x0b4, .mst_fifo_status = 0x0b8, + .fairness_arb = 0x0e8, .sw_mutex = 0x0ec, }; @@ -300,6 +302,7 @@ static const struct tegra_i2c_regs tegra410_i2c_regs = { .master_reset_cntrl = 0x0ac, .mst_fifo_control = 0x0b8, .mst_fifo_status = 0x0bc, + .fairness_arb = 0x0ec, .sw_mutex = 0x0f0, }; @@ -379,6 +382,7 @@ enum tegra_i2c_variant { * timing settings. * @enable_hs_mode_support: Enable support for high speed (HS) mode transfers. * @has_mutex: Has mutex register for mutual exclusion with other firmwares or VMs. + * @has_fairarb_reg: Has fairness arbitration register for SMBUS/MCTP support. * @variant: This represents the I2C controller variant. * @regs: Register offsets for the specific SoC variant. */ @@ -412,6 +416,7 @@ struct tegra_i2c_hw_feature { bool has_interface_timing_reg; bool enable_hs_mode_support; bool has_mutex; + bool has_fairarb_reg; enum tegra_i2c_variant variant; const struct tegra_i2c_regs *regs; }; @@ -436,6 +441,7 @@ struct tegra_i2c_hw_feature { * @msg_read: indicates that the transfer is a read access * @timings: i2c timings information like bus frequency * @multimaster_mode: indicates that I2C controller is in multi-master mode + * @is_mctp: indicates that the I2C controller is used as an MCTP controller * @dma_chan: DMA channel * @dma_phys: handle to DMA resources * @dma_buf: pointer to allocated DMA buffer @@ -476,6 +482,7 @@ struct tegra_i2c_dev { void *dma_buf; bool multimaster_mode; + bool is_mctp; bool atomic_mode; bool dma_mode; bool msg_read; @@ -709,15 +716,15 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) goto err_out; } - i2c_dev->dma_dev = i2c_dev->dma_chan->device->dev; + i2c_dev->dma_dev = dmaengine_get_dma_device(i2c_dev->dma_chan); i2c_dev->dma_buf_size = i2c_dev->hw->quirks->max_write_len + I2C_PACKET_HEADER_SIZE; dma_buf = dma_alloc_coherent(i2c_dev->dma_dev, i2c_dev->dma_buf_size, &dma_phys, GFP_KERNEL | __GFP_NOWARN); if (!dma_buf) { - dev_err(i2c_dev->dev, "failed to allocate DMA buffer\n"); - err = -ENOMEM; + err = dev_err_probe(i2c_dev->dev, -ENOMEM, + "failed to allocate DMA buffer\n"); goto err_out; } @@ -729,8 +736,7 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) err_out: tegra_i2c_release_dma(i2c_dev); if (err != -EPROBE_DEFER) { - dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err); - dev_err(i2c_dev->dev, "falling back to PIO\n"); + dev_err(i2c_dev->dev, "cannot use DMA, falling back to PIO\n"); return 0; } @@ -911,6 +917,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) if (IS_VI(i2c_dev)) tegra_i2c_vi_init(i2c_dev); + /* Disable fairness arbitration if not an MCTP controller */ + if (i2c_dev->hw->has_fairarb_reg && !i2c_dev->is_mctp) + i2c_writel(i2c_dev, 0, i2c_dev->hw->regs->fairness_arb); + if (i2c_dev->hw->enable_hs_mode_support) max_bus_freq_hz = I2C_MAX_HIGH_SPEED_MODE_FREQ; else @@ -1778,6 +1788,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1811,6 +1822,7 @@ static const struct tegra_i2c_hw_feature tegra20_dvc_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DVC, .regs = &tegra20_dvc_i2c_regs, }; @@ -1844,6 +1856,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1876,6 +1889,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1908,6 +1922,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1940,6 +1955,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1973,6 +1989,7 @@ static const struct tegra_i2c_hw_feature tegra210_vi_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_VI, .regs = &tegra210_vi_i2c_regs, }; @@ -2006,6 +2023,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -2040,6 +2058,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -2074,6 +2093,7 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .has_fairarb_reg = true, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra264_i2c_regs, }; @@ -2108,6 +2128,7 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .has_fairarb_reg = true, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra264_i2c_regs, }; @@ -2115,9 +2136,9 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = { static const struct tegra_i2c_hw_feature tegra410_i2c_hw = { .has_continue_xfer_support = true, .has_per_pkt_xfer_complete_irq = true, - .clk_divisor_hs_mode = 1, + .clk_divisor_hs_mode = 2, .clk_divisor_std_mode = 0x3f, - .clk_divisor_fast_mode = 0x2c, + .clk_divisor_fast_mode = 0x2f, .clk_divisor_fast_plus_mode = 0x11, .has_config_load_reg = true, .has_multi_master_mode = true, @@ -2133,8 +2154,8 @@ static const struct tegra_i2c_hw_feature tegra410_i2c_hw = { .thigh_fast_mode = 0x2, .tlow_fastplus_mode = 0x2, .thigh_fastplus_mode = 0x2, - .tlow_hs_mode = 0x8, - .thigh_hs_mode = 0x6, + .tlow_hs_mode = 0x5, + .thigh_hs_mode = 0x2, .setup_hold_time_std_mode = 0x08080808, .setup_hold_time_fast_mode = 0x02020202, .setup_hold_time_fastplus_mode = 0x02020202, @@ -2142,6 +2163,7 @@ static const struct tegra_i2c_hw_feature tegra410_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .has_fairarb_reg = true, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra410_i2c_regs, }; @@ -2174,6 +2196,7 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev) multi_mode = device_property_read_bool(i2c_dev->dev, "multi-master"); i2c_dev->multimaster_mode = multi_mode; + i2c_dev->is_mctp = device_property_present(i2c_dev->dev, "mctp-controller"); } static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev) @@ -2207,7 +2230,7 @@ static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev) err = clk_enable(i2c_dev->div_clk); if (err) { - dev_err(i2c_dev->dev, "failed to enable div-clk: %d\n", err); + dev_err_probe(i2c_dev->dev, err, "failed to enable div-clk\n"); goto unprepare_clocks; } @@ -2233,7 +2256,7 @@ static int tegra_i2c_init_hardware(struct tegra_i2c_dev *i2c_dev) ret = pm_runtime_get_sync(i2c_dev->dev); if (ret < 0) - dev_err(i2c_dev->dev, "runtime resume failed: %d\n", ret); + dev_err_probe(i2c_dev->dev, ret, "runtime resume failed\n"); else ret = tegra_i2c_init(i2c_dev); @@ -2402,28 +2425,37 @@ static int __maybe_unused tegra_i2c_runtime_suspend(struct device *dev) static int __maybe_unused tegra_i2c_suspend(struct device *dev) { + /* + * Bring the controller up and hold a usage count so it stays + * available until the noirq phase. + */ + return pm_runtime_resume_and_get(dev); +} + +static int __maybe_unused tegra_i2c_suspend_noirq(struct device *dev) +{ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); - int err; i2c_mark_adapter_suspended(&i2c_dev->adapter); - if (!pm_runtime_status_suspended(dev)) { - err = tegra_i2c_runtime_suspend(dev); - if (err) - return err; - } - - return 0; + /* + * Runtime PM is already disabled at this point, so invoke the + * runtime_suspend callback directly to put the controller down. + */ + return tegra_i2c_runtime_suspend(dev); } -static int __maybe_unused tegra_i2c_resume(struct device *dev) +static int __maybe_unused tegra_i2c_resume_noirq(struct device *dev) { struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); int err; /* - * We need to ensure that clocks are enabled so that registers can be - * restored in tegra_i2c_init(). + * Runtime PM is still disabled at this point, so invoke the + * runtime_resume callback directly to bring the controller back up + * before re-initializing the hardware. The adapter is then marked + * resumed so that consumers can issue transfers from their own + * resume_noirq() handlers and onwards. */ err = tegra_i2c_runtime_resume(dev); if (err) @@ -2433,24 +2465,22 @@ static int __maybe_unused tegra_i2c_resume(struct device *dev) if (err) return err; - /* - * In case we are runtime suspended, disable clocks again so that we - * don't unbalance the clock reference counts during the next runtime - * resume transition. - */ - if (pm_runtime_status_suspended(dev)) { - err = tegra_i2c_runtime_suspend(dev); - if (err) - return err; - } - i2c_mark_adapter_resumed(&i2c_dev->adapter); return 0; } +static int __maybe_unused tegra_i2c_resume(struct device *dev) +{ + pm_runtime_put(dev); + + return 0; +} + static const struct dev_pm_ops tegra_i2c_pm = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume) + SET_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend_noirq, + tegra_i2c_resume_noirq) SET_RUNTIME_PM_OPS(tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume, NULL) }; diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c index 88d66593d9fc..73355a56aabf 100644 --- a/drivers/i2c/busses/i2c-tiny-usb.c +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -254,9 +254,8 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface, dev->usb_dev->bus->busnum, dev->usb_dev->devnum); if (usb_write(&dev->adapter, CMD_SET_DELAY, delay, 0, NULL, 0) != 0) { - dev_err(&dev->adapter.dev, - "failure setting delay to %dus\n", delay); - retval = -EIO; + retval = dev_err_probe(&dev->adapter.dev, -EIO, + "failure setting delay to %dus\n", delay); goto error; } diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c index 7b0b0bff8000..5da6fef92bec 100644 --- a/drivers/i2c/busses/i2c-virtio.c +++ b/drivers/i2c/busses/i2c-virtio.c @@ -222,6 +222,8 @@ static int virtio_i2c_probe(struct virtio_device *vdev) */ ACPI_COMPANION_SET(&vi->adap.dev, ACPI_COMPANION(vdev->dev.parent)); + virtio_device_ready(vdev); + ret = i2c_add_adapter(&vi->adap); if (ret) virtio_i2c_del_vqs(vdev); diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 28c0e4884a7f..e5fddacae9a4 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -84,8 +84,11 @@ static int i2c_acpi_resource_count(struct acpi_resource *ares, void *data) * i2c_acpi_client_count - Count the number of I2cSerialBus resources * @adev: ACPI device * - * Returns the number of I2cSerialBus resources in the ACPI-device's + * Return: + * The number of I2cSerialBus resources in the ACPI-device's * resource-list; or a negative error code. + * + * Specifically returns -ENOENT when no resources found. */ int i2c_acpi_client_count(struct acpi_device *adev) { @@ -97,7 +100,7 @@ int i2c_acpi_client_count(struct acpi_device *adev) return ret; acpi_dev_free_resource_list(&r); - return count; + return count ?: -ENOENT; } EXPORT_SYMBOL_GPL(i2c_acpi_client_count); diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index a2132d70fb36..3ec04787a737 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -63,6 +63,7 @@ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); +static void i2c_deregister_clients(struct i2c_adapter *adap); static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); @@ -1106,8 +1107,8 @@ EXPORT_SYMBOL(i2c_find_device_by_fwnode); static const struct i2c_device_id dummy_id[] = { - { "dummy", }, - { "smbus_host_notify", }, + { .name = "dummy" }, + { .name = "smbus_host_notify" }, { } }; @@ -1515,23 +1516,48 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) } EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); +static int i2c_allocate_adapter_id(struct i2c_adapter *adap) +{ + int id, start, end; + + if (adap->nr == -1) { + start = __i2c_first_dynamic_bus_num; + end = 0; + } else { + start = adap->nr; + end = adap->nr + 1; + } + + mutex_lock(&core_lock); + id = idr_alloc(&i2c_adapter_idr, NULL, start, end, GFP_KERNEL); + mutex_unlock(&core_lock); + if (id < 0) { + if (adap->nr != -1 && id == -ENOSPC) + id = -EBUSY; + pr_err("adapter '%s': failed to allocate id: %d\n", adap->name, id); + return id; + } + + adap->nr = id; + + return 0; +} + static int i2c_register_adapter(struct i2c_adapter *adap) { - int res = -EINVAL; + int res; /* Can't register until after driver model init */ - if (WARN_ON(!is_registered)) { - res = -EAGAIN; - goto out_list; - } + if (WARN_ON(!is_registered)) + return -EAGAIN; /* Sanity checks */ if (WARN(!adap->name[0], "i2c adapter has no name")) - goto out_list; + return -EINVAL; if (!adap->algo) { pr_err("adapter '%s': no algo supplied!\n", adap->name); - goto out_list; + return -EINVAL; } if (!adap->lock_ops) @@ -1552,14 +1578,25 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (res) { pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n", adap->name, res); - goto out_list; + return res; } - dev_set_name(&adap->dev, "i2c-%d", adap->nr); + res = i2c_allocate_adapter_id(adap); + if (res) + goto err_remove_irq_domain; + + res = dev_set_name(&adap->dev, "i2c-%d", adap->nr); + if (res) + goto err_free_id; + adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; device_initialize(&adap->dev); + res = i2c_init_recovery(adap); + if (res == -EPROBE_DEFER) + goto err_put_adap; + /* * This adapter can be used as a parent immediately after device_add(), * setup runtime-pm (especially ignore-children) before hand. @@ -1569,22 +1606,21 @@ static int i2c_register_adapter(struct i2c_adapter *adap) pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); + adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); + + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, adap, adap->nr); + mutex_unlock(&core_lock); + res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - put_device(&adap->dev); - goto out_list; + goto err_replace_id; } - adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); - res = i2c_setup_smbus_alert(adap); if (res) - goto out_reg; - - res = i2c_init_recovery(adap); - if (res == -EPROBE_DEFER) - goto out_reg; + goto err_deregister_clients; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); @@ -1603,36 +1639,27 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return 0; -out_reg: +err_deregister_clients: + i2c_deregister_clients(adap); + device_del(&adap->dev); +err_replace_id: + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); debugfs_remove_recursive(adap->debugfs); + pm_runtime_disable(&adap->dev); +err_put_adap: init_completion(&adap->dev_released); - device_unregister(&adap->dev); + put_device(&adap->dev); wait_for_completion(&adap->dev_released); -out_list: +err_free_id: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); - return res; -} - -/** - * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1 - * @adap: the adapter to register (with adap->nr initialized) - * Context: can sleep - * - * See i2c_add_numbered_adapter() for details. - */ -static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) -{ - int id; - - mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); - mutex_unlock(&core_lock); - if (WARN(id < 0, "couldn't get idr")) - return id == -ENOSPC ? -EBUSY : id; +err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); - return i2c_register_adapter(adap); + return res; } /** @@ -1655,17 +1682,8 @@ int i2c_add_adapter(struct i2c_adapter *adapter) int id; id = of_alias_get_id(dev->of_node, "i2c"); - if (id >= 0) { - adapter->nr = id; - return __i2c_add_numbered_adapter(adapter); - } - - mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adapter, - __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); - mutex_unlock(&core_lock); - if (WARN(id < 0, "couldn't get idr")) - return id; + if (id < 0) + id = -1; adapter->nr = id; @@ -1701,7 +1719,7 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adap) if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); - return __i2c_add_numbered_adapter(adap); + return i2c_register_adapter(adap); } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); @@ -1743,29 +1761,10 @@ static int __process_removed_adapter(struct device_driver *d, void *data) return 0; } -/** - * i2c_del_adapter - unregister I2C adapter - * @adap: the adapter being unregistered - * Context: can sleep - * - * This unregisters an I2C adapter which was previously registered - * by @i2c_add_adapter or @i2c_add_numbered_adapter. - */ -void i2c_del_adapter(struct i2c_adapter *adap) +static void i2c_deregister_clients(struct i2c_adapter *adap) { - struct i2c_adapter *found; struct i2c_client *client, *next; - /* First make sure that this adapter was ever added */ - mutex_lock(&core_lock); - found = idr_find(&i2c_adapter_idr, adap->nr); - mutex_unlock(&core_lock); - if (found != adap) { - pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); - return; - } - - i2c_acpi_remove_space_handler(adap); /* Tell drivers about this removal */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, @@ -1791,6 +1790,34 @@ void i2c_del_adapter(struct i2c_adapter *adap) * them up properly, so we give them a chance to do that first. */ device_for_each_child(&adap->dev, NULL, __unregister_client); device_for_each_child(&adap->dev, NULL, __unregister_dummy); +} + +/** + * i2c_del_adapter - unregister I2C adapter + * @adap: the adapter being unregistered + * Context: can sleep + * + * This unregisters an I2C adapter which was previously registered + * by @i2c_add_adapter or @i2c_add_numbered_adapter. + */ +void i2c_del_adapter(struct i2c_adapter *adap) +{ + struct i2c_adapter *found; + + /* First make sure that this adapter was ever added */ + mutex_lock(&core_lock); + found = idr_find(&i2c_adapter_idr, adap->nr); + if (found == adap) + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); + if (found != adap) { + pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); + return; + } + + i2c_acpi_remove_space_handler(adap); + + i2c_deregister_clients(adap); /* device name is gone after device_unregister */ dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index 354a88d0599e..30b48a428c0b 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -176,11 +176,6 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); client = of_i2c_register_device(adap, rd->dn); if (IS_ERR(client)) { dev_err(&adap->dev, "failed to create client for '%pOF'\n", diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c index 6bc2ef650a74..226d722af662 100644 --- a/drivers/i2c/i2c-slave-eeprom.c +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -191,14 +191,14 @@ static void i2c_slave_eeprom_remove(struct i2c_client *client) } static const struct i2c_device_id i2c_slave_eeprom_id[] = { - { "slave-24c02", I2C_SLAVE_DEVICE_MAGIC(2048 / 8, 0) }, - { "slave-24c02ro", I2C_SLAVE_DEVICE_MAGIC(2048 / 8, I2C_SLAVE_FLAG_RO) }, - { "slave-24c32", I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16) }, - { "slave-24c32ro", I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, - { "slave-24c64", I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16) }, - { "slave-24c64ro", I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, - { "slave-24c512", I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16) }, - { "slave-24c512ro", I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c02", .driver_data = I2C_SLAVE_DEVICE_MAGIC(2048 / 8, 0) }, + { .name = "slave-24c02ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(2048 / 8, I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c32", .driver_data = I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c32ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c64", .driver_data = I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c64ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c512", .driver_data = I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c512ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, { } }; MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id); diff --git a/drivers/i2c/i2c-slave-testunit.c b/drivers/i2c/i2c-slave-testunit.c index 871c58461ebc..4a1bf1aa504b 100644 --- a/drivers/i2c/i2c-slave-testunit.c +++ b/drivers/i2c/i2c-slave-testunit.c @@ -270,7 +270,7 @@ static void i2c_slave_testunit_remove(struct i2c_client *client) } static const struct i2c_device_id i2c_slave_testunit_id[] = { - { "slave-testunit" }, + { .name = "slave-testunit" }, { } }; MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id); diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index bc7bd55e6370..069f08f68bc1 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -220,7 +220,7 @@ static void smbalert_remove(struct i2c_client *ara) } static const struct i2c_device_id smbalert_ids[] = { - { "smbus_alert" }, + { .name = "smbus_alert" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, smbalert_ids); diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c index 50fbc0d06e62..18228f5b80e8 100644 --- a/drivers/i2c/muxes/i2c-mux-ltc4306.c +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -191,8 +191,8 @@ static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan) } static const struct i2c_device_id ltc4306_id[] = { - { "ltc4305", ltc_4305 }, - { "ltc4306", ltc_4306 }, + { .name = "ltc4305", .driver_data = ltc_4305 }, + { .name = "ltc4306", .driver_data = ltc_4306 }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4306_id); diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 3d8002caf703..9a59129bc50f 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -74,8 +74,8 @@ struct pca9541 { }; static const struct i2c_device_id pca9541_id[] = { - { "pca9541" }, - {} + { .name = "pca9541" }, + { } }; MODULE_DEVICE_TABLE(i2c, pca9541_id); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index b9f370c9f018..8fca709ed279 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -250,24 +250,24 @@ static const struct chip_desc chips[] = { }; static const struct i2c_device_id pca954x_id[] = { - { "max7356", max_7356 }, - { "max7357", max_7357 }, - { "max7358", max_7358 }, - { "max7367", max_7367 }, - { "max7368", max_7368 }, - { "max7369", max_7369 }, - { "pca9540", pca_9540 }, - { "pca9542", pca_9542 }, - { "pca9543", pca_9543 }, - { "pca9544", pca_9544 }, - { "pca9545", pca_9545 }, - { "pca9546", pca_9546 }, - { "pca9547", pca_9547 }, - { "pca9548", pca_9548 }, - { "pca9846", pca_9846 }, - { "pca9847", pca_9847 }, - { "pca9848", pca_9848 }, - { "pca9849", pca_9849 }, + { .name = "max7356", .driver_data = max_7356 }, + { .name = "max7357", .driver_data = max_7357 }, + { .name = "max7358", .driver_data = max_7358 }, + { .name = "max7367", .driver_data = max_7367 }, + { .name = "max7368", .driver_data = max_7368 }, + { .name = "max7369", .driver_data = max_7369 }, + { .name = "pca9540", .driver_data = pca_9540 }, + { .name = "pca9542", .driver_data = pca_9542 }, + { .name = "pca9543", .driver_data = pca_9543 }, + { .name = "pca9544", .driver_data = pca_9544 }, + { .name = "pca9545", .driver_data = pca_9545 }, + { .name = "pca9546", .driver_data = pca_9546 }, + { .name = "pca9547", .driver_data = pca_9547 }, + { .name = "pca9548", .driver_data = pca_9548 }, + { .name = "pca9846", .driver_data = pca_9846 }, + { .name = "pca9847", .driver_data = pca_9847 }, + { .name = "pca9848", .driver_data = pca_9848 }, + { .name = "pca9849", .driver_data = pca_9849 }, { } }; MODULE_DEVICE_TABLE(i2c, pca954x_id); diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index 1e566ea92bc9..13da757100fe 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_address.h> #include <linux/platform_data/i2c-mux-reg.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -75,37 +74,34 @@ static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan) return 0; } -#ifdef CONFIG_OF -static int i2c_mux_reg_probe_dt(struct regmux *mux, - struct platform_device *pdev) +static int i2c_mux_reg_probe_fw(struct regmux *mux, struct device *dev) { - struct device_node *np = pdev->dev.of_node; - struct device_node *adapter_np, *child; + struct fwnode_handle *fwnode, *child; struct i2c_adapter *adapter; - struct resource res; unsigned *values; - int i = 0; + int ret, i = 0; - if (!np) + if (!dev_fwnode(dev)) return -ENODEV; - adapter_np = of_parse_phandle(np, "i2c-parent", 0); - if (!adapter_np) { - dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); + fwnode = fwnode_find_reference(dev_fwnode(dev), "i2c-parent", 0); + if (IS_ERR(fwnode)) { + dev_err(dev, "missing 'i2c-parent' property\n"); return -ENODEV; } - adapter = of_find_i2c_adapter_by_node(adapter_np); - of_node_put(adapter_np); + + adapter = i2c_find_adapter_by_fwnode(fwnode); + fwnode_handle_put(fwnode); if (!adapter) return -EPROBE_DEFER; mux->data.parent = i2c_adapter_id(adapter); put_device(&adapter->dev); - mux->data.n_values = of_get_child_count(np); - if (of_property_read_bool(np, "little-endian")) { + mux->data.n_values = device_get_child_node_count(dev); + if (device_property_read_bool(dev, "little-endian")) { mux->data.little_endian = true; - } else if (of_property_read_bool(np, "big-endian")) { + } else if (device_property_read_bool(dev, "big-endian")) { mux->data.little_endian = false; } else { #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \ @@ -118,40 +114,35 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, #error Endianness not defined? #endif } - mux->data.write_only = of_property_read_bool(np, "write-only"); + mux->data.write_only = device_property_read_bool(dev, "write-only"); - values = devm_kcalloc(&pdev->dev, - mux->data.n_values, sizeof(*mux->data.values), + values = devm_kcalloc(dev, mux->data.n_values, sizeof(*mux->data.values), GFP_KERNEL); if (!values) return -ENOMEM; - for_each_child_of_node(np, child) { - of_property_read_u32(child, "reg", values + i); + device_for_each_child_node(dev, child) { + if (is_acpi_device_node(child)) { + ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), + &values[i]); + if (ret) { + fwnode_handle_put(child); + return dev_err_probe(dev, ret, + "Cannot get address\n"); + } + } else { + fwnode_property_read_u32(child, "reg", &values[i]); + } + i++; } mux->data.values = values; - if (!of_property_read_u32(np, "idle-state", &mux->data.idle)) + if (!device_property_read_u32(dev, "idle-state", &mux->data.idle)) mux->data.idle_in_use = true; - /* map address from "reg" if exists */ - if (of_address_to_resource(np, 0, &res) == 0) { - mux->data.reg_size = resource_size(&res); - mux->data.reg = devm_ioremap_resource(&pdev->dev, &res); - if (IS_ERR(mux->data.reg)) - return PTR_ERR(mux->data.reg); - } - return 0; } -#else -static int i2c_mux_reg_probe_dt(struct regmux *mux, - struct platform_device *pdev) -{ - return 0; -} -#endif static int i2c_mux_reg_probe(struct platform_device *pdev) { @@ -169,34 +160,29 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) memcpy(&mux->data, dev_get_platdata(&pdev->dev), sizeof(mux->data)); } else { - ret = i2c_mux_reg_probe_dt(mux, pdev); + ret = i2c_mux_reg_probe_fw(mux, &pdev->dev); if (ret < 0) return dev_err_probe(&pdev->dev, ret, - "Error parsing device tree"); + "Error parsing firmware description\n"); } - parent = i2c_get_adapter(mux->data.parent); - if (!parent) - return -EPROBE_DEFER; - if (!mux->data.reg) { - dev_info(&pdev->dev, - "Register not set, using platform resource\n"); mux->data.reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(mux->data.reg)) { - ret = PTR_ERR(mux->data.reg); - goto err_put_parent; - } + if (IS_ERR(mux->data.reg)) + return PTR_ERR(mux->data.reg); mux->data.reg_size = resource_size(res); } if (mux->data.reg_size != 4 && mux->data.reg_size != 2 && mux->data.reg_size != 1) { dev_err(&pdev->dev, "Invalid register size\n"); - ret = -EINVAL; - goto err_put_parent; + return -EINVAL; } + parent = i2c_get_adapter(mux->data.parent); + if (!parent) + return -EPROBE_DEFER; + muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0, i2c_mux_reg_select, NULL); if (!muxc) { diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index f49354e37777..d74b478db280 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -81,6 +81,11 @@ static bool ibrs_off __read_mostly; /* Maximum allowed C-state target residency */ #define MAX_CMDLINE_RESIDENCY_US (100 * USEC_PER_MSEC) +/* The Package C-State Limit bits in MSR_PKG_CST_CONFIG_CONTROL */ +#define SKX_PKG_CST_LIMIT_MASK GENMASK(2, 0) +/* PC6 is enabled when Package C-State Limit >= this value */ +#define SKX_PKG_CST_LIMIT_PC6 2 + static char cmdline_table_str[MAX_CMDLINE_TABLE_LEN] __read_mostly; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; @@ -2074,12 +2079,13 @@ static void __init sklh_idle_state_table_update(void) } /** - * skx_idle_state_table_update - Adjust the Sky Lake/Cascade Lake - * idle states table. + * skx_is_pc6_disabled() - Check if PC6 is disabled in BIOS. + * + * Return: %true if PC6 is disabled, %false otherwise. */ -static void __init skx_idle_state_table_update(void) +static bool __init skx_is_pc6_disabled(void) { - unsigned long long msr; + u64 msr; rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr); @@ -2090,38 +2096,84 @@ static void __init skx_idle_state_table_update(void) * 011b: C6 (retention) * 111b: No Package C state limits. */ - if ((msr & 0x7) < 2) { - /* - * Uses the CC6 + PC0 latency and 3 times of - * latency for target_residency if the PC6 - * is disabled in BIOS. This is consistent - * with how intel_idle driver uses _CST - * to set the target_residency. - */ + return (msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6; +} + +/** + * skx_idle_state_table_update - Adjust the SKX/CLX idle states table. + * + * Adjust Sky Lake or Cascade Lake Xeon idle states if PC6 is disabled in BIOS. + * Use the CC6 + PC0 latency and 3 times of that latency for target_residency. + * This is consistent with how the intel_idle driver uses _CST to set the + * target_residency. + */ +static void __init skx_idle_state_table_update(void) +{ + if (skx_is_pc6_disabled()) { skx_cstates[2].exit_latency = 92; skx_cstates[2].target_residency = 276; } } /** - * spr_idle_state_table_update - Adjust Sapphire Rapids idle states table. + * spr_idle_state_table_update - Adjust Sapphire Rapids Xeon idle states table. + * + * By default, the C6 state assumes the worst-case scenario of package C6. + * However, if PC6 is disabled in BIOS, update the numbers to match core C6. */ static void __init spr_idle_state_table_update(void) { - unsigned long long msr; + if (skx_is_pc6_disabled()) { + spr_cstates[2].exit_latency = 190; + spr_cstates[2].target_residency = 600; + } +} + +/** + * drop_pc6_redundant_cstates() - Drop C-states redundant when PC6 is disabled. + * @states: Idle states table to modify. + * + * When PC6 is disabled in BIOS, C-states that exist solely to enable PC6 + * entry (such as C6P or C6SP) become identical to shallower C-states like + * C6, and are therefore redundant. Should be called only on systems with + * multiple C6 flavors. + */ +static void __init drop_pc6_redundant_cstates(struct cpuidle_state *states) +{ + int count; + + if (!skx_is_pc6_disabled()) + /* PC6 is not disabled, nothing to do */ + return; + + for (count = 0; states[count].enter; count++) + continue; + + if (count < 2) { + pr_debug("Too few idle states to drop PC6-redundant states\n"); + return; + } /* - * By default, the C6 state assumes the worst-case scenario of package - * C6. However, if PC6 is disabled, we update the numbers to match - * core C6. + * Sanity check: At this point all platforms with multiple C6 flavors + * use the CPUIDLE_FLAG_PARTIAL_HINT_MATCH flag. And the last state in + * the table is the one that becomes redundant when PC6 is disabled. */ - rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr); - - /* Limit value 2 and above allow for PC6. */ - if ((msr & 0x7) < 2) { - spr_cstates[2].exit_latency = 190; - spr_cstates[2].target_residency = 600; + if (!(states[count - 1].flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH)) { + pr_debug("Can't drop PC6-redundant states: unexpected flags\n"); + return; } + + /* + * On all current platforms with multiple C6 flavors, there is only one + * C-state that becomes redundant when PC6 is disabled. This state is + * the last one in the table. Drop it by marking it with + * CPUIDLE_FLAG_UNUSABLE so that cpuidle excludes it when registering + * idle states. + */ + pr_info("Dropping idle state %s because PC6 is disabled\n", + states[count - 1].name); + states[count - 1].flags |= CPUIDLE_FLAG_UNUSABLE; } /** @@ -2213,6 +2265,12 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv) case INTEL_ATOM_AIRMONT: byt_cht_auto_demotion_disable(); break; + case INTEL_GRANITERAPIDS_D: + case INTEL_GRANITERAPIDS_X: + case INTEL_ATOM_CRESTMONT_X: + case INTEL_ATOM_DARKMONT_X: + drop_pc6_redundant_cstates(cpuidle_state_table); + break; } for (cstate = 0; cstate < CPUIDLE_STATE_MAX; ++cstate) { @@ -2370,7 +2428,7 @@ static void intel_c1_demotion_toggle(void *enable) { unsigned long long msr_val; - rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr_val); + rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr_val); /* * Enable/disable C1 undemotion along with C1 demotion, as this is the * most sensible configuration in general. @@ -2379,7 +2437,7 @@ static void intel_c1_demotion_toggle(void *enable) msr_val |= NHM_C1_AUTO_DEMOTE | SNB_C1_AUTO_UNDEMOTE; else msr_val &= ~(NHM_C1_AUTO_DEMOTE | SNB_C1_AUTO_UNDEMOTE); - wrmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr_val); + wrmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr_val); } static ssize_t intel_c1_demotion_store(struct device *dev, @@ -2410,7 +2468,7 @@ static ssize_t intel_c1_demotion_show(struct device *dev, * Read the MSR value for a CPU and assume it is the same for all CPUs. Any other * configuration would be a BIOS bug. */ - rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr_val); + rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr_val); return sysfs_emit(buf, "%d\n", !!(msr_val & NHM_C1_AUTO_DEMOTE)); } static DEVICE_ATTR_RW(intel_c1_demotion); diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c index cda419638d9a..53642de7330d 100644 --- a/drivers/iio/adc/ad4695.c +++ b/drivers/iio/adc/ad4695.c @@ -876,14 +876,14 @@ static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) if (ret) goto err_unoptimize_message; - ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, - &config); + ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); if (ret) goto err_disable_busy_output; - ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); if (ret) - goto err_offload_trigger_disable; + goto err_exit_conversion_mode; mutex_lock(&st->cnv_pwm_lock); pwm_get_state(st->cnv_pwm, &state); @@ -895,23 +895,16 @@ static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) ret = pwm_apply_might_sleep(st->cnv_pwm, &state); mutex_unlock(&st->cnv_pwm_lock); if (ret) - goto err_offload_exit_conversion_mode; + goto err_offload_trigger_disable; return 0; -err_offload_exit_conversion_mode: - /* - * We have to unwind in a different order to avoid triggering offload. - * ad4695_exit_conversion_mode() triggers a conversion, so it has to be - * done after spi_offload_trigger_disable(). - */ - spi_offload_trigger_disable(st->offload, st->offload_trigger); - ad4695_exit_conversion_mode(st); - goto err_disable_busy_output; - err_offload_trigger_disable: spi_offload_trigger_disable(st->offload, st->offload_trigger); +err_exit_conversion_mode: + ad4695_exit_conversion_mode(st); + err_disable_busy_output: regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, AD4695_REG_GP_MODE_BUSY_GP_EN); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 23991a3612bd..000e39ca5c62 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -817,9 +817,11 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) } priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl"); - if (IS_ERR(priv->tsc_regmap)) + if (IS_ERR(priv->tsc_regmap)) { + kfree(buf); return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap), "failed to get amlogic,hhi-sysctrl regmap\n"); + } trimming_bits = priv->param->temperature_trimming_bits; trimming_mask = BIT(trimming_bits) - 1; diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c index 6b9ed9b1fde2..1d9724ef0983 100644 --- a/drivers/iio/adc/mt6359-auxadc.c +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -497,6 +497,7 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, return ret; /* Read the params before stopping */ + val_v = 0; regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v); mt6358_stop_imp_conv(adc_dev); diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index ddabb9600d46..61c8b825bda1 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -231,7 +231,7 @@ static int npcm_adc_probe(struct platform_device *pdev) if (IS_ERR(info->reset)) return PTR_ERR(info->reset); - info->adc_clk = devm_clk_get(&pdev->dev, NULL); + info->adc_clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(info->adc_clk)) { dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); return PTR_ERR(info->adc_clk); @@ -244,17 +244,13 @@ static int npcm_adc_probe(struct platform_device *pdev) info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto err_disable_clk; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0, "NPCM_ADC", indio_dev); - if (ret < 0) { - dev_err(dev, "failed requesting interrupt\n"); - goto err_disable_clk; - } + if (ret < 0) + return ret; reg_con = ioread32(info->regs + NPCM_ADCCON); info->vref = devm_regulator_get_optional(&pdev->dev, "vref"); @@ -262,7 +258,7 @@ static int npcm_adc_probe(struct platform_device *pdev) ret = regulator_enable(info->vref); if (ret) { dev_err(&pdev->dev, "Can't enable ADC reference voltage\n"); - goto err_disable_clk; + return ret; } iowrite32(reg_con & ~NPCM_ADCCON_REFSEL, @@ -272,10 +268,8 @@ static int npcm_adc_probe(struct platform_device *pdev) * Any error which is not ENODEV indicates the regulator * has been specified and so is a failure case. */ - if (PTR_ERR(info->vref) != -ENODEV) { - ret = PTR_ERR(info->vref); - goto err_disable_clk; - } + if (PTR_ERR(info->vref) != -ENODEV) + return PTR_ERR(info->vref); /* Use internal reference */ iowrite32(reg_con | NPCM_ADCCON_REFSEL, @@ -314,8 +308,6 @@ err_iio_register: iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); if (!IS_ERR(info->vref)) regulator_disable(info->vref); -err_disable_clk: - clk_disable_unprepare(info->adc_clk); return ret; } @@ -332,7 +324,6 @@ static void npcm_adc_remove(struct platform_device *pdev) iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); if (!IS_ERR(info->vref)) regulator_disable(info->vref); - clk_disable_unprepare(info->adc_clk); } static struct platform_driver npcm_adc_driver = { diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 9d9f2c76bed4..8f4ed3db94f0 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -198,6 +198,15 @@ static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable) writel(0, NXP_SAR_ADC_IMR(info->regs)); } +static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, unsigned int cycles) +{ + u64 rate; + + rate = clk_get_rate(info->clk); + if (rate) + ndelay(div64_u64(NSEC_PER_SEC, rate * cycles)); +} + static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable) { u32 mcr; @@ -221,7 +230,7 @@ static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable) * configuration of NCMR and the setting of NSTART. */ if (enable) - ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk) * 3)); + nxp_sar_adc_wait_for(info, 3); return pwdn; } @@ -469,7 +478,7 @@ static void nxp_sar_adc_stop_conversion(struct nxp_sar_adc *info) * only when the capture finishes. The delay will be very * short, usec-ish, which is acceptable in the atomic context. */ - ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk)) * 80); + nxp_sar_adc_wait_for(info, 80); } static int nxp_sar_adc_start_conversion(struct nxp_sar_adc *info, bool raw) @@ -560,6 +569,9 @@ static int nxp_sar_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: + if (val <= 0) + return -EINVAL; + /* * Configures the sample period duration in terms of the SAR * controller clock. The minimum acceptable value is 8. @@ -568,7 +580,11 @@ static int nxp_sar_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec * sampling timing which gives us the number of cycles expected. * The value is 8-bit wide, consequently the max value is 0xFF. */ - inpsamp = clk_get_rate(info->clk) / val - NXP_SAR_ADC_CONV_TIME; + inpsamp = clk_get_rate(info->clk) / val; + if (inpsamp < NXP_SAR_ADC_CONV_TIME) + return -EINVAL; + + inpsamp -= NXP_SAR_ADC_CONV_TIME; nxp_sar_adc_conversion_timing_set(info, inpsamp); return 0; @@ -660,7 +676,7 @@ static void nxp_sar_adc_dma_cb(void *data) static int nxp_sar_adc_start_cyclic_dma(struct iio_dev *indio_dev) { struct nxp_sar_adc *info = iio_priv(indio_dev); - struct dma_slave_config config; + struct dma_slave_config config = { }; struct dma_async_tx_descriptor *desc; int ret; diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c index f8168a14b907..48c793b18d11 100644 --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c @@ -482,7 +482,7 @@ static int adc5_gen3_get_fw_channel_data(struct adc5_chip *adc, sid = FIELD_GET(ADC5_GEN3_VIRTUAL_SID_MASK, chan); chan = FIELD_GET(ADC5_GEN3_CHANNEL_MASK, chan); - if (chan > ADC5_MAX_CHANNEL) + if (chan >= ADC5_MAX_CHANNEL) return dev_err_probe(dev, -EINVAL, "%s invalid channel number %d\n", name, chan); diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c index 9bb0b83c8f67..6efe1c618ef7 100644 --- a/drivers/iio/adc/viperboard_adc.c +++ b/drivers/iio/adc/viperboard_adc.c @@ -70,8 +70,10 @@ static int vprbrd_iio_read_raw(struct iio_dev *iio_dev, VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg, sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); if (ret != sizeof(struct vprbrd_adc_msg)) { - dev_err(&iio_dev->dev, "usb send error on adc read\n"); + mutex_unlock(&vb->lock); error = -EREMOTEIO; + dev_err(&iio_dev->dev, "usb send error on adc read\n"); + goto error; } ret = usb_control_msg(vb->usb_dev, diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index e257c1b94a5f..3980dfacbcd7 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -817,6 +817,7 @@ static int xadc_postdisable(struct iio_dev *indio_dev) { struct xadc *xadc = iio_priv(indio_dev); unsigned long scan_mask; + int seq_mode; int ret; int i; @@ -824,6 +825,12 @@ static int xadc_postdisable(struct iio_dev *indio_dev) for (i = 0; i < indio_dev->num_channels; i++) scan_mask |= BIT(indio_dev->channels[i].scan_index); + /* + * Use the correct sequencer mode for the idle state: simultaneous + * mode for dual external mux configurations, continuous otherwise. + */ + seq_mode = xadc_get_seq_mode(xadc, scan_mask); + /* Enable all channels and calibration */ ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); if (ret) @@ -834,11 +841,11 @@ static int xadc_postdisable(struct iio_dev *indio_dev) return ret; ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, - XADC_CONF1_SEQ_CONTINUOUS); + seq_mode); if (ret) return ret; - return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS); + return xadc_power_adc_b(xadc, seq_mode); } static int xadc_preenable(struct iio_dev *indio_dev) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 24d7df603760..700528c9a0a4 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -85,7 +85,7 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( */ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) { - struct hw_consumer_buffer *buf; + struct hw_consumer_buffer *buf, *tmp; struct iio_hw_consumer *hwc; struct iio_channel *chan; int ret; @@ -116,7 +116,7 @@ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) return hwc; err_put_buffers: - list_for_each_entry(buf, &hwc->buffers, head) + list_for_each_entry_safe(buf, tmp, &hwc->buffers, head) iio_buffer_put(&buf->buffer); iio_channel_release_all(hwc->channels); err_free_hwc: diff --git a/drivers/iio/chemical/mhz19b.c b/drivers/iio/chemical/mhz19b.c index 3c64154918b1..9d4cf432919e 100644 --- a/drivers/iio/chemical/mhz19b.c +++ b/drivers/iio/chemical/mhz19b.c @@ -52,6 +52,8 @@ struct mhz19b_state { struct completion buf_ready; u8 buf_idx; + bool buf_overflow; + /* * Serdev receive buffer. * When data is received from the MH-Z19B, @@ -106,6 +108,10 @@ static int mhz19b_serdev_cmd(struct iio_dev *indio_dev, int cmd, u16 arg) cmd_buf[8] = mhz19b_get_checksum(cmd_buf); /* Write buf to uart ctrl synchronously */ + st->buf_idx = 0; + st->buf_overflow = false; + reinit_completion(&st->buf_ready); + ret = serdev_device_write(serdev, cmd_buf, MHZ19B_CMD_SIZE, 0); if (ret < 0) return ret; @@ -121,6 +127,9 @@ static int mhz19b_serdev_cmd(struct iio_dev *indio_dev, int cmd, u16 arg) if (!ret) return -ETIMEDOUT; + if (st->buf_overflow) + return -EMSGSIZE; + if (st->buf[8] != mhz19b_get_checksum(st->buf)) { dev_err(dev, "checksum err"); return -EINVAL; @@ -240,6 +249,14 @@ static size_t mhz19b_receive_buf(struct serdev_device *serdev, { struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); struct mhz19b_state *st = iio_priv(indio_dev); + size_t remaining = MHZ19B_CMD_SIZE - st->buf_idx; + + if (len > remaining) { + st->buf_idx = 0; + st->buf_overflow = true; + complete(&st->buf_ready); + return len; + } memcpy(st->buf + st->buf_idx, data, len); st->buf_idx += len; diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index a665fcb78806..11d6bc1b63e6 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -256,7 +256,7 @@ static int scd30_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const guard(mutex)(&state->lock); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val) + if (val || !val2) return -EINVAL; val = 1000000000 / val2; diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index da09c9f3ceb6..e2538a84c812 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -590,6 +590,7 @@ static void ssp_remove(struct spi_device *spi) ssp_clean_pending_list(data); free_irq(data->spi->irq, data); + cancel_delayed_work_sync(&data->work_refresh); timer_delete_sync(&data->wdt_timer); cancel_work_sync(&data->work_wdt); diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index b97b46090d80..d9db3226ecd6 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -105,6 +105,12 @@ static const char * const ad3530r_powerdown_modes[] = { "32kohm_to_gnd", }; +static const char * const ad3531r_powerdown_modes[] = { + "500ohm_to_gnd", + "3.85kohm_to_gnd", + "16kohm_to_gnd", +}; + static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -133,6 +139,13 @@ static const struct iio_enum ad3530r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static const struct iio_enum ad3531r_powerdown_mode_enum = { + .items = ad3531r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3531r_powerdown_modes), + .get = ad3530r_get_powerdown_mode, + .set = ad3530r_set_powerdown_mode, +}; + static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -276,7 +289,20 @@ static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { { } }; -#define AD3530R_CHAN(_chan) \ +static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3530r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3531r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3531r_powerdown_mode_enum), + { } +}; + +#define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -284,25 +310,25 @@ static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { .output = 1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ - .ext_info = ad3530r_ext_info, \ + .ext_info = _ext_info, \ } static const struct iio_chan_spec ad3530r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), - AD3530R_CHAN(4), - AD3530R_CHAN(5), - AD3530R_CHAN(6), - AD3530R_CHAN(7), + AD3530R_CHAN(0, ad3530r_ext_info), + AD3530R_CHAN(1, ad3530r_ext_info), + AD3530R_CHAN(2, ad3530r_ext_info), + AD3530R_CHAN(3, ad3530r_ext_info), + AD3530R_CHAN(4, ad3530r_ext_info), + AD3530R_CHAN(5, ad3530r_ext_info), + AD3530R_CHAN(6, ad3530r_ext_info), + AD3530R_CHAN(7, ad3530r_ext_info), }; static const struct iio_chan_spec ad3531r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), + AD3530R_CHAN(0, ad3531r_ext_info), + AD3530R_CHAN(1, ad3531r_ext_info), + AD3530R_CHAN(2, ad3531r_ext_info), + AD3530R_CHAN(3, ad3531r_ext_info), }; static const struct ad3530r_chip_info ad3530_chip = { diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 4b18498aa074..a7213bc6b156 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -25,22 +25,37 @@ static const char * const ad5686_powerdown_modes[] = { "three_state" }; +static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) +{ + if (chan->channel == chan->address) + return chan->channel * 2; + + /* one-hot encoding is used in dual/quad channel devices */ + return __ffs(chan->address) * 2; +} + static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); - return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; + guard(mutex)(&st->lock); + + return ((st->pwr_down_mode >> shift) & 0x3U) - 1; } static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int mode) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); - st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); - st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); + guard(mutex)(&st->lock); + + st->pwr_down_mode &= ~(0x3U << shift); + st->pwr_down_mode |= (mode + 1) << shift; return 0; } @@ -55,10 +70,12 @@ static const struct iio_enum ad5686_powerdown_mode_enum = { static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); - return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & - (0x3 << (chan->channel * 2)))); + guard(mutex)(&st->lock); + + return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3U << shift))); } static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, @@ -77,10 +94,12 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; + guard(mutex)(&st->lock); + if (readin) - st->pwr_down_mask |= (0x3 << (chan->channel * 2)); + st->pwr_down_mask |= 0x3U << ad5686_pd_mask_shift(chan); else - st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); + st->pwr_down_mask &= ~(0x3U << ad5686_pd_mask_shift(chan)); switch (st->chip_info->regmap_type) { case AD5310_REGMAP: @@ -154,7 +173,7 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (val > (1 << chan->scan_type.realbits) || val < 0) + if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; mutex_lock(&st->lock); @@ -460,7 +479,7 @@ int ad5686_probe(struct device *dev, { struct ad5686_state *st; struct iio_dev *indio_dev; - unsigned int val, ref_bit_msk; + unsigned int val, ref_bit_msk, shift; bool has_external_vref; u8 cmd; int ret, i; @@ -484,9 +503,18 @@ int ad5686_probe(struct device *dev, has_external_vref = ret != -ENODEV; st->vref_mv = has_external_vref ? ret / 1000 : st->chip_info->int_vref_mv; + /* Initialize masks to all ones provided the max shift (last channel) */ + shift = ad5686_pd_mask_shift(&st->chip_info->channels[st->chip_info->num_channels - 1]); + st->pwr_down_mask = GENMASK(shift + 1, 0); + st->pwr_down_mode = GENMASK(shift + 1, 0); + /* Set all the power down mode for all channels to 1K pulldown */ - for (i = 0; i < st->chip_info->num_channels; i++) - st->pwr_down_mode |= (0x01 << (i * 2)); + for (i = 0; i < st->chip_info->num_channels; i++) { + shift = ad5686_pd_mask_shift(&st->chip_info->channels[i]); + st->pwr_down_mask &= ~(0x3U << shift); /* powered up state */ + st->pwr_down_mode &= ~(0x3U << shift); + st->pwr_down_mode |= 0x01U << shift; + } indio_dev->name = name; indio_dev->info = &ad5686_info; @@ -509,7 +537,7 @@ int ad5686_probe(struct device *dev, break; case AD5686_REGMAP: cmd = AD5686_CMD_INTERNAL_REFER_SETUP; - ref_bit_msk = 0; + ref_bit_msk = AD5686_REF_BIT_MSK; break; case AD5693_REGMAP: cmd = AD5686_CMD_CONTROL_REG; @@ -520,9 +548,9 @@ int ad5686_probe(struct device *dev, return -EINVAL; } - val = (has_external_vref | ref_bit_msk); + val = has_external_vref ? ref_bit_msk : 0; - ret = st->write(st, cmd, 0, !!val); + ret = st->write(st, cmd, 0, val); if (ret) return ret; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index e7d36bae3e59..36e16c5c4581 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -46,6 +46,7 @@ #define AD5310_REF_BIT_MSK BIT(8) #define AD5683_REF_BIT_MSK BIT(12) +#define AD5686_REF_BIT_MSK BIT(0) #define AD5693_REF_BIT_MSK BIT(12) /** diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index e7e29359f8fe..dd4e35460195 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -90,6 +90,7 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data, const struct iio_chan_spec *chan) { u8 outbuf[2]; + int ret; outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE; @@ -103,7 +104,13 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data, else outbuf[1] |= MAX5821_EXTENDED_POWER_UP; - return i2c_master_send(data->client, outbuf, 2); + ret = i2c_master_send(data->client, outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + if (ret != sizeof(outbuf)) + return -EIO; + + return 0; } static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 586e6cfa14a9..91b9c5f18ec4 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -287,6 +287,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, addr = adis16260_addresses[chan->scan_index][1]; return adis_write_reg_16(adis, addr, val); case IIO_CHAN_INFO_SAMP_FREQ: + if (val <= 0) + return -EINVAL; + if (spi_get_device_id(adis->spi)->driver_data) t = 256 / val; else diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index cf97adfa9727..87efa2c74ca4 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -34,7 +34,7 @@ static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf) .addr = i2c->addr, .flags = i2c->flags | I2C_M_RD, .len = ITG3200_SCAN_ELEMENTS * sizeof(s16), - .buf = (char *)&buf, + .buf = (char *)buf, }, }; diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 1f2af506f4bd..75679612052f 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -836,7 +836,7 @@ static irqreturn_t adis16550_trigger_handler(int irq, void *p) u16 dummy; bool valid; struct iio_poll_func *pf = p; - __be32 data[ADIS16550_MAX_SCAN_DATA] __aligned(8); + __be32 data[ADIS16550_MAX_SCAN_DATA] __aligned(8) = { }; struct iio_dev *indio_dev = pf->indio_dev; struct adis16550 *st = iio_priv(indio_dev); struct adis *adis = iio_device_get_drvdata(indio_dev); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 5b28a3ffcc3d..48291203d1cd 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -609,7 +609,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) * must be passed a buffer that is aligned to 8 bytes so * as to allow insertion of a naturally aligned timestamp. */ - u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8); + u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8) = { }; u8 tag; bool reset_ts = false; int i, err, read_len; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 46f36a6ed271..5c3df993bea2 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1909,6 +1909,7 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, dma_resv_add_fence(dmabuf->resv, &fence->base, dma_to_ram ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ); + dma_fence_put(&fence->base); dma_resv_unlock(dmabuf->resv); cookie = dma_fence_begin_signalling(); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 0df0ab3de270..9ce20cb05a9b 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -738,7 +738,11 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, if (ret < 0) return ret; - return iio_multiply_value(val, scale, ret, pval, pval2); + ret = iio_multiply_value(val, scale, ret, pval, pval2); + if (ret < 0) + return ret; + + return 0; } else { ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); if (ret < 0) diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index 79ad6e2209ca..0fe61b8a7029 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -89,15 +89,14 @@ static int cm3323_init(struct iio_dev *indio_dev) /* enable sensor and set auto force mode */ ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT); + data->reg_conf = ret; - ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret); + ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_conf\n"); return ret; } - data->reg_conf = ret; - return 0; } diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c index 74d7246e5225..4bbd86d0cb46 100644 --- a/drivers/iio/light/veml6070.c +++ b/drivers/iio/light/veml6070.c @@ -245,13 +245,6 @@ static const struct iio_info veml6070_info = { .write_raw = veml6070_write_raw, }; -static void veml6070_i2c_unreg(void *p) -{ - struct veml6070_data *data = p; - - i2c_unregister_device(data->client2); -} - static int veml6070_probe(struct i2c_client *client) { struct veml6070_data *data; @@ -281,7 +274,8 @@ static int veml6070_probe(struct i2c_client *client) if (ret < 0) return ret; - data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB); + data->client2 = devm_i2c_new_dummy_device(&client->dev, client->adapter, + VEML6070_ADDR_DATA_LSB); if (IS_ERR(data->client2)) return dev_err_probe(&client->dev, PTR_ERR(data->client2), "i2c device for second chip address failed\n"); @@ -292,10 +286,6 @@ static int veml6070_probe(struct i2c_client *client) if (ret < 0) return ret; - ret = devm_add_action_or_reset(&client->dev, veml6070_i2c_unreg, data); - if (ret < 0) - return ret; - return devm_iio_device_register(&client->dev, indio_dev); } diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index ef348d316c00..7644bd04654b 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -506,6 +506,11 @@ static const struct st_sensors_platform_data default_magn_pdata = { .drdy_int_pin = 2, }; +/* LIS2MDL only supports DRDY on INT1 */ +static const struct st_sensors_platform_data alt_magn_pdata = { + .drdy_int_pin = 1, +}; + static int st_magn_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -628,8 +633,12 @@ int st_magn_common_probe(struct iio_dev *indio_dev) mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0]; mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz; - if (!pdata) - pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + if (!pdata) { + if (mdata->sensor_settings->drdy_irq.int2.mask) + pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + else + pdata = (struct st_sensors_platform_data *)&alt_magn_pdata; + } err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index d983ce9c0b99..9b489766e457 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -2616,7 +2616,7 @@ static irqreturn_t bmp580_trigger_handler(int irq, void *p) __le32 comp_temp; __le32 comp_press; aligned_s64 timestamp; - } buffer; + } buffer = { }; int ret; guard(mutex)(&data->lock); diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index 334bba6fdae6..104dd45598b0 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -119,7 +119,7 @@ static bool tsys01_crc_valid(u16 *n_prom) u8 sum = 0; for (cnt = 0; cnt < TSYS01_PROM_WORDS_NB; cnt++) - sum += ((n_prom[0] >> 8) + (n_prom[0] & 0xFF)); + sum += ((n_prom[cnt] >> 8) + (n_prom[cnt] & 0xFF)); return (sum == 0); } diff --git a/drivers/infiniband/core/iter.c b/drivers/infiniband/core/iter.c index 8e543d100657..3ed351e8fcf6 100644 --- a/drivers/infiniband/core/iter.c +++ b/drivers/infiniband/core/iter.c @@ -19,8 +19,8 @@ EXPORT_SYMBOL(__rdma_block_iter_start); bool __rdma_block_iter_next(struct ib_block_iter *biter) { - unsigned int block_offset; - unsigned int delta; + dma_addr_t block_offset; + dma_addr_t delta; if (!biter->__sg_nents || !biter->__sg) return false; diff --git a/drivers/infiniband/core/ucaps.c b/drivers/infiniband/core/ucaps.c index 948093260dbd..5155ff0e538e 100644 --- a/drivers/infiniband/core/ucaps.c +++ b/drivers/infiniband/core/ucaps.c @@ -82,14 +82,12 @@ static int get_ucap_from_devt(dev_t devt, u64 *idx_mask) static int get_devt_from_fd(unsigned int fd, dev_t *ret_dev) { - struct file *file; + CLASS(fd, f)(fd); - file = fget(fd); - if (!file) + if (fd_empty(f) || fd_file(f)->f_op != &ucaps_cdev_fops) return -EBADF; - *ret_dev = file_inode(file)->i_rdev; - fput(file); + *ret_dev = file_inode(fd_file(f))->i_rdev; return 0; } diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 786fa1aa8e55..4b055712b0d0 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -332,3 +332,19 @@ int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, return 0; } EXPORT_SYMBOL(ib_umem_copy_from); + +/* + * Called during rereg mr if the driver is able to re-use a umem for + * IB_MR_REREG_ACCESS. + */ +int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags) +{ + if (!umem) + return 0; + + if ((flags & IB_MR_REREG_ACCESS) && !(flags & IB_MR_REREG_TRANS)) + if (ib_access_writable(new_access_flags) && !umem->writable) + return -EACCES; + return 0; +} +EXPORT_SYMBOL(ib_umem_check_rereg); diff --git a/drivers/infiniband/core/uverbs_std_types_dmah.c b/drivers/infiniband/core/uverbs_std_types_dmah.c index 453ce656c6f2..97101e093826 100644 --- a/drivers/infiniband/core/uverbs_std_types_dmah.c +++ b/drivers/infiniband/core/uverbs_std_types_dmah.c @@ -47,6 +47,11 @@ static int UVERBS_HANDLER(UVERBS_METHOD_DMAH_ALLOC)( if (ret) goto err; + if (dmah->cpu_id >= nr_cpu_ids) { + ret = -EINVAL; + goto err; + } + if (!cpumask_test_cpu(dmah->cpu_id, current->cpus_ptr)) { ret = -EPERM; goto err; diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 7bd0838ebc99..9b2b652800e4 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -613,7 +613,8 @@ err_remove_mmap: } static int efa_qp_validate_cap(struct efa_dev *dev, - struct ib_qp_init_attr *init_attr) + struct ib_qp_init_attr *init_attr, + u32 sq_ring_size) { if (init_attr->cap.max_send_wr > dev->dev_attr.max_sq_depth) { ibdev_dbg(&dev->ibdev, @@ -622,6 +623,14 @@ static int efa_qp_validate_cap(struct efa_dev *dev, dev->dev_attr.max_sq_depth); return -EINVAL; } + + if (sq_ring_size > dev->dev_attr.max_llq_size) { + ibdev_dbg(&dev->ibdev, + "qp: requested sq ring size[%u] exceeds the max[%u]\n", + sq_ring_size, dev->dev_attr.max_llq_size); + return -EINVAL; + } + if (init_attr->cap.max_recv_wr > dev->dev_attr.max_rq_depth) { ibdev_dbg(&dev->ibdev, "qp: requested receive wr[%u] exceeds the max[%u]\n", @@ -691,14 +700,6 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, ucontext = rdma_udata_to_drv_context(udata, struct efa_ucontext, ibucontext); - err = efa_qp_validate_cap(dev, init_attr); - if (err) - goto err_out; - - err = efa_qp_validate_attr(dev, init_attr); - if (err) - goto err_out; - err = ib_copy_validate_udata_in_cm(udata, cmd, driver_qp_type, 0); if (err) goto err_out; @@ -720,6 +721,14 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, goto err_out; } + err = efa_qp_validate_cap(dev, init_attr, cmd.sq_ring_size); + if (err) + goto err_out; + + err = efa_qp_validate_attr(dev, init_attr); + if (err) + goto err_out; + create_qp_params.uarn = ucontext->uarn; create_qp_params.pd = to_epd(ibqp->pd)->pdn; diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index 896af1828a38..25bfd3970f5b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -300,6 +300,10 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, goto err_out; } + ret = ib_umem_check_rereg(mr->pbl_mtr.umem, flags, mr_access_flags); + if (ret) + goto err_out; + mailbox = hns_roce_alloc_cmd_mailbox(hr_dev); ret = PTR_ERR_OR_ZERO(mailbox); if (ret) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 17086048d2d7..8cd427532805 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3803,6 +3803,10 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + ret = ib_umem_check_rereg(iwmr->region, flags, new_access); + if (ret) + return ERR_PTR(ret); + if (dmabuf_revocable) { umem_dmabuf = to_ib_umem_dmabuf(iwmr->region); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 650b4a9121ff..6747bca30677 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -209,6 +209,10 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, struct mlx4_mpt_entry **pmpt_entry = &mpt_entry; int err; + err = ib_umem_check_rereg(mmr->umem, flags, mr_access_flags); + if (err) + return ERR_PTR(err); + /* Since we synchronize this call and mlx4_ib_dereg_mr via uverbs, * we assume that the calls can't run concurrently. Otherwise, a * race exists. diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 3b6da45061a5..fb40b44496f4 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1179,6 +1179,10 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + err = ib_umem_check_rereg(mr->umem, flags, new_access_flags); + if (err) + return ERR_PTR(err); + if (!(flags & IB_MR_REREG_ACCESS)) new_access_flags = mr->access_flags; if (!(flags & IB_MR_REREG_PD)) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 4d4891dc2884..4cf04a44189c 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -1319,6 +1319,7 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, struct rxe_mr *mr = to_rmr(ibmr); struct rxe_pd *old_pd = to_rpd(ibmr->pd); struct rxe_pd *pd = to_rpd(ibpd); + int err; /* for now only support the two easy cases: * rereg_pd and rereg_access @@ -1328,6 +1329,10 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, return ERR_PTR(-EOPNOTSUPP); } + err = ib_umem_check_rereg(mr->umem, flags, access); + if (err) + return ERR_PTR(err); + if (flags & IB_MR_REREG_PD) { rxe_put(old_pd); rxe_get(pd); diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 348005e71891..1015a51f750a 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1383,6 +1383,12 @@ isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc) ib_dma_sync_single_for_cpu(ib_dev, isert_conn->login_desc->dma_addr, ISER_RX_SIZE, DMA_FROM_DEVICE); + if (unlikely(wc->byte_len < ISER_HEADERS_LEN)) { + isert_dbg("login request length %u is too short\n", + wc->byte_len); + return; + } + isert_conn->login_req_len = wc->byte_len - ISER_HEADERS_LEN; if (isert_conn->conn) { diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index b58868e1cf11..acbd787de265 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1932,7 +1932,8 @@ static int srp_post_recv(struct srp_rdma_ch *ch, struct srp_iu *iu) return ib_post_recv(ch->qp, &wr, NULL); } -static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) +static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp, + u32 byte_len) { struct srp_target_port *target = ch->target; struct srp_request *req; @@ -1973,10 +1974,27 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) scmnd->result = rsp->status; if (rsp->flags & SRP_RSP_FLAG_SNSVALID) { - memcpy(scmnd->sense_buffer, rsp->data + - be32_to_cpu(rsp->resp_data_len), - min_t(int, be32_to_cpu(rsp->sense_data_len), - SCSI_SENSE_BUFFERSIZE)); + u32 resp_len = be32_to_cpu(rsp->resp_data_len); + u32 sense_len = be32_to_cpu(rsp->sense_data_len); + + /* + * The sense data starts resp_data_len bytes past the + * response data area; both lengths come from the + * target-controlled response. Copy the sense data + * only if it has not been truncated, that is, only if + * the full sense region fits within the bytes actually + * received. Otherwise the copy source would run past + * the receive buffer (sized to the target-chosen + * max_ti_iu_len), reading out of bounds. + */ + if (sizeof(*rsp) + (u64)resp_len + sense_len <= byte_len) + memcpy(scmnd->sense_buffer, + rsp->data + resp_len, + min(sense_len, SCSI_SENSE_BUFFERSIZE)); + else + shost_printk(KERN_ERR, target->scsi_host, + "dropping truncated sense data (resp_data_len %u sense_data_len %u, %u bytes received)\n", + resp_len, sense_len, byte_len); } if (unlikely(rsp->flags & SRP_RSP_FLAG_DIUNDER)) @@ -2086,7 +2104,7 @@ static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc) switch (opcode) { case SRP_RSP: - srp_process_rsp(ch, iu->buf); + srp_process_rsp(ch, iu->buf, wc->byte_len); break; case SRP_CRED_REQ: diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 423cccdea34f..1e8c6c044844 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -125,8 +125,8 @@ static void fm801_gp_remove(struct pci_dev *pci) } static const struct pci_device_id fm801_gp_id_table[] = { - { PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE(FORTEMEDIA, PCI_DEVICE_ID_FM801_GP) }, + { } }; MODULE_DEVICE_TABLE(pci, fm801_gp_id_table); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 0549fdc5a985..feb8f368f834 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -186,6 +186,10 @@ static const struct xpad_device { { 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 }, { 0x0b05, 0x1a38, "ASUS ROG RAIKIRI", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, { 0x0b05, 0x1abb, "ASUS ROG RAIKIRI PRO", 0, XTYPE_XBOXONE }, + { 0x0b05, 0x1c91, "ASUS ROG RAIKIRI II", 0, XTYPE_XBOX360 }, + { 0x0b05, 0x1c92, "ASUS ROG RAIKIRI II WIRELESS", 0, XTYPE_XBOX360 }, + { 0x0b05, 0x1c96, "ASUS ROG RAIKIRI II XBOX", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, + { 0x0b05, 0x1d04, "ASUS ROG RAIKIRI II XBOX WIRELESS", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, { 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX }, { 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, @@ -391,6 +395,7 @@ static const struct xpad_device { { 0x3285, 0x0662, "Nacon Revolution5 Pro", 0, XTYPE_XBOX360 }, { 0x3285, 0x0663, "Nacon Evol-X", 0, XTYPE_XBOXONE }, { 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 }, + { 0x3537, 0x100f, "GameSir Nova 2 Lite", 0, XTYPE_XBOX360 }, { 0x3537, 0x1010, "GameSir G7 SE", 0, XTYPE_XBOXONE }, { 0x3651, 0x1000, "CRKD SG", 0, XTYPE_XBOX360 }, { 0x366c, 0x0005, "ByoWave Proteus Controller", MAP_SHARE_BUTTON, XTYPE_XBOXONE, FLAG_DELAY_INIT }, @@ -507,6 +512,7 @@ static const struct usb_device_id xpad_table[] = { { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */ XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz Gamepad */ + XPAD_XBOX360_VENDOR(0x0b05), /* ASUS controllers */ XPAD_XBOXONE_VENDOR(0x0b05), /* ASUS controllers */ XPAD_XBOX360_VENDOR(0x0c12), /* Zeroplus X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x0db0), /* Micro Star International X-Box 360 controllers */ @@ -1077,10 +1083,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char input_report_key(dev, BTN_START, data[4] & BIT(2)); input_report_key(dev, BTN_SELECT, data[4] & BIT(3)); if (xpad->mapping & MAP_SHARE_BUTTON) { - if (xpad->mapping & MAP_SHARE_OFFSET) - input_report_key(dev, KEY_RECORD, data[len - 26] & BIT(0)); - else - input_report_key(dev, KEY_RECORD, data[len - 18] & BIT(0)); + u32 offset = (xpad->mapping & MAP_SHARE_OFFSET) ? 26 : 18; + + if (len >= offset) + input_report_key(dev, KEY_RECORD, data[len - offset] & BIT(0)); } /* buttons A,B,X,Y */ diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index c8ad55f26ea8..8cb4dc6fb165 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1923,6 +1923,21 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + /* Lenovo Yoga Air 14 (83QK) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), + }, + .callback = atkbd_deactivate_fixup, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 47b31725e850..835ad45a9d65 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -60,11 +60,15 @@ static acpi_status acpi_atlas_button_handler(u32 function, static int atlas_acpi_button_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; acpi_status status; int i; int err; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + input_dev = input_allocate_device(); if (!input_dev) { pr_err("unable to allocate input device\n"); diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 4c022a36dbe8..7a1cb9333f53 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1624,7 +1624,7 @@ static void ims_pcu_buffers_free(struct ims_pcu *pcu) usb_kill_urb(pcu->urb_in); usb_free_urb(pcu->urb_in); - usb_free_coherent(pcu->udev, pcu->max_out_size, + usb_free_coherent(pcu->udev, pcu->max_in_size, pcu->urb_in_buf, pcu->read_dma); kfree(pcu->urb_out_buf); diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index fee1796da3d0..5cba02a156ce 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -162,6 +162,9 @@ static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, case 0x15: *validpage_count = 1024; break; + case 0x19: + *validpage_count = 2032; + break; default: /* unknown ic type clear value */ *validpage_count = 0; @@ -645,6 +648,11 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, return error; } + if (fw->size < data->fw_signature_address + sizeof(signature)) { + dev_err(dev, "firmware file too small\n"); + return -EBADF; + } + /* Firmware file must match signature data */ fw_signature = &fw->data[data->fw_signature_address]; if (memcmp(fw_signature, signature, sizeof(signature)) != 0) { diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index a9057d124a88..88d4070d4b44 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -690,7 +690,7 @@ static int elan_i2c_finish_fw_update(struct i2c_client *client, if (error) { dev_err(dev, "device reset failed: %d\n", error); } else if (!wait_for_completion_timeout(completion, - msecs_to_jiffies(300))) { + msecs_to_jiffies(700))) { dev_err(dev, "timeout waiting for device reset\n"); error = -ETIMEDOUT; } diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 26071128f43a..c70502e24031 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -190,6 +190,7 @@ static const char * const smbus_pnp_ids[] = { "LEN2044", /* L470 */ "LEN2054", /* E480 */ "LEN2055", /* E580 */ + "LEN2058", /* E490 */ "LEN2068", /* T14 Gen 1 */ "SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */ "SYN3003", /* HP EliteBook 850 G1 */ diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 87c6a10381f2..f21bf2844112 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -275,8 +275,8 @@ struct mxt_cfg { off_t raw_pos; u8 *mem; - size_t mem_size; - int start_ofs; + u16 mem_size; + u16 start_ofs; struct mxt_info info; }; @@ -1473,7 +1473,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) } cfg->raw_pos += offset; - if (i > mxt_obj_size(object)) + if (i >= mxt_obj_size(object)) continue; byte_offset = reg + i - cfg->start_ofs; @@ -1627,6 +1627,13 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) cfg.start_ofs = MXT_OBJECT_START + data->info->object_num * sizeof(struct mxt_object) + MXT_INFO_CHECKSUM_SIZE; + + if (data->mem_size <= cfg.start_ofs) { + dev_err(dev, "Memory size too small: %u < %u\n", + data->mem_size, cfg.start_ofs); + return -EINVAL; + } + cfg.mem_size = data->mem_size - cfg.start_ofs; u8 *mem_buf __free(kfree) = cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index daa28135f887..0bbacb517c28 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -1067,6 +1067,11 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) if (x_len > 0xff) x_len -= 0x80; + if (data_len > usbtouch->data_size - sizeof(*packet)) + data_len = usbtouch->data_size - sizeof(*packet); + if (x_len > data_len) + x_len = data_len; + /* send ACK */ ret = usb_submit_urb(priv->ack, GFP_ATOMIC); if (ret) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 54d96e847f16..9b1bf6c8c6b3 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -1918,12 +1918,18 @@ static int iommu_dma_iova_link_swiotlb(struct device *dev, return 0; } + /* + * After removing the partial head and tail, there may be no aligned + * middle left to map. The tail still gets bounced below. + */ size -= iova_end_pad; - error = __dma_iova_link(dev, addr + mapped, phys + mapped, size, dir, - attrs); - if (error) - goto out_unmap; - mapped += size; + if (size) { + error = __dma_iova_link(dev, addr + mapped, phys + mapped, + size, dir, attrs); + if (error) + goto out_unmap; + mapped += size; + } if (iova_end_pad) { error = iommu_dma_iova_bounce_and_link(dev, addr + mapped, @@ -1936,7 +1942,8 @@ static int iommu_dma_iova_link_swiotlb(struct device *dev, return 0; out_unmap: - dma_iova_unlink(dev, state, 0, mapped, dir, attrs); + if (mapped) + dma_iova_unlink(dev, state, offset, mapped, dir, attrs); return error; } @@ -2142,18 +2149,21 @@ EXPORT_SYMBOL_GPL(dma_iova_destroy); void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain) { + bool dma_iommu; + if (dev_is_pci(dev)) dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac; - dev->dma_iommu = iommu_is_dma_domain(domain); - if (dev->dma_iommu && iommu_dma_init_domain(domain, dev)) + dma_iommu = iommu_is_dma_domain(domain); + dev_assign_dma_iommu(dev, dma_iommu); + if (dma_iommu && iommu_dma_init_domain(domain, dev)) goto out_err; return; out_err: pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n", dev_name(dev)); - dev->dma_iommu = false; + dev_clear_dma_iommu(dev); } static bool has_msi_cookie(const struct iommu_domain *domain) diff --git a/drivers/iommu/intel/cache.c b/drivers/iommu/intel/cache.c index be8410f0e841..fdc88817709f 100644 --- a/drivers/iommu/intel/cache.c +++ b/drivers/iommu/intel/cache.c @@ -254,37 +254,29 @@ void cache_tag_unassign_domain(struct dmar_domain *domain, } static unsigned long calculate_psi_aligned_address(unsigned long start, - unsigned long end, - unsigned long *_mask) + unsigned long last, + unsigned long *size_order) { - unsigned long pages = aligned_nrpages(start, end - start + 1); - unsigned long aligned_pages = __roundup_pow_of_two(pages); - unsigned long bitmask = aligned_pages - 1; - unsigned long mask = ilog2(aligned_pages); - unsigned long pfn = IOVA_PFN(start); - - /* - * PSI masks the low order bits of the base address. If the - * address isn't aligned to the mask, then compute a mask value - * needed to ensure the target range is flushed. - */ - if (unlikely(bitmask & pfn)) { - unsigned long end_pfn = pfn + pages - 1, shared_bits; - + unsigned int sz_lg2; + + /* Compute a sz_lg2 that spans start and last */ + start &= GENMASK(BITS_PER_LONG - 1, VTD_PAGE_SHIFT); + sz_lg2 = fls_long(start ^ last); + if (sz_lg2 <= 12) { + *size_order = 0; + return start; + } + if (unlikely(sz_lg2 >= BITS_PER_LONG)) { /* - * Since end_pfn <= pfn + bitmask, the only way bits - * higher than bitmask can differ in pfn and end_pfn is - * by carrying. This means after masking out bitmask, - * high bits starting with the first set bit in - * shared_bits are all equal in both pfn and end_pfn. + * MAX_AGAW_PFN_WIDTH triggers full invalidation in all + * downstream users. */ - shared_bits = ~(pfn ^ end_pfn) & ~bitmask; - mask = shared_bits ? __ffs(shared_bits) : MAX_AGAW_PFN_WIDTH; + *size_order = MAX_AGAW_PFN_WIDTH; + return 0; } - *_mask = mask; - - return ALIGN_DOWN(start, VTD_PAGE_SIZE << mask); + *size_order = sz_lg2 - VTD_PAGE_SHIFT; + return start & GENMASK(BITS_PER_LONG - 1, sz_lg2); } static void qi_batch_flush_descs(struct intel_iommu *iommu, struct qi_batch *batch) @@ -441,12 +433,7 @@ void cache_tag_flush_range(struct dmar_domain *domain, unsigned long start, struct cache_tag *tag; unsigned long flags; - if (start == 0 && end == ULONG_MAX) { - addr = 0; - mask = MAX_AGAW_PFN_WIDTH; - } else { - addr = calculate_psi_aligned_address(start, end, &mask); - } + addr = calculate_psi_aligned_address(start, end, &mask); spin_lock_irqsave(&domain->cache_lock, flags); list_for_each_entry(tag, &domain->cache_tags, node) { diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c index 40e33257d3c2..1dbef8c55007 100644 --- a/drivers/iommu/io-pgtable-arm-v7s.c +++ b/drivers/iommu/io-pgtable-arm-v7s.c @@ -777,21 +777,27 @@ struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns = { static struct io_pgtable_cfg *cfg_cookie __initdata; -static void __init dummy_tlb_flush_all(void *cookie) +/* + * __noipa prevents gcc from turning indirect iommu_flush_ops calls + * into direct calls from a specialized __arm_v7s_unmap() that triggers + * a build time section mismatch assertion. + */ +static __noipa void __init dummy_tlb_flush_all(void *cookie) { WARN_ON(cookie != cfg_cookie); } -static void __init dummy_tlb_flush(unsigned long iova, size_t size, - size_t granule, void *cookie) +static __noipa void __init dummy_tlb_flush(unsigned long iova, size_t size, + size_t granule, void *cookie) { WARN_ON(cookie != cfg_cookie); WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); } -static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather, - unsigned long iova, size_t granule, - void *cookie) +static __noipa void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather, + unsigned long iova, + size_t granule, + void *cookie) { dummy_tlb_flush(iova, granule, granule, cookie); } diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index d1a9e713d3a0..e8f13dcebbde 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -611,9 +611,8 @@ static void iommu_deinit_device(struct device *dev) dev->iommu_group = NULL; module_put(ops->owner); dev_iommu_free(dev); -#ifdef CONFIG_IOMMU_DMA - dev->dma_iommu = false; -#endif + if (IS_ENABLED(CONFIG_IOMMU_DMA)) + dev_clear_dma_iommu(dev); } static struct iommu_domain *pasid_array_entry_to_domain(void *entry) diff --git a/drivers/irqchip/.kunitconfig b/drivers/irqchip/.kunitconfig new file mode 100644 index 000000000000..00a12703f635 --- /dev/null +++ b/drivers/irqchip/.kunitconfig @@ -0,0 +1,5 @@ +CONFIG_KUNIT=y +CONFIG_OF=y +CONFIG_COMPILE_TEST=y +CONFIG_ASPEED_AST2700_INTC=y +CONFIG_ASPEED_AST2700_INTC_TEST=y diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index e755a2a05209..fdf27cf529fc 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -110,6 +110,29 @@ config AL_FIC help Support Amazon's Annapurna Labs Fabric Interrupt Controller. +config ASPEED_AST2700_INTC + bool "ASPEED AST2700 Interrupt Controller support" + depends on OF + depends on ARCH_ASPEED || COMPILE_TEST + select IRQ_DOMAIN_HIERARCHY + help + Enable support for the ASPEED AST2700 interrupt controller. + This driver handles interrupt, routing and merged interrupt + sources to upstream parent interrupt controllers. + + If unsure, say N. + +config ASPEED_AST2700_INTC_TEST + bool "Tests for the ASPEED AST2700 Interrupt Controller" + depends on ASPEED_AST2700_INTC && KUNIT=y + default KUNIT_ALL_TESTS + help + Enable KUnit tests for AST2700 INTC route resolution. + The tests exercise error handling and route selection paths. + This option is intended for test builds. + + If unsure, say N. + config ATMEL_AIC_IRQ bool select GENERIC_IRQ_CHIP @@ -476,7 +499,7 @@ config STM32_EXTI select GENERIC_IRQ_CHIP config QCOM_IRQ_COMBINER - bool "QCOM IRQ combiner support" + bool "Qualcomm IRQ combiner support" depends on ARCH_QCOM && ACPI select IRQ_DOMAIN_HIERARCHY help @@ -509,7 +532,7 @@ config GOLDFISH_PIC for Goldfish based virtual platforms. config QCOM_PDC - tristate "QCOM PDC" + tristate "Qualcomm PDC" depends on ARCH_QCOM select IRQ_DOMAIN_HIERARCHY help @@ -517,7 +540,7 @@ config QCOM_PDC IRQs for Qualcomm Technologies Inc (QTI) mobile chips. config QCOM_MPM - tristate "QCOM MPM" + tristate "Qualcomm MPM" depends on ARCH_QCOM depends on MAILBOX select IRQ_DOMAIN_HIERARCHY @@ -654,13 +677,13 @@ config SIFIVE_PLIC select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP -config STARFIVE_JH8100_INTC - bool "StarFive JH8100 External Interrupt Controller" +config STARFIVE_JHB100_INTC + bool "StarFive JHB100 External Interrupt Controller" depends on ARCH_STARFIVE || COMPILE_TEST default ARCH_STARFIVE select IRQ_DOMAIN_HIERARCHY help - This enables support for the INTC chip found in StarFive JH8100 + This enables support for the INTC chip found in StarFive JHB100 SoC. If you don't know what to do here, say Y. diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 26aa3b6ec99f..72cdcc9caa16 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -89,8 +89,9 @@ obj-$(CONFIG_MVEBU_PIC) += irq-mvebu-pic.o obj-$(CONFIG_MVEBU_SEI) += irq-mvebu-sei.o obj-$(CONFIG_LS_EXTIRQ) += irq-ls-extirq.o obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o +obj-$(CONFIG_ASPEED_AST2700_INTC) += irq-ast2700.o irq-ast2700-intc0.o irq-ast2700-intc1.o +obj-$(CONFIG_ASPEED_AST2700_INTC_TEST) += irq-ast2700-intc0-test.o obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o -obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-intc.o obj-$(CONFIG_STM32MP_EXTI) += irq-stm32mp-exti.o obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o @@ -108,7 +109,7 @@ obj-$(CONFIG_RISCV_APLIC_MSI) += irq-riscv-aplic-msi.o obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o obj-$(CONFIG_RISCV_RPMI_SYSMSI) += irq-riscv-rpmi-sysmsi.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o -obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o +obj-$(CONFIG_STARFIVE_JHB100_INTC) += irq-starfive-jhb100-intc.o obj-$(CONFIG_ACLINT_SSWI) += irq-aclint-sswi.o obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o @@ -119,7 +120,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o -obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o +obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o irq-loongarch-avec.o irq-loongarch-ir.o obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 03cafcc5c835..d9d408cb4711 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -24,8 +24,6 @@ #define IRQ_IN_COMBINER 8 -static DEFINE_RAW_SPINLOCK(irq_controller_lock); - struct combiner_chip_data { unsigned int hwirq_offset; unsigned int irq_mask; @@ -72,9 +70,7 @@ static void combiner_handle_cascade_irq(struct irq_desc *desc) chained_irq_enter(chip, desc); - raw_spin_lock(&irq_controller_lock); status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS); - raw_spin_unlock(&irq_controller_lock); status &= chip_data->irq_mask; if (status == 0) diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc.c deleted file mode 100644 index 4fb0dd8349da..000000000000 --- a/drivers/irqchip/irq-aspeed-intc.c +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Aspeed Interrupt Controller. - * - * Copyright (C) 2023 ASPEED Technology Inc. - */ - -#include <linux/bitops.h> -#include <linux/irq.h> -#include <linux/irqchip.h> -#include <linux/irqchip/chained_irq.h> -#include <linux/irqdomain.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/io.h> -#include <linux/spinlock.h> - -#define INTC_INT_ENABLE_REG 0x00 -#define INTC_INT_STATUS_REG 0x04 -#define INTC_IRQS_PER_WORD 32 - -struct aspeed_intc_ic { - void __iomem *base; - raw_spinlock_t gic_lock; - raw_spinlock_t intc_lock; - struct irq_domain *irq_domain; -}; - -static void aspeed_intc_ic_irq_handler(struct irq_desc *desc) -{ - struct aspeed_intc_ic *intc_ic = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); - - chained_irq_enter(chip, desc); - - scoped_guard(raw_spinlock, &intc_ic->gic_lock) { - unsigned long bit, status; - - status = readl(intc_ic->base + INTC_INT_STATUS_REG); - for_each_set_bit(bit, &status, INTC_IRQS_PER_WORD) { - generic_handle_domain_irq(intc_ic->irq_domain, bit); - writel(BIT(bit), intc_ic->base + INTC_INT_STATUS_REG); - } - } - - chained_irq_exit(chip, desc); -} - -static void aspeed_intc_irq_mask(struct irq_data *data) -{ - struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); - unsigned int mask = readl(intc_ic->base + INTC_INT_ENABLE_REG) & ~BIT(data->hwirq); - - guard(raw_spinlock)(&intc_ic->intc_lock); - writel(mask, intc_ic->base + INTC_INT_ENABLE_REG); -} - -static void aspeed_intc_irq_unmask(struct irq_data *data) -{ - struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); - unsigned int unmask = readl(intc_ic->base + INTC_INT_ENABLE_REG) | BIT(data->hwirq); - - guard(raw_spinlock)(&intc_ic->intc_lock); - writel(unmask, intc_ic->base + INTC_INT_ENABLE_REG); -} - -static struct irq_chip aspeed_intc_chip = { - .name = "ASPEED INTC", - .irq_mask = aspeed_intc_irq_mask, - .irq_unmask = aspeed_intc_irq_unmask, -}; - -static int aspeed_intc_ic_map_irq_domain(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &aspeed_intc_chip, handle_level_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops aspeed_intc_ic_irq_domain_ops = { - .map = aspeed_intc_ic_map_irq_domain, -}; - -static int __init aspeed_intc_ic_of_init(struct device_node *node, - struct device_node *parent) -{ - struct aspeed_intc_ic *intc_ic; - int irq, i, ret = 0; - - intc_ic = kzalloc_obj(*intc_ic); - if (!intc_ic) - return -ENOMEM; - - intc_ic->base = of_iomap(node, 0); - if (!intc_ic->base) { - pr_err("Failed to iomap intc_ic base\n"); - ret = -ENOMEM; - goto err_free_ic; - } - writel(0xffffffff, intc_ic->base + INTC_INT_STATUS_REG); - writel(0x0, intc_ic->base + INTC_INT_ENABLE_REG); - - intc_ic->irq_domain = irq_domain_create_linear(of_fwnode_handle(node), INTC_IRQS_PER_WORD, - &aspeed_intc_ic_irq_domain_ops, intc_ic); - if (!intc_ic->irq_domain) { - ret = -ENOMEM; - goto err_iounmap; - } - - raw_spin_lock_init(&intc_ic->gic_lock); - raw_spin_lock_init(&intc_ic->intc_lock); - - /* Check all the irq numbers valid. If not, unmaps all the base and frees the data. */ - for (i = 0; i < of_irq_count(node); i++) { - irq = irq_of_parse_and_map(node, i); - if (!irq) { - pr_err("Failed to get irq number\n"); - ret = -EINVAL; - goto err_iounmap; - } - } - - for (i = 0; i < of_irq_count(node); i++) { - irq = irq_of_parse_and_map(node, i); - irq_set_chained_handler_and_data(irq, aspeed_intc_ic_irq_handler, intc_ic); - } - - return 0; - -err_iounmap: - iounmap(intc_ic->base); -err_free_ic: - kfree(intc_ic); - return ret; -} - -IRQCHIP_DECLARE(ast2700_intc_ic, "aspeed,ast2700-intc-ic", aspeed_intc_ic_of_init); diff --git a/drivers/irqchip/irq-ast2700-intc0-test.c b/drivers/irqchip/irq-ast2700-intc0-test.c new file mode 100644 index 000000000000..d49784509ac7 --- /dev/null +++ b/drivers/irqchip/irq-ast2700-intc0-test.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Code Construct + */ +#include <kunit/test.h> + +#include "irq-ast2700.h" + +static void aspeed_intc0_resolve_route_bad_args(struct kunit *test) +{ + static const struct aspeed_intc_interrupt_range c1ranges[] = { 0 }; + static const u32 c1outs[] = { 0 }; + struct aspeed_intc_interrupt_range resolved; + const struct irq_domain c0domain = { 0 }; + int rc; + + rc = aspeed_intc0_resolve_route(NULL, 0, c1outs, 0, c1ranges, NULL); + KUNIT_EXPECT_EQ(test, rc, -EINVAL); + + rc = aspeed_intc0_resolve_route(&c0domain, 0, c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_EQ(test, rc, -ENOENT); + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + 0, c1ranges, &resolved); + KUNIT_EXPECT_EQ(test, rc, -ENOENT); +} + +static int gicv3_fwnode_read_string_array(const struct fwnode_handle *fwnode, + const char *propname, const char **val, size_t nval) +{ + if (!propname) + return -EINVAL; + + if (!val) + return 1; + + if (WARN_ON(nval != 1)) + return -EOVERFLOW; + + *val = "arm,gic-v3"; + return 1; +} + +static const struct fwnode_operations arm_gicv3_fwnode_ops = { + .property_read_string_array = gicv3_fwnode_read_string_array, +}; + +static void aspeed_intc_resolve_route_invalid_c0domain(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &arm_gicv3_fwnode_ops }, + }; + const struct irq_domain c0domain = { .fwnode = &intc0_node.fwnode }; + static const struct aspeed_intc_interrupt_range c1ranges[] = { 0 }; + static const u32 c1outs[] = { 0 }; + struct aspeed_intc_interrupt_range resolved; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_NE(test, rc, 0); +} + +static int +aspeed_intc0_fwnode_read_string_array(const struct fwnode_handle *fwnode_handle, + const char *propname, const char **val, + size_t nval) +{ + if (!propname) + return -EINVAL; + + if (!val) + return 1; + + if (WARN_ON(nval != 1)) + return -EOVERFLOW; + + *val = "aspeed,ast2700-intc0"; + return nval; +} + +static const struct fwnode_operations intc0_fwnode_ops = { + .property_read_string_array = aspeed_intc0_fwnode_read_string_array, +}; + +static void +aspeed_intc0_resolve_route_c1i1o1c0i1o1_connected(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] = { + { + .start = 0, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 1, + .param = { 128 } + } + } + }; + static const u32 c1outs[] = { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] = { + { + .start = 128, + .count = 1, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = { 0 }, + } + } + }; + struct aspeed_intc0 intc0 = { + .ranges = { .ranges = intc0_ranges, .nranges = ARRAY_SIZE(intc0_ranges), } + }; + const struct irq_domain c0domain = { + .host_data = &intc0, + .fwnode = &intc0_node.fwnode + }; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_EQ(test, rc, 0); + KUNIT_EXPECT_EQ(test, resolved.start, 0); + KUNIT_EXPECT_EQ(test, resolved.count, 1); + KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 128); +} + +static void +aspeed_intc0_resolve_route_c1i1o1c0i1o1_disconnected(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] = { + { + .start = 0, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 1, + .param = { 128 } + } + } + }; + static const u32 c1outs[] = { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] = { + { + .start = 129, + .count = 1, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = { 0 }, + } + } + }; + struct aspeed_intc0 intc0 = { + .ranges = { + .ranges = intc0_ranges, + .nranges = ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain = { + .host_data = &intc0, + .fwnode = &intc0_node.fwnode + }; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_NE(test, rc, 0); +} + +static void aspeed_intc0_resolve_route_c1i1o1mc0i1o1(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] = { + { + .start = 0, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 1, + .param = { 480 } + } + } + }; + static const u32 c1outs[] = { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] = { + { + .start = 192, + .count = 1, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = { 0 }, + } + } + }; + struct aspeed_intc0 intc0 = { + .ranges = { + .ranges = intc0_ranges, + .nranges = ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain = { + .host_data = &intc0, + .fwnode = &intc0_node.fwnode + }; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_EQ(test, rc, 0); + KUNIT_EXPECT_EQ(test, resolved.start, 0); + KUNIT_EXPECT_EQ(test, resolved.count, 1); + KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 480); +} + +static void aspeed_intc0_resolve_route_c1i2o2mc0i1o1(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] = { + { + .start = 0, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 1, + .param = { 480 } + } + }, + { + .start = 1, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 1, + .param = { 510 } + } + } + }; + static const u32 c1outs[] = { 1 }; + struct aspeed_intc_interrupt_range resolved; + static struct aspeed_intc_interrupt_range intc0_ranges[] = { + { + .start = 208, + .count = 1, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = { 0 }, + } + } + }; + struct aspeed_intc0 intc0 = { + .ranges = { + .ranges = intc0_ranges, + .nranges = ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain = { + .host_data = &intc0, + .fwnode = &intc0_node.fwnode + }; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_EQ(test, rc, 0); + KUNIT_EXPECT_EQ(test, resolved.start, 1); + KUNIT_EXPECT_EQ(test, resolved.count, 1); + KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 510); +} + +static void aspeed_intc0_resolve_route_c1i1o1mc0i2o1(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] = { + { + .start = 0, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 1, + .param = { 510 } + } + }, + }; + static const u32 c1outs[] = { 0 }; + struct aspeed_intc_interrupt_range resolved; + static struct aspeed_intc_interrupt_range intc0_ranges[] = { + { + .start = 192, + .count = 1, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = {0}, + } + }, + { + .start = 208, + .count = 1, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = {0}, + } + } + }; + struct aspeed_intc0 intc0 = { + .ranges = { + .ranges = intc0_ranges, + .nranges = ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain = { + .host_data = &intc0, + .fwnode = &intc0_node.fwnode + }; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_EQ(test, rc, 0); + KUNIT_EXPECT_EQ(test, resolved.start, 0); + KUNIT_EXPECT_EQ(test, resolved.count, 1); + KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 510); +} + +static void aspeed_intc0_resolve_route_c1i1o2mc0i1o1_invalid(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] = { + { + .start = 0, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 1, + .param = { 480 } + } + } + }; + static const u32 c1outs[] = { + AST2700_INTC_INVALID_ROUTE, 0 + }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] = { + { + .start = 192, + .count = 1, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = { 0 }, + } + } + }; + struct aspeed_intc0 intc0 = { + .ranges = { + .ranges = intc0_ranges, + .nranges = ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain = { + .host_data = &intc0, + .fwnode = &intc0_node.fwnode + }; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_EQ(test, rc, 1); + KUNIT_EXPECT_EQ(test, resolved.start, 0); + KUNIT_EXPECT_EQ(test, resolved.count, 1); + KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 480); +} + +static void +aspeed_intc0_resolve_route_c1i1o1mc0i1o1_bad_range_upstream(struct kunit *test) +{ + struct device_node intc0_node = { + .fwnode = { .ops = &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] = { + { + .start = 0, + .count = 1, + .upstream = { + .fwnode = &intc0_node.fwnode, + .param_count = 0, + .param = { 0 } + } + } + }; + static const u32 c1outs[] = { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] = { + { + .start = 0, + .count = 0, + .upstream = { + .fwnode = NULL, + .param_count = 0, + .param = { 0 }, + } + } + }; + struct aspeed_intc0 intc0 = { + .ranges = { + .ranges = intc0_ranges, + .nranges = ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain = { + .host_data = &intc0, + .fwnode = &intc0_node.fwnode + }; + int rc; + + rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_NE(test, rc, 0); +} + +static struct kunit_case ast2700_intc0_test_cases[] = { + KUNIT_CASE(aspeed_intc0_resolve_route_bad_args), + KUNIT_CASE(aspeed_intc_resolve_route_invalid_c0domain), + KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1c0i1o1_connected), + KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1c0i1o1_disconnected), + KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1mc0i1o1), + KUNIT_CASE(aspeed_intc0_resolve_route_c1i2o2mc0i1o1), + KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1mc0i2o1), + KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o2mc0i1o1_invalid), + KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1mc0i1o1_bad_range_upstream), + {}, +}; + +static struct kunit_suite ast2700_intc0_test_suite = { + .name = "ast2700-intc0", + .test_cases = ast2700_intc0_test_cases, +}; + +kunit_test_suite(ast2700_intc0_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-ast2700-intc0.c b/drivers/irqchip/irq-ast2700-intc0.c new file mode 100644 index 000000000000..14b8b88f1179 --- /dev/null +++ b/drivers/irqchip/irq-ast2700-intc0.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Aspeed AST2700 Interrupt Controller. + * + * Copyright (C) 2026 ASPEED Technology Inc. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fwnode.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/kconfig.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/overflow.h> +#include <linux/property.h> +#include <linux/spinlock.h> + +#include "irq-ast2700.h" + +#define INT_NUM 480 +#define INTM_NUM 50 +#define SWINT_NUM 16 + +#define INTM_BASE (INT_NUM) +#define SWINT_BASE (INT_NUM + INTM_NUM) +#define INT0_NUM (INT_NUM + INTM_NUM + SWINT_NUM) + +#define INTC0_IN_NUM 480 +#define INTC0_ROUTE_NUM 5 +#define INTC0_INTM_NUM 50 +#define INTC0_ROUTE_BITS 3 + +#define GIC_P2P_SPI_END 128 +#define INTC0_SWINT_OUT_BASE 144 + +#define INTC0_SWINT_IER 0x10 +#define INTC0_SWINT_ISR 0x14 +#define INTC0_INTBANKX_IER 0x1000 +#define INTC0_INTBANK_SIZE 0x100 +#define INTC0_INTBANK_GROUPS 11 +#define INTC0_INTBANKS_PER_GRP 3 +#define INTC0_INTMX_IER 0x1b00 +#define INTC0_INTMX_ISR 0x1b04 +#define INTC0_INTMX_BANK_SIZE 0x10 +#define INTC0_INTM_BANK_NUM 3 +#define INTC0_IRQS_PER_BANK 32 +#define INTM_IRQS_PER_BANK 10 +#define INTC0_SEL_BASE 0x200 +#define INTC0_SEL_BANK_SIZE 0x4 +#define INTC0_SEL_ROUTE_SIZE 0x100 + +static void aspeed_swint_irq_mask(struct irq_data *data) +{ + struct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data); + int bit = data->hwirq - SWINT_BASE; + u32 ier; + + guard(raw_spinlock)(&intc0->intc_lock); + ier = readl(intc0->base + INTC0_SWINT_IER) & ~BIT(bit); + writel(ier, intc0->base + INTC0_SWINT_IER); + irq_chip_mask_parent(data); +} + +static void aspeed_swint_irq_unmask(struct irq_data *data) +{ + struct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data); + int bit = data->hwirq - SWINT_BASE; + u32 ier; + + guard(raw_spinlock)(&intc0->intc_lock); + ier = readl(intc0->base + INTC0_SWINT_IER) | BIT(bit); + writel(ier, intc0->base + INTC0_SWINT_IER); + irq_chip_unmask_parent(data); +} + +static void aspeed_swint_irq_eoi(struct irq_data *data) +{ + struct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data); + int bit = data->hwirq - SWINT_BASE; + + writel(BIT(bit), intc0->base + INTC0_SWINT_ISR); + irq_chip_eoi_parent(data); +} + +static struct irq_chip aspeed_swint_chip = { + .name = "ast2700-swint", + .irq_eoi = aspeed_swint_irq_eoi, + .irq_mask = aspeed_swint_irq_mask, + .irq_unmask = aspeed_swint_irq_unmask, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static void aspeed_intc0_irq_mask(struct irq_data *data) +{ + struct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data); + int bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK; + int bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK; + u32 ier; + + guard(raw_spinlock)(&intc0->intc_lock); + ier = readl(intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE) & ~BIT(bit); + writel(ier, intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE); + irq_chip_mask_parent(data); +} + +static void aspeed_intc0_irq_unmask(struct irq_data *data) +{ + struct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data); + int bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK; + int bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK; + u32 ier; + + guard(raw_spinlock)(&intc0->intc_lock); + ier = readl(intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE) | BIT(bit); + writel(ier, intc0->base + INTC0_INTMX_IER + bank * INTC0_INTMX_BANK_SIZE); + irq_chip_unmask_parent(data); +} + +static void aspeed_intc0_irq_eoi(struct irq_data *data) +{ + struct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data); + int bank = (data->hwirq - INTM_BASE) / INTM_IRQS_PER_BANK; + int bit = (data->hwirq - INTM_BASE) % INTM_IRQS_PER_BANK; + + writel(BIT(bit), intc0->base + INTC0_INTMX_ISR + bank * INTC0_INTMX_BANK_SIZE); + irq_chip_eoi_parent(data); +} + +static struct irq_chip aspeed_intm_chip = { + .name = "ast2700-intmerge", + .irq_eoi = aspeed_intc0_irq_eoi, + .irq_mask = aspeed_intc0_irq_mask, + .irq_unmask = aspeed_intc0_irq_unmask, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static struct irq_chip linear_intr_irq_chip = { + .name = "ast2700-int", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static const u32 aspeed_intc0_routes[INTC0_IN_NUM / INTC0_IRQS_PER_BANK][INTC0_ROUTE_NUM] = { + { 0, 256, 426, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE }, + { 32, 288, 458, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE }, + { 64, 320, 490, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE }, + { 96, 352, 522, AST2700_INTC_INVALID_ROUTE, AST2700_INTC_INVALID_ROUTE }, + { 128, 384, 554, 160, 176 }, + { 129, 385, 555, 161, 177 }, + { 130, 386, 556, 162, 178 }, + { 131, 387, 557, 163, 179 }, + { 132, 388, 558, 164, 180 }, + { 133, 544, 714, 165, 181 }, + { 134, 545, 715, 166, 182 }, + { 135, 546, 706, 167, 183 }, + { 136, 547, 707, 168, 184 }, + { 137, 548, 708, 169, 185 }, + { 138, 549, 709, 170, 186 }, +}; + +static const u32 aspeed_intc0_intm_routes[INTC0_INTM_NUM / INTM_IRQS_PER_BANK] = { + 192, 416, 586, 208, 224 +}; + +static int resolve_input_from_child_ranges(const struct aspeed_intc0 *intc0, + const struct aspeed_intc_interrupt_range *range, + u32 outpin, u32 *input) +{ + u32 offset, base; + + if (!in_range32(outpin, range->start, range->count)) + return -ENOENT; + + if (range->upstream.param_count == 0) + return -EINVAL; + + base = range->upstream.param[ASPEED_INTC_RANGES_BASE]; + offset = outpin - range->start; + if (check_add_overflow(base, offset, input)) { + dev_warn(intc0->dev, "%s: Arithmetic overflow for input derivation: %u + %u\n", + __func__, base, offset); + return -EINVAL; + } + return 0; +} + +static int resolve_parent_range_for_output(const struct aspeed_intc0 *intc0, + const struct fwnode_handle *parent, u32 output, + struct aspeed_intc_interrupt_range *resolved) +{ + for (size_t i = 0; i < intc0->ranges.nranges; i++) { + struct aspeed_intc_interrupt_range range = intc0->ranges.ranges[i]; + + if (!in_range32(output, range.start, range.count)) + continue; + + if (range.upstream.fwnode != parent) + continue; + + if (resolved) { + resolved->start = output; + resolved->count = 1; + resolved->upstream = range.upstream; + resolved->upstream.param[ASPEED_INTC_RANGES_COUNT] += + output - range.start; + } + + return 0; + } + + return -ENOENT; +} + +static int resolve_parent_route_for_input(const struct aspeed_intc0 *intc0, + const struct fwnode_handle *parent, u32 input, + struct aspeed_intc_interrupt_range *resolved) +{ + int rc = -ENOENT; + u32 c0o; + + if (input < INT_NUM) { + static_assert(INTC0_ROUTE_NUM < INT_MAX, "Broken cast"); + for (size_t i = 0; rc == -ENOENT && i < INTC0_ROUTE_NUM; i++) { + c0o = aspeed_intc0_routes[input / INTC0_IRQS_PER_BANK][i]; + if (c0o == AST2700_INTC_INVALID_ROUTE) + continue; + + if (input < GIC_P2P_SPI_END) + c0o += input % INTC0_IRQS_PER_BANK; + + rc = resolve_parent_range_for_output(intc0, parent, c0o, resolved); + if (!rc) + return (int)i; + } + } else if (input < (INT_NUM + INTM_NUM)) { + c0o = aspeed_intc0_intm_routes[(input - INT_NUM) / INTM_IRQS_PER_BANK]; + c0o += ((input - INT_NUM) % INTM_IRQS_PER_BANK); + return resolve_parent_range_for_output(intc0, parent, c0o, resolved); + } else if (input < (INT_NUM + INTM_NUM + SWINT_NUM)) { + c0o = input - SWINT_BASE + INTC0_SWINT_OUT_BASE; + return resolve_parent_range_for_output(intc0, parent, c0o, resolved); + } else { + return -ENOENT; + } + + return rc; +} + +/** + * aspeed_intc0_resolve_route - Determine the necessary interrupt output at intc1 + * @c0domain: The pointer to intc0's irq_domain + * @nc1outs: The number of valid intc1 outputs available for the input + * @c1outs: The array of available intc1 output indices for the input + * @nc1ranges: The number of interrupt range entries for intc1 + * @c1ranges: The array of configured intc1 interrupt ranges + * @resolved: The fully resolved range entry after applying the resolution + * algorithm + * + * Returns: The intc1 route index associated with the intc1 output identified in + * @resolved on success. Otherwise, a negative errno value. + * + * The AST2700 interrupt architecture allows any peripheral interrupt source + * to be routed to one of up to four processors running in the SoC. A processor + * binding a driver for a peripheral that requests an interrupt is (without + * further design and effort) the destination for the requested interrupt. + * + * Routing a peripheral interrupt to its destination processor requires + * coordination between INTC0 on the CPU die and one or more INTC1 instances. + * At least one INTC1 instance exists in the SoC on the IO-die, however up + * to two more instances may be integrated via LTPI (LVDS Tunneling Protocol + * & Interface). + * + * Between the multiple destinations, various route constraints, and the + * devicetree binding design, some information that's needed at INTC1 instances + * to route inbound interrupts correctly to the destination processor is only + * available at INTC0. + * + * aspeed_intc0_resolve_route() is to be invoked by INTC1 driver instances to + * perform the route resolution. The implementation in INTC0 allows INTC0 to + * encapsulate the information used to perform route selection, and provides it + * with an opportunity to apply policy as part of the selection process. Such + * policy may, for instance, choose to de-prioritise some interrupts destined + * for the PSP (Primary Service Processor) GIC. + */ +int aspeed_intc0_resolve_route(const struct irq_domain *c0domain, size_t nc1outs, + const u32 *c1outs, size_t nc1ranges, + const struct aspeed_intc_interrupt_range *c1ranges, + struct aspeed_intc_interrupt_range *resolved) +{ + struct fwnode_handle *parent_fwnode; + struct aspeed_intc0 *intc0; + int ret; + + if (!c0domain || !resolved) + return -EINVAL; + + if (nc1outs > INT_MAX) + return -EINVAL; + + if (nc1outs == 0 || nc1ranges == 0) + return -ENOENT; + + if (!IS_ENABLED(CONFIG_ASPEED_AST2700_INTC_TEST) && + !fwnode_device_is_compatible(c0domain->fwnode, "aspeed,ast2700-intc0")) + return -ENODEV; + + intc0 = c0domain->host_data; + if (!intc0) + return -EINVAL; + + parent_fwnode = of_fwnode_handle(intc0->parent); + + for (size_t i = 0; i < nc1outs; i++) { + u32 c1o = c1outs[i]; + + if (c1o == AST2700_INTC_INVALID_ROUTE) + continue; + + for (size_t j = 0; j < nc1ranges; j++) { + struct aspeed_intc_interrupt_range c1r = c1ranges[j]; + u32 input; + + /* + * Range match for intc1 output pin + * + * Assume a failed match is still a match for the purpose of testing, + * saves a bunch of mess in the test fixtures + */ + if (!(c0domain == c1r.domain || + IS_ENABLED(CONFIG_ASPEED_AST2700_INTC_TEST))) + continue; + + ret = resolve_input_from_child_ranges(intc0, &c1r, c1o, &input); + if (ret) + continue; + + /* + * INTC1 should never request routes for peripheral interrupt sources + * directly attached to INTC0. + */ + if (input < GIC_P2P_SPI_END) + continue; + + ret = resolve_parent_route_for_input(intc0, parent_fwnode, input, NULL); + if (ret < 0) + continue; + + /* Route resolution succeeded */ + resolved->start = c1o; + resolved->count = 1; + resolved->upstream = c1r.upstream; + resolved->upstream.param[ASPEED_INTC_RANGES_BASE] = input; + /* Cast protected by prior test against nc1outs */ + return (int)i; + } + } + + return -ENOENT; +} + +static int aspeed_intc0_irq_domain_map(struct irq_domain *domain, + unsigned int irq, irq_hw_number_t hwirq) +{ + if (hwirq < GIC_P2P_SPI_END) + irq_set_chip_and_handler(irq, &linear_intr_irq_chip, handle_level_irq); + else if (hwirq < INTM_BASE) + return -EINVAL; + else if (hwirq < SWINT_BASE) + irq_set_chip_and_handler(irq, &aspeed_intm_chip, handle_level_irq); + else if (hwirq < INT0_NUM) + irq_set_chip_and_handler(irq, &aspeed_swint_chip, handle_level_irq); + else + return -EINVAL; + + irq_set_chip_data(irq, domain->host_data); + return 0; +} + +static int aspeed_intc0_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (fwspec->param_count != 1) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = IRQ_TYPE_NONE; + return 0; +} + +static int aspeed_intc0_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct aspeed_intc0 *intc0 = domain->host_data; + struct aspeed_intc_interrupt_range resolved; + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + struct irq_chip *chip; + unsigned long hwirq; + unsigned int type; + int ret; + + ret = aspeed_intc0_irq_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + if (hwirq >= GIC_P2P_SPI_END && hwirq < INT_NUM) + return -EINVAL; + + if (hwirq < INTM_BASE) + chip = &linear_intr_irq_chip; + else if (hwirq < SWINT_BASE) + chip = &aspeed_intm_chip; + else + chip = &aspeed_swint_chip; + + ret = resolve_parent_route_for_input(intc0, domain->parent->fwnode, + (u32)hwirq, &resolved); + if (ret) + return ret; + + parent_fwspec = resolved.upstream; + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); + if (ret) + return ret; + + for (int i = 0; i < nr_irqs; ++i, ++hwirq, ++virq) { + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, + domain->host_data); + if (ret) + return ret; + } + + return 0; +} + +static int aspeed_intc0_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + struct aspeed_intc0 *intc0 = irq_data_get_irq_chip_data(data); + unsigned long hwirq = data->hwirq; + int route, bank, bit; + u32 mask; + + if (hwirq >= INT0_NUM) + return -EINVAL; + + if (in_range32(hwirq, INTM_BASE, INTM_NUM + SWINT_NUM)) + return 0; + + bank = hwirq / INTC0_IRQS_PER_BANK; + bit = hwirq % INTC0_IRQS_PER_BANK; + mask = BIT(bit); + + route = resolve_parent_route_for_input(intc0, intc0->local->parent->fwnode, + hwirq, NULL); + if (route < 0) + return route; + + guard(raw_spinlock)(&intc0->intc_lock); + for (int i = 0; i < INTC0_ROUTE_BITS; i++) { + void __iomem *sel = intc0->base + INTC0_SEL_BASE + + (bank * INTC0_SEL_BANK_SIZE) + + (INTC0_SEL_ROUTE_SIZE * i); + u32 reg = readl(sel); + + if (route & BIT(i)) + reg |= mask; + else + reg &= ~mask; + + writel(reg, sel); + if (readl(sel) != reg) + return -EACCES; + } + + return 0; +} + +static const struct irq_domain_ops aspeed_intc0_irq_domain_ops = { + .translate = aspeed_intc0_irq_domain_translate, + .activate = aspeed_intc0_irq_domain_activate, + .alloc = aspeed_intc0_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .map = aspeed_intc0_irq_domain_map, +}; + +static void aspeed_intc0_disable_swint(struct aspeed_intc0 *intc0) +{ + writel(0, intc0->base + INTC0_SWINT_IER); +} + +static void aspeed_intc0_disable_intbank(struct aspeed_intc0 *intc0) +{ + for (int i = 0; i < INTC0_INTBANK_GROUPS; i++) { + for (int j = 0; j < INTC0_INTBANKS_PER_GRP; j++) { + u32 base = INTC0_INTBANKX_IER + + (INTC0_INTBANK_SIZE * i) + + (INTC0_INTMX_BANK_SIZE * j); + + writel(0, intc0->base + base); + } + } +} + +static void aspeed_intc0_disable_intm(struct aspeed_intc0 *intc0) +{ + for (int i = 0; i < INTC0_INTM_BANK_NUM; i++) + writel(0, intc0->base + INTC0_INTMX_IER + (INTC0_INTMX_BANK_SIZE * i)); +} + +static int aspeed_intc0_probe(struct platform_device *pdev, + struct device_node *parent) +{ + struct device_node *node = pdev->dev.of_node; + struct irq_domain *parent_domain; + struct aspeed_intc0 *intc0; + int ret; + + if (!parent) { + pr_err("missing parent interrupt node\n"); + return -ENODEV; + } + + intc0 = devm_kzalloc(&pdev->dev, sizeof(*intc0), GFP_KERNEL); + if (!intc0) + return -ENOMEM; + + intc0->dev = &pdev->dev; + intc0->parent = parent; + intc0->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(intc0->base)) + return PTR_ERR(intc0->base); + + aspeed_intc0_disable_swint(intc0); + aspeed_intc0_disable_intbank(intc0); + aspeed_intc0_disable_intm(intc0); + + raw_spin_lock_init(&intc0->intc_lock); + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("unable to obtain parent domain\n"); + return -ENODEV; + } + + if (!of_device_is_compatible(parent, "arm,gic-v3")) + return -ENODEV; + + intc0->local = irq_domain_create_hierarchy(parent_domain, 0, INT0_NUM, + of_fwnode_handle(node), + &aspeed_intc0_irq_domain_ops, + intc0); + if (!intc0->local) + return -ENOMEM; + + ret = aspeed_intc_populate_ranges(&pdev->dev, &intc0->ranges); + if (ret < 0) { + irq_domain_remove(intc0->local); + return ret; + } + + return 0; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(ast2700_intc0) +IRQCHIP_MATCH("aspeed,ast2700-intc0", aspeed_intc0_probe) +IRQCHIP_PLATFORM_DRIVER_END(ast2700_intc0) diff --git a/drivers/irqchip/irq-ast2700-intc1.c b/drivers/irqchip/irq-ast2700-intc1.c new file mode 100644 index 000000000000..59e8f0d5ddcd --- /dev/null +++ b/drivers/irqchip/irq-ast2700-intc1.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Aspeed AST2700 Interrupt Controller. + * + * Copyright (C) 2026 ASPEED Technology Inc. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/spinlock.h> + +#include "irq-ast2700.h" + +#define INTC1_IER 0x100 +#define INTC1_ISR 0x104 +#define INTC1_BANK_SIZE 0x10 +#define INTC1_SEL_BASE 0x80 +#define INTC1_SEL_BANK_SIZE 0x4 +#define INTC1_SEL_ROUTE_SIZE 0x20 +#define INTC1_IRQS_PER_BANK 32 +#define INTC1_BANK_NUM 6 +#define INTC1_ROUTE_NUM 7 +#define INTC1_IN_NUM 192 +#define INTC1_BOOTMCU_ROUTE 6 +#define INTC1_ROUTE_SELECTOR_BITS 3 +#define INTC1_ROUTE_IRQS_PER_GROUP 32 +#define INTC1_ROUTE_SHIFT 5 + +struct aspeed_intc1 { + struct device *dev; + void __iomem *base; + raw_spinlock_t intc_lock; + struct irq_domain *local; + struct irq_domain *upstream; + struct aspeed_intc_interrupt_ranges ranges; +}; + +static void aspeed_intc1_disable_int(struct aspeed_intc1 *intc1) +{ + for (int i = 0; i < INTC1_BANK_NUM; i++) + writel(0, intc1->base + INTC1_IER + (INTC1_BANK_SIZE * i)); +} + +static void aspeed_intc1_irq_handler(struct irq_desc *desc) +{ + struct aspeed_intc1 *intc1 = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long bit, status; + + chained_irq_enter(chip, desc); + + for (int bank = 0; bank < INTC1_BANK_NUM; bank++) { + status = readl(intc1->base + INTC1_ISR + (INTC1_BANK_SIZE * bank)); + if (!status) + continue; + + for_each_set_bit(bit, &status, INTC1_IRQS_PER_BANK) { + generic_handle_domain_irq(intc1->local, (bank * INTC1_IRQS_PER_BANK) + bit); + writel(BIT(bit), intc1->base + INTC1_ISR + (INTC1_BANK_SIZE * bank)); + } + } + + chained_irq_exit(chip, desc); +} + +static void aspeed_intc1_irq_mask(struct irq_data *data) +{ + struct aspeed_intc1 *intc1 = irq_data_get_irq_chip_data(data); + int bank = data->hwirq / INTC1_IRQS_PER_BANK; + int bit = data->hwirq % INTC1_IRQS_PER_BANK; + u32 ier; + + guard(raw_spinlock)(&intc1->intc_lock); + ier = readl(intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank)) & ~BIT(bit); + writel(ier, intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank)); +} + +static void aspeed_intc1_irq_unmask(struct irq_data *data) +{ + struct aspeed_intc1 *intc1 = irq_data_get_irq_chip_data(data); + int bank = data->hwirq / INTC1_IRQS_PER_BANK; + int bit = data->hwirq % INTC1_IRQS_PER_BANK; + u32 ier; + + guard(raw_spinlock)(&intc1->intc_lock); + ier = readl(intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank)) | BIT(bit); + writel(ier, intc1->base + INTC1_IER + (INTC1_BANK_SIZE * bank)); +} + +static struct irq_chip aspeed_intc_chip = { + .name = "ASPEED INTC1", + .irq_mask = aspeed_intc1_irq_mask, + .irq_unmask = aspeed_intc1_irq_unmask, +}; + +static int aspeed_intc1_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (fwspec->param_count != 1) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = IRQ_TYPE_LEVEL_HIGH; + return 0; +} + +static int aspeed_intc1_map_irq_domain(struct irq_domain *domain, + unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_domain_set_info(domain, irq, hwirq, &aspeed_intc_chip, + domain->host_data, handle_level_irq, NULL, NULL); + return 0; +} + +/* + * In-bound interrupts are progressively merged into one out-bound interrupt in + * groups of 32. Apply this fact to compress the route table in corresponding + * groups of 32. + */ +static const u32 +aspeed_intc1_routes[INTC1_IN_NUM / INTC1_ROUTE_IRQS_PER_GROUP][INTC1_ROUTE_NUM] = { + { 0, AST2700_INTC_INVALID_ROUTE, 10, 20, 30, 40, 50 }, + { 1, AST2700_INTC_INVALID_ROUTE, 11, 21, 31, 41, 50 }, + { 2, AST2700_INTC_INVALID_ROUTE, 12, 22, 32, 42, 50 }, + { 3, AST2700_INTC_INVALID_ROUTE, 13, 23, 33, 43, 50 }, + { 4, AST2700_INTC_INVALID_ROUTE, 14, 24, 34, 44, 50 }, + { 5, AST2700_INTC_INVALID_ROUTE, 15, 25, 35, 45, 50 }, +}; + +static int aspeed_intc1_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + struct aspeed_intc1 *intc1 = irq_data_get_irq_chip_data(data); + struct aspeed_intc_interrupt_range resolved; + int rc, bank, bit; + u32 mask; + + if (WARN_ON_ONCE((data->hwirq >> INTC1_ROUTE_SHIFT) >= ARRAY_SIZE(aspeed_intc1_routes))) + return -EINVAL; + + /* + * outpin may be an error if the upstream is the BootMCU APLIC node, or + * anything except a valid intc0 driver instance + */ + rc = aspeed_intc0_resolve_route(intc1->upstream, INTC1_ROUTE_NUM, + aspeed_intc1_routes[data->hwirq >> INTC1_ROUTE_SHIFT], + intc1->ranges.nranges, + intc1->ranges.ranges, &resolved); + if (rc < 0) { + if (!fwnode_device_is_compatible(intc1->upstream->fwnode, "riscv,aplic")) { + dev_warn(intc1->dev, + "Failed to resolve interrupt route for hwirq %lu in domain %s\n", + data->hwirq, domain->name); + return rc; + } + rc = INTC1_BOOTMCU_ROUTE; + } + + bank = data->hwirq / INTC1_IRQS_PER_BANK; + bit = data->hwirq % INTC1_IRQS_PER_BANK; + mask = BIT(bit); + + guard(raw_spinlock)(&intc1->intc_lock); + for (int i = 0; i < INTC1_ROUTE_SELECTOR_BITS; i++) { + void __iomem *sel = intc1->base + INTC1_SEL_BASE + + (bank * INTC1_SEL_BANK_SIZE) + + (INTC1_SEL_ROUTE_SIZE * i); + u32 reg = readl(sel); + + if (rc & BIT(i)) + reg |= mask; + else + reg &= ~mask; + + writel(reg, sel); + if (readl(sel) != reg) + return -EACCES; + } + + return 0; +} + +static const struct irq_domain_ops aspeed_intc1_irq_domain_ops = { + .map = aspeed_intc1_map_irq_domain, + .translate = aspeed_intc1_irq_domain_translate, + .activate = aspeed_intc1_irq_domain_activate, +}; + +static void aspeed_intc1_request_interrupts(struct aspeed_intc1 *intc1) +{ + for (unsigned int i = 0; i < intc1->ranges.nranges; i++) { + struct aspeed_intc_interrupt_range *r = + &intc1->ranges.ranges[i]; + + if (intc1->upstream != r->domain) + continue; + + for (u32 k = 0; k < r->count; k++) { + struct of_phandle_args parent_irq; + int irq; + + parent_irq.np = to_of_node(r->upstream.fwnode); + parent_irq.args_count = 1; + parent_irq.args[0] = + intc1->ranges.ranges[i].upstream.param[ASPEED_INTC_RANGES_BASE] + k; + + irq = irq_create_of_mapping(&parent_irq); + if (!irq) + continue; + + irq_set_chained_handler_and_data(irq, + aspeed_intc1_irq_handler, intc1); + } + } +} + +static int aspeed_intc1_probe(struct platform_device *pdev, + struct device_node *parent) +{ + struct device_node *node = pdev->dev.of_node; + struct aspeed_intc1 *intc1; + struct irq_domain *host; + int ret; + + if (!parent) { + dev_err(&pdev->dev, "missing parent interrupt node\n"); + return -ENODEV; + } + + if (!of_device_is_compatible(parent, "aspeed,ast2700-intc0")) + return -ENODEV; + + host = irq_find_host(parent); + if (!host) + return -ENODEV; + + intc1 = devm_kzalloc(&pdev->dev, sizeof(*intc1), GFP_KERNEL); + if (!intc1) + return -ENOMEM; + + intc1->dev = &pdev->dev; + intc1->upstream = host; + intc1->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(intc1->base)) + return PTR_ERR(intc1->base); + + aspeed_intc1_disable_int(intc1); + + raw_spin_lock_init(&intc1->intc_lock); + + intc1->local = irq_domain_create_linear(of_fwnode_handle(node), + INTC1_BANK_NUM * INTC1_IRQS_PER_BANK, + &aspeed_intc1_irq_domain_ops, intc1); + if (!intc1->local) + return -ENOMEM; + + ret = aspeed_intc_populate_ranges(&pdev->dev, &intc1->ranges); + if (ret < 0) { + irq_domain_remove(intc1->local); + return ret; + } + + aspeed_intc1_request_interrupts(intc1); + + return 0; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(ast2700_intc1) +IRQCHIP_MATCH("aspeed,ast2700-intc1", aspeed_intc1_probe) +IRQCHIP_PLATFORM_DRIVER_END(ast2700_intc1) diff --git a/drivers/irqchip/irq-ast2700.c b/drivers/irqchip/irq-ast2700.c new file mode 100644 index 000000000000..1e4c4a624dbf --- /dev/null +++ b/drivers/irqchip/irq-ast2700.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Aspeed AST2700 Interrupt Controller. + * + * Copyright (C) 2026 ASPEED Technology Inc. + */ +#include "irq-ast2700.h" + +#define ASPEED_INTC_RANGE_FIXED_CELLS 3U +#define ASPEED_INTC_RANGE_OFF_START 0U +#define ASPEED_INTC_RANGE_OFF_COUNT 1U +#define ASPEED_INTC_RANGE_OFF_PHANDLE 2U + +/** + * aspeed_intc_populate_ranges + * @dev: Device owning the interrupt controller node. + * @ranges: Destination for parsed range descriptors. + * + * Return: 0 on success, negative errno on error. + */ +int aspeed_intc_populate_ranges(struct device *dev, + struct aspeed_intc_interrupt_ranges *ranges) +{ + struct aspeed_intc_interrupt_range *arr; + const __be32 *pvs, *pve; + struct device_node *dn; + int len; + + if (!dev || !ranges) + return -EINVAL; + + dn = dev->of_node; + + pvs = of_get_property(dn, "aspeed,interrupt-ranges", &len); + if (!pvs) + return -EINVAL; + + if (len % sizeof(__be32)) + return -EINVAL; + + /* Over-estimate the range entry count for now */ + ranges->ranges = devm_kmalloc_array(dev, + len / (ASPEED_INTC_RANGE_FIXED_CELLS * sizeof(__be32)), + sizeof(*ranges->ranges), + GFP_KERNEL); + if (!ranges->ranges) + return -ENOMEM; + + pve = pvs + (len / sizeof(__be32)); + for (unsigned int i = 0; pve - pvs >= ASPEED_INTC_RANGE_FIXED_CELLS; i++) { + struct aspeed_intc_interrupt_range *r; + struct device_node *target; + u32 target_cells; + + target = of_find_node_by_phandle(be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_PHANDLE])); + if (!target) + return -EINVAL; + + if (of_property_read_u32(target, "#interrupt-cells", + &target_cells)) { + of_node_put(target); + return -EINVAL; + } + + if (!target_cells || target_cells > IRQ_DOMAIN_IRQ_SPEC_PARAMS) { + of_node_put(target); + return -EINVAL; + } + + if (pve - pvs < ASPEED_INTC_RANGE_FIXED_CELLS + target_cells) { + of_node_put(target); + return -EINVAL; + } + + r = &ranges->ranges[i]; + r->start = be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_START]); + r->count = be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_COUNT]); + + { + struct of_phandle_args args = { + .np = target, + .args_count = target_cells, + }; + + for (u32 j = 0; j < target_cells; j++) + args.args[j] = be32_to_cpu(pvs[ASPEED_INTC_RANGE_FIXED_CELLS + j]); + + of_phandle_args_to_fwspec(target, args.args, + args.args_count, + &r->upstream); + } + + of_node_put(target); + r->domain = irq_find_matching_fwspec(&r->upstream, DOMAIN_BUS_ANY); + pvs += ASPEED_INTC_RANGE_FIXED_CELLS + target_cells; + ranges->nranges++; + } + + /* Re-fit the range array now we know the entry count */ + arr = devm_krealloc_array(dev, ranges->ranges, ranges->nranges, + sizeof(*ranges->ranges), GFP_KERNEL); + if (!arr) + return -ENOMEM; + ranges->ranges = arr; + + return 0; +} diff --git a/drivers/irqchip/irq-ast2700.h b/drivers/irqchip/irq-ast2700.h new file mode 100644 index 000000000000..318296638445 --- /dev/null +++ b/drivers/irqchip/irq-ast2700.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Aspeed AST2700 Interrupt Controller. + * + * Copyright (C) 2026 ASPEED Technology Inc. + */ +#ifndef DRIVERS_IRQCHIP_AST2700 +#define DRIVERS_IRQCHIP_AST2700 + +#include <linux/device.h> +#include <linux/irqdomain.h> + +#define AST2700_INTC_INVALID_ROUTE (~0U) +#define ASPEED_INTC_RANGES_BASE 0U +#define ASPEED_INTC_RANGES_COUNT 1U + +struct aspeed_intc_interrupt_range { + u32 start; + u32 count; + struct irq_fwspec upstream; + struct irq_domain *domain; +}; + +struct aspeed_intc_interrupt_ranges { + struct aspeed_intc_interrupt_range *ranges; + unsigned int nranges; +}; + +struct aspeed_intc0 { + struct device *dev; + void __iomem *base; + raw_spinlock_t intc_lock; + struct irq_domain *local; + struct device_node *parent; + struct aspeed_intc_interrupt_ranges ranges; +}; + +int aspeed_intc_populate_ranges(struct device *dev, + struct aspeed_intc_interrupt_ranges *ranges); + +int aspeed_intc0_resolve_route(const struct irq_domain *c0domain, + size_t nc1outs, + const u32 *c1outs, + size_t nc1ranges, + const struct aspeed_intc_interrupt_range *c1ranges, + struct aspeed_intc_interrupt_range *resolved); + +#endif diff --git a/drivers/irqchip/irq-econet-en751221.c b/drivers/irqchip/irq-econet-en751221.c index d83d5eb12795..2ca5d901866f 100644 --- a/drivers/irqchip/irq-econet-en751221.c +++ b/drivers/irqchip/irq-econet-en751221.c @@ -30,6 +30,8 @@ #include <linux/irqchip.h> #include <linux/irqchip/chained_irq.h> +#include <asm/setup.h> + #define IRQ_COUNT 40 #define NOT_PERCPU 0xff @@ -41,15 +43,19 @@ #define REG_PENDING1 0x54 /** - * @membase: Base address of the interrupt controller registers - * @interrupt_shadows: Array of all interrupts, for each value, - * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow - * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt - * - else: This is a per-cpu interrupt whose shadow is the value + * @membase: Base address of the interrupt controller registers + * @domain: The irq_domain for direct dispatch + * @ipi_domain: The irq_domain for inter-process dispatch + * @interrupt_shadows: Array of all interrupts, for each value, + * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow + * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt + * - else: This is a per-cpu interrupt whose shadow is the value */ static struct { - void __iomem *membase; - u8 interrupt_shadows[IRQ_COUNT]; + void __iomem *membase; + struct irq_domain *domain; + struct irq_domain *ipi_domain; + u8 interrupt_shadows[IRQ_COUNT]; } econet_intc __ro_after_init; static DEFINE_RAW_SPINLOCK(irq_lock); @@ -150,6 +156,56 @@ static void econet_intc_from_parent(struct irq_desc *desc) chained_irq_exit(chip, desc); } +/* + * When in VEIC mode, the CPU jumps to a handler in the vector table. + * The only way to know which interrupt is being triggered is from the vector table offset that + * has been jumped to. Reading REG_PENDING(0|1) will tell you which interrupts are currently + * pending in the intc, but that will not tell you which one the intc wants you to process + * right now. And if you are not processing the exact interrupt that the intc wants you to be + * processing, you might be on the wrong VPE. You can't tell which VPE any given REG_PENDING + * interrupt is intended for (shadow IRQ numbers are for masking only, they never flag as + * pending). + * + * Consequently, this little ritual of generating n handler functions and registering one per + * interrupt is unavoidable. + */ +#define X(irq) \ + static void econet_irq_dispatch ## irq (void) \ + { \ + do_domain_IRQ(econet_intc.domain, irq); \ + } + + X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) +X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) +X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) +X(30) X(31) X(32) X(33) X(34) X(35) X(36) X(37) X(38) X(39) + +#undef X +#define X(irq) econet_irq_dispatch ## irq, + +static void (* const econet_irq_dispatchers[])(void) = { + X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) + X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) + X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) + X(30) X(31) X(32) X(33) X(34) X(35) X(36) X(37) X(38) X(39) +}; + +/* Likewise, we do the same for the 2 IPI IRQs so that we can route them back */ +static void econet_cpu_dispatch0(void) +{ + do_domain_IRQ(econet_intc.ipi_domain, 0); +} + +static void econet_cpu_dispatch1(void) +{ + do_domain_IRQ(econet_intc.ipi_domain, 1); +} + +static void (* const econet_cpu_dispatchers[])(void) = { + econet_cpu_dispatch0, + econet_cpu_dispatch1, +}; + static const struct irq_chip econet_irq_chip; static int econet_intc_map(struct irq_domain *d, u32 irq, irq_hw_number_t hwirq) @@ -174,6 +230,10 @@ static int econet_intc_map(struct irq_domain *d, u32 irq, irq_hw_number_t hwirq) } irq_set_chip_data(irq, NULL); + + if (cpu_has_veic) + set_vi_handler(hwirq + 1, econet_irq_dispatchers[hwirq]); + return 0; } @@ -249,6 +309,100 @@ static int __init get_shadow_interrupts(struct device_node *node) return 0; } +/** + * econet_cpu_init() - configure routing of CPU interrupts to the correct domain. + * @node: The devicetree node of this interrupt controller. + * + * Interrupts that originate from the CPU are unconditionally unmasked here and are re-routed back + * to the IPI irq_domain in the CPU intc. Masking still takes place but the CPU intc is in charge + * of it, using the mask bits of the c0_status register. + * + * Note that because IP2 ... IP7 are repurposed as Interrupt Priority Level, only the two IPI + * interrupts are actually supported. + */ +static int __init econet_cpu_init(struct device_node *node) +{ + const char *field = "econet,cpu-interrupt-map"; + struct device_node *parent_intc; + int map_size; + u32 mask; + + map_size = of_property_count_u32_elems(node, field); + + if (map_size <= 0) { + return 0; + } else if (map_size % 2) { + pr_err("%pOF: %s count is odd, ignoring\n", node, field); + return 0; + } + + u32 *maps __free(kfree) = kmalloc_array(map_size, sizeof(u32), GFP_KERNEL); + if (!maps) + return -ENOMEM; + + if (of_property_read_u32_array(node, field, maps, map_size)) { + pr_err("%pOF: Failed to read %s\n", node, field); + return -EINVAL; + } + + /* Validation */ + for (int i = 0; i < map_size; i += 2) { + u32 receive = maps[i]; + u32 dispatch = maps[i + 1]; + u8 shadow; + + if (receive >= IRQ_COUNT) { + pr_err("%pOF: Entry %d:%d in %s (%u) is out of bounds\n", + node, i, 0, field, receive); + return -EINVAL; + } + + shadow = econet_intc.interrupt_shadows[receive]; + if (shadow != NOT_PERCPU && shadow >= IRQ_COUNT) { + pr_err("%pOF: Entry %d:%d in %s (%u) has invalid shadow (%d)\n", + node, i, 0, field, receive, shadow); + return -EINVAL; + } + + if (dispatch >= ARRAY_SIZE(econet_cpu_dispatchers)) { + pr_err("%pOF: Entry %d:%d in %s (%u) is out of bounds only IPI interrupts are supported\n", + node, i, 1, field, dispatch); + return -EINVAL; + } + } + + parent_intc = of_irq_find_parent(node); + if (!parent_intc) { + pr_err("%pOF: Failed to find parent %s\n", node, "IRQ device"); + return -ENODEV; + } + + econet_intc.ipi_domain = irq_find_matching_host(parent_intc, DOMAIN_BUS_IPI); + if (!econet_intc.ipi_domain) { + pr_err("%pOF: Failed to find parent %s\n", node, "IPI domain"); + return -ENODEV; + } + + mask = 0; + for (int i = 0; i < map_size; i += 2) { + u32 receive = maps[i]; + u32 dispatch = maps[i + 1]; + u8 shadow; + + set_vi_handler(receive + 1, econet_cpu_dispatchers[dispatch]); + + mask |= BIT(receive); + + shadow = econet_intc.interrupt_shadows[receive]; + if (shadow != NOT_PERCPU) + mask |= BIT(shadow); + } + + econet_wreg(REG_MASK0, mask, mask); + + return 0; +} + static int __init econet_intc_of_init(struct device_node *node, struct device_node *parent) { struct irq_domain *domain; @@ -294,7 +448,23 @@ static int __init econet_intc_of_init(struct device_node *node, struct device_no goto err_unmap; } - irq_set_chained_handler_and_data(irq, econet_intc_from_parent, domain); + /* + * 34K Manual (MD00534) Section 6.3.1.3 rev 1.13 page 136: + * In VEIC mode, IP2 ... IP7 are repurposed as Interrupt Priority Level. The controller + * will filter incoming interrupts whose priority is lower than the IPL number. Therefore + * we must not set any of these bits. We avoid setting IP2 by not actually chaining this + * intc to the CPU intc. + */ + if (cpu_has_veic) { + ret = econet_cpu_init(node); + + if (ret) + return ret; + } else { + irq_set_chained_handler_and_data(irq, econet_intc_from_parent, domain); + } + + econet_intc.domain = domain; return 0; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 291d7668cc8d..b57d81ad33a0 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -4784,8 +4784,7 @@ static bool __maybe_unused its_enable_quirk_cavium_22375(void *data) struct its_node *its = data; /* erratum 22375: only alloc 8MB table size (20 bits) */ - its->typer &= ~GITS_TYPER_DEVBITS; - its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, 20 - 1); + FIELD_MODIFY(GITS_TYPER_DEVBITS, &its->typer, 20 - 1); its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; return true; @@ -4805,8 +4804,7 @@ static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data) struct its_node *its = data; /* On QDF2400, the size of the ITE is 16Bytes */ - its->typer &= ~GITS_TYPER_ITT_ENTRY_SIZE; - its->typer |= FIELD_PREP(GITS_TYPER_ITT_ENTRY_SIZE, 16 - 1); + FIELD_MODIFY(GITS_TYPER_ITT_ENTRY_SIZE, &its->typer, 16 - 1); return true; } @@ -4840,10 +4838,8 @@ static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data) its->get_msi_base = its_irq_get_msi_base_pre_its; ids = ilog2(pre_its_window[1]) - 2; - if (device_ids(its) > ids) { - its->typer &= ~GITS_TYPER_DEVBITS; - its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, ids - 1); - } + if (device_ids(its) > ids) + FIELD_MODIFY(GITS_TYPER_DEVBITS, &its->typer, ids - 1); /* the pre-ITS breaks isolation, so disable MSI remapping */ its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_ISOLATED_MSI; @@ -5837,6 +5833,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists, its_acpi_probe(); if (list_empty(&its_nodes)) { + rdists->has_vlpis = false; pr_warn("ITS: No ITS available, not enabling LPIs\n"); return -ENXIO; } diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c index 758262fd5bd6..53d7d23af9bb 100644 --- a/drivers/irqchip/irq-loongarch-avec.c +++ b/drivers/irqchip/irq-loongarch-avec.c @@ -24,7 +24,6 @@ #define VECTORS_PER_REG 64 #define IRR_VECTOR_MASK 0xffUL #define IRR_INVALID_MASK 0x80000000UL -#define AVEC_MSG_OFFSET 0x100000 #ifdef CONFIG_SMP struct pending_list { @@ -47,15 +46,6 @@ struct avecintc_chip { static struct avecintc_chip loongarch_avec; -struct avecintc_data { - struct list_head entry; - unsigned int cpu; - unsigned int vec; - unsigned int prev_cpu; - unsigned int prev_vec; - unsigned int moving; -}; - static inline void avecintc_enable(void) { #ifdef CONFIG_MACH_LOONGSON64 @@ -87,7 +77,7 @@ static inline void pending_list_init(int cpu) INIT_LIST_HEAD(&plist->head); } -static void avecintc_sync(struct avecintc_data *adata) +void avecintc_sync(struct avecintc_data *adata) { struct pending_list *plist; @@ -111,7 +101,7 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de return -EBUSY; if (cpu_online(adata->cpu) && cpumask_test_cpu(adata->cpu, dest)) - return 0; + return IRQ_SET_MASK_OK_DONE; cpumask_and(&intersect_mask, dest, cpu_online_mask); @@ -123,7 +113,8 @@ static int avecintc_set_affinity(struct irq_data *data, const struct cpumask *de adata->cpu = cpu; adata->vec = vector; per_cpu_ptr(irq_map, adata->cpu)[adata->vec] = irq_data_to_desc(data); - avecintc_sync(adata); + if (!cpu_has_redirectint) + avecintc_sync(adata); } irq_data_update_effective_affinity(data, cpumask_of(cpu)); @@ -415,6 +406,9 @@ static int __init pch_msi_parse_madt(union acpi_subtable_headers *header, static inline int __init acpi_cascade_irqdomain_init(void) { + if (cpu_has_redirectint) + return redirect_acpi_init(loongarch_avec.domain); + return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1); } diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c new file mode 100644 index 000000000000..21c649a89a70 --- /dev/null +++ b/drivers/irqchip/irq-loongarch-ir.c @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024-2026 Loongson Technologies, Inc. + */ +#define pr_fmt(fmt) "redirect: " fmt + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/irq-msi-lib.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/msi.h> +#include <linux/spinlock.h> + +#include <asm/irq.h> +#include <asm/loongarch.h> +#include <asm/loongson.h> +#include <asm/numa.h> +#include <asm/setup.h> + +#include "irq-loongson.h" + +#define LOONGARCH_IOCSR_REDIRECT_CFG 0x15e0 +#define LOONGARCH_IOCSR_REDIRECT_TBR 0x15e8 /* IRT BASE REG */ +#define LOONGARCH_IOCSR_REDIRECT_CQB 0x15f0 /* IRT CACHE QUEUE BASE */ +#define LOONGARCH_IOCSR_REDIRECT_CQH 0x15f8 /* IRT CACHE QUEUE HEAD, 32bit */ +#define LOONGARCH_IOCSR_REDIRECT_CQT 0x15fc /* IRT CACHE QUEUE TAIL, 32bit */ + +#define CQB_ADDR_MASK GENMASK_U64(47, 12) +#define CQB_SIZE_MASK 0xf + +#define GPID_ADDR_MASK GENMASK_U64(47, 6) +#define GPID_ADDR_SHIFT 6 + +#define INVALID_INDEX 0 +#define CFG_DISABLE_IDLE 2 + +#define MAX_IR_ENGINES 16 + +struct redirect_entry { + struct { + u64 valid : 1, + res1 : 5, + gpid : 42, + res2 : 8, + vector : 8; + } lo; + u64 hi; +}; + +#define IRD_ENTRY_SIZE sizeof(struct redirect_entry) +#define IRD_ENTRIES SZ_64K +#define IRD_TABLE_PAGE_ORDER get_order(IRD_ENTRIES * IRD_ENTRY_SIZE) + +struct redirect_cmd { + union { + u64 cmd_info; + struct { + u64 res1 : 4, + type : 1, + need_notice : 1, + pad1 : 2, + index : 16, + pad2 : 40; + } index; + }; + u64 notice_addr; +}; + +#define IRD_CMD_SIZE sizeof(struct redirect_cmd) +#define INV_QUEUE_SIZE SZ_4K +#define INV_QUEUE_PAGE_ORDER get_order(INV_QUEUE_SIZE * IRD_CMD_SIZE) + +struct redirect_gpid { + u64 pir[4]; /* Pending interrupt requested */ + u8 en : 1, /* Doorbell */ + res1 : 7; + u8 irqnum; + u16 res2; + u32 dstcpu; + u32 rsvd[6]; +}; + +struct redirect_table { + struct redirect_entry *table; + unsigned long *bitmap; + raw_spinlock_t lock; +}; + +struct redirect_queue { + struct redirect_cmd *cmd_base; + int head; + int tail; + raw_spinlock_t lock; +}; + +struct redirect_desc { + struct redirect_table ird_table; + struct redirect_queue inv_queue; + int node; +}; + +struct redirect_item { + int index; + struct redirect_desc *irde; + struct redirect_gpid *gpid; +}; + +static struct irq_domain *redirect_domain; +static struct redirect_desc redirect_descs[MAX_IR_ENGINES]; + +static phys_addr_t msi_base_addr; +static phys_addr_t redirect_reg_base = LOONGSON_REG_BASE; + +#ifdef CONFIG_32BIT + +#define REDIRECT_REG(reg, node) \ + ((void __iomem *)(IO_BASE | redirect_reg_base | (reg))) + +#else + +#define REDIRECT_REG(reg, node) \ + ((void __iomem *)(IO_BASE | redirect_reg_base | (u64)(node) << NODE_ADDRSPACE_SHIFT | (reg))) + +#endif + +static inline u32 redirect_read_reg32(u32 node, u32 reg) +{ + return readl(REDIRECT_REG(reg, node)); +} + +static inline void redirect_write_reg32(u32 node, u32 val, u32 reg) +{ + writel(val, REDIRECT_REG(reg, node)); +} + +static inline void redirect_write_reg64(u32 node, u64 val, u32 reg) +{ + writeq(val, REDIRECT_REG(reg, node)); +} + +static inline struct redirect_entry *item_get_entry(struct redirect_item *item) +{ + return item->irde->ird_table.table + item->index; +} + +static inline bool invalid_queue_is_full(int node, u32 *tail) +{ + u32 head = redirect_read_reg32(node, LOONGARCH_IOCSR_REDIRECT_CQH); + + *tail = redirect_read_reg32(node, LOONGARCH_IOCSR_REDIRECT_CQT); + + return head == ((*tail + 1) % INV_QUEUE_SIZE); +} + +static void invalid_enqueue(struct redirect_item *item, struct redirect_cmd *cmd) +{ + struct redirect_queue *inv_queue = &item->irde->inv_queue; + u32 tail; + + guard(raw_spinlock_irqsave)(&inv_queue->lock); + + while (invalid_queue_is_full(item->irde->node, &tail)) + cpu_relax(); + + memcpy(&inv_queue->cmd_base[tail], cmd, sizeof(*cmd)); + + redirect_write_reg32(item->irde->node, (tail + 1) % INV_QUEUE_SIZE, LOONGARCH_IOCSR_REDIRECT_CQT); +} + +static void irde_invalidate_entry(struct redirect_item *item) +{ + struct redirect_cmd cmd; + u64 raddr = 0; + + cmd.cmd_info = 0; + cmd.index.type = INVALID_INDEX; + cmd.index.need_notice = 1; + cmd.index.index = item->index; + cmd.notice_addr = (u64)(__pa(&raddr)); + + invalid_enqueue(item, &cmd); + + /* + * The CPU needs to wait here for cmd to complete, and it determines this + * by checking whether the invalidation queue has already written a valid value + * to cmd.notice_addr. + */ + while (!raddr) + cpu_relax(); +} + +static inline struct avecintc_data *irq_data_get_avec_data(struct irq_data *data) +{ + return data->parent_data->chip_data; +} + +static int redirect_table_alloc(int node, u32 nr_irqs) +{ + struct redirect_table *ird_table = &redirect_descs[node].ird_table; + int index, order = 0; + + if (nr_irqs > 1) { + nr_irqs = __roundup_pow_of_two(nr_irqs); + order = ilog2(nr_irqs); + } + + guard(raw_spinlock_irqsave)(&ird_table->lock); + + index = bitmap_find_free_region(ird_table->bitmap, IRD_ENTRIES, order); + if (index < 0) { + pr_err("No redirect entry to use\n"); + return -EINVAL; + } + + return index; +} + +static void redirect_table_free(struct redirect_item *item) +{ + struct redirect_table *ird_table = &item->irde->ird_table; + struct redirect_entry *entry = item_get_entry(item); + + memset(entry, 0, sizeof(*entry)); + + scoped_guard(raw_spinlock_irq, &ird_table->lock) + clear_bit(item->index, ird_table->bitmap); + + kfree(item->gpid); + + irde_invalidate_entry(item); +} + +static inline void redirect_domain_prepare_entry(struct redirect_item *item, + struct avecintc_data *adata) +{ + struct redirect_entry *entry = item_get_entry(item); + + item->gpid->en = 1; + item->gpid->dstcpu = adata->cpu; + item->gpid->irqnum = adata->vec; + + entry->lo.valid = 1; + entry->lo.vector = 0xff; + entry->lo.gpid = ((unsigned long)item->gpid & GPID_ADDR_MASK) >> GPID_ADDR_SHIFT; +} + +static void redirect_free_resources(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + for (int i = 0; i < nr_irqs; i++) { + struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq + i); + + if (irq_data && irq_data->chip_data) { + struct redirect_item *item = irq_data->chip_data; + + redirect_table_free(item); + kfree(item); + } + } +} + +#ifdef CONFIG_SMP +static int redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force) +{ + struct avecintc_data *adata = irq_data_get_avec_data(data); + struct redirect_item *item = data->chip_data; + int ret; + + ret = irq_chip_set_affinity_parent(data, dest, force); + switch (ret) { + case IRQ_SET_MASK_OK: + break; + case IRQ_SET_MASK_OK_DONE: + return ret; + default: + pr_err("IRDE: set_affinity error %d\n", ret); + return ret; + } + + redirect_domain_prepare_entry(item, adata); + irde_invalidate_entry(item); + avecintc_sync(adata); + + return IRQ_SET_MASK_OK; +} +#endif + +static void redirect_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct redirect_item *item = irq_data_get_irq_chip_data(d); + + msg->address_hi = 0x0; + msg->address_lo = (msi_base_addr | 1 << 2); + msg->data = item->index; +} + +static struct irq_chip loongarch_redirect_chip = { + .name = "REDIRECT", + .irq_ack = irq_chip_ack_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, +#ifdef CONFIG_SMP + .irq_set_affinity = redirect_set_affinity, +#endif + .irq_compose_msi_msg = redirect_compose_msi_msg, +}; + +static int redirect_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + msi_alloc_info_t *info = arg; + int ret, i, node, index; + + node = dev_to_node(info->desc->dev); + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); + if (ret < 0) + return ret; + + index = redirect_table_alloc(node, nr_irqs); + if (index < 0) { + pr_err("Alloc redirect table entry failed\n"); + return -EINVAL; + } + + for (i = 0; i < nr_irqs; i++) { + struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq + i); + struct redirect_item *item; + + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) { + pr_err("Alloc redirect descriptor failed\n"); + goto out_free_resources; + } + item->irde = &redirect_descs[node]; + + /* + * Only bits 47:6 of the GPID are passed to the controller, + * 64-byte alignment must be guarantee and make kzalloc can + * align to the respective size. + */ + static_assert(sizeof(*item->gpid) == 64); + item->gpid = kzalloc_node(sizeof(*item->gpid), GFP_KERNEL, node); + if (!item->gpid) { + pr_err("Alloc redirect GPID failed\n"); + goto out_free_resources; + } + item->index = index + i; + + irq_data->chip_data = item; + irq_data->chip = &loongarch_redirect_chip; + + redirect_domain_prepare_entry(item, irq_data_get_avec_data(irq_data)); + } + + return 0; + +out_free_resources: + redirect_free_resources(domain, virq, nr_irqs); + irq_domain_free_irqs_common(domain, virq, nr_irqs); + + return -ENOMEM; +} + +static void redirect_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) +{ + redirect_free_resources(domain, virq, nr_irqs); + return irq_domain_free_irqs_common(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops redirect_domain_ops = { + .alloc = redirect_domain_alloc, + .free = redirect_domain_free, + .select = msi_lib_irq_domain_select, +}; + +static int redirect_table_init(struct redirect_desc *irde) +{ + struct redirect_table *ird_table = &irde->ird_table; + unsigned long *bitmap; + struct folio *folio; + + folio = __folio_alloc_node(GFP_KERNEL | __GFP_ZERO, IRD_TABLE_PAGE_ORDER, irde->node); + if (!folio) { + pr_err("Node [%d] redirect table alloc pages failed!\n", irde->node); + return -ENOMEM; + } + ird_table->table = folio_address(folio); + + bitmap = bitmap_zalloc(IRD_ENTRIES, GFP_KERNEL); + if (!bitmap) { + pr_err("Node [%d] redirect table bitmap alloc pages failed!\n", irde->node); + folio_put(folio); + ird_table->table = NULL; + return -ENOMEM; + } + ird_table->bitmap = bitmap; + + raw_spin_lock_init(&ird_table->lock); + + return 0; +} + +static int redirect_queue_init(struct redirect_desc *irde) +{ + struct redirect_queue *inv_queue = &irde->inv_queue; + struct folio *folio; + + folio = __folio_alloc_node(GFP_KERNEL | __GFP_ZERO, INV_QUEUE_PAGE_ORDER, irde->node); + if (!folio) { + pr_err("Node [%d] invalid queue alloc pages failed!\n", irde->node); + return -ENOMEM; + } + + inv_queue->cmd_base = folio_address(folio); + inv_queue->head = 0; + inv_queue->tail = 0; + raw_spin_lock_init(&inv_queue->lock); + + return 0; +} + +static void redirect_irde_cfg(struct redirect_desc *irde) +{ + redirect_write_reg64(irde->node, CFG_DISABLE_IDLE, LOONGARCH_IOCSR_REDIRECT_CFG); + redirect_write_reg64(irde->node, __pa(irde->ird_table.table), LOONGARCH_IOCSR_REDIRECT_TBR); + redirect_write_reg32(irde->node, 0, LOONGARCH_IOCSR_REDIRECT_CQH); + redirect_write_reg32(irde->node, 0, LOONGARCH_IOCSR_REDIRECT_CQT); + redirect_write_reg64(irde->node, ((unsigned long)irde->inv_queue.cmd_base & CQB_ADDR_MASK) | + CQB_SIZE_MASK, LOONGARCH_IOCSR_REDIRECT_CQB); +} + +static void __init redirect_irde_free(struct redirect_desc *irde) +{ + struct redirect_table *ird_table = &redirect_descs->ird_table; + struct redirect_queue *inv_queue = &redirect_descs->inv_queue; + + if (ird_table->table) { + folio_put(virt_to_folio(ird_table->table)); + ird_table->table = NULL; + } + + if (ird_table->bitmap) { + bitmap_free(ird_table->bitmap); + ird_table->bitmap = NULL; + } + + if (inv_queue->cmd_base) { + folio_put(virt_to_folio(inv_queue->cmd_base)); + inv_queue->cmd_base = NULL; + } +} + +static int __init redirect_irde_init(int node) +{ + struct redirect_desc *irde = &redirect_descs[node]; + int ret; + + irde->node = node; + + ret = redirect_table_init(irde); + if (ret) + return ret; + + ret = redirect_queue_init(irde); + if (ret) { + redirect_irde_free(irde); + return ret; + } + + redirect_irde_cfg(irde); + + return 0; +} + +static int __init pch_msi_parse_madt(union acpi_subtable_headers *header, const unsigned long end) +{ + struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header; + + msi_base_addr = pchmsi_entry->msg_address - AVEC_MSG_OFFSET; + + return pch_msi_acpi_init_avec(redirect_domain); +} + +static int __init acpi_cascade_irqdomain_init(void) +{ + return acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1); +} + +int __init redirect_acpi_init(struct irq_domain *parent) +{ + struct fwnode_handle *fwnode; + int ret = -EINVAL, node; + + fwnode = irq_domain_alloc_named_fwnode("redirect"); + if (!fwnode) { + pr_err("Unable to alloc redirect domain handle\n"); + goto fail; + } + + redirect_domain = irq_domain_create_hierarchy(parent, 0, IRD_ENTRIES, fwnode, + &redirect_domain_ops, redirect_descs); + if (!redirect_domain) { + pr_err("Unable to alloc redirect domain\n"); + goto out_free_fwnode; + } + + for_each_node_mask(node, node_possible_map) { + ret = redirect_irde_init(node); + if (ret) + goto out_clear_irde; + } + + ret = acpi_cascade_irqdomain_init(); + if (ret < 0) { + pr_err("Failed to cascade IRQ domain, ret=%d\n", ret); + goto out_clear_irde; + } + + pr_info("init succeeded\n"); + + return 0; + +out_clear_irde: + for_each_node_mask(node, node_possible_map) { + redirect_irde_free(&redirect_descs[node]); + } + irq_domain_remove(redirect_domain); +out_free_fwnode: + irq_domain_free_fwnode(fwnode); +fail: + return ret; +} diff --git a/drivers/irqchip/irq-loongson.h b/drivers/irqchip/irq-loongson.h index 11fa138d1f44..dd37cd7f453d 100644 --- a/drivers/irqchip/irq-loongson.h +++ b/drivers/irqchip/irq-loongson.h @@ -6,6 +6,17 @@ #ifndef _DRIVERS_IRQCHIP_IRQ_LOONGSON_H #define _DRIVERS_IRQCHIP_IRQ_LOONGSON_H +#define AVEC_MSG_OFFSET 0x100000 + +struct avecintc_data { + struct list_head entry; + unsigned int cpu; + unsigned int vec; + unsigned int prev_cpu; + unsigned int prev_vec; + unsigned int moving; +}; + int find_pch_pic(u32 gsi); int liointc_acpi_init(struct irq_domain *parent, @@ -14,6 +25,8 @@ int eiointc_acpi_init(struct irq_domain *parent, struct acpi_madt_eio_pic *acpi_eiointc); int avecintc_acpi_init(struct irq_domain *parent); +int redirect_acpi_init(struct irq_domain *parent); + int htvec_acpi_init(struct irq_domain *parent, struct acpi_madt_ht_pic *acpi_htvec); int pch_lpc_acpi_init(struct irq_domain *parent, @@ -24,4 +37,6 @@ int pch_msi_acpi_init(struct irq_domain *parent, struct acpi_madt_msi_pic *acpi_pchmsi); int pch_msi_acpi_init_avec(struct irq_domain *parent); +void avecintc_sync(struct avecintc_data *adata); + #endif /* _DRIVERS_IRQCHIP_IRQ_LOONGSON_H */ diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index 74a376ef452e..91a9c337fe6d 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -27,6 +27,10 @@ /* use for A1 like chips */ #define REG_PIN_A1_SEL 0x04 +/* use for A9 like chips */ +#define REG_A9_AO_POL 0x00 +#define REG_A9_AO_EDGE 0x30 + /* * Note: The S905X3 datasheet reports that BOTH_EDGE is controlled by * bits 24 to 31. Tests on the actual HW show that these bits are @@ -53,6 +57,8 @@ static void meson_a1_gpio_irq_sel_pin(struct meson_gpio_irq_controller *ctl, static void meson_a1_gpio_irq_init(struct meson_gpio_irq_controller *ctl); static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, unsigned int type, u32 *channel_hwirq); +static int meson_a9_ao_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, + unsigned int type, u32 *channel_hwirq); static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, unsigned int type, u32 *channel_hwirq); @@ -116,6 +122,18 @@ struct meson_gpio_irq_params { .pin_sel_mask = 0xff, \ .nr_channels = 2, \ +#define INIT_MESON_A9_AO_COMMON_DATA(irqs) \ + INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init, \ + meson_a1_gpio_irq_sel_pin, \ + meson_a9_ao_gpio_irq_set_type) \ + .support_edge_both = true, \ + .edge_both_offset = 0, \ + .edge_single_offset = 0, \ + .edge_pol_reg = 0x2c, \ + .pol_low_offset = 0, \ + .pin_sel_mask = 0xff, \ + .nr_channels = 20, \ + #define INIT_MESON_S4_COMMON_DATA(irqs) \ INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init, \ meson_a1_gpio_irq_sel_pin, \ @@ -170,6 +188,14 @@ static const struct meson_gpio_irq_params a5_params = { INIT_MESON_S4_COMMON_DATA(99) }; +static const struct meson_gpio_irq_params a9_params = { + INIT_MESON_S4_COMMON_DATA(96) +}; + +static const struct meson_gpio_irq_params a9_ao_params = { + INIT_MESON_A9_AO_COMMON_DATA(39) +}; + static const struct meson_gpio_irq_params s4_params = { INIT_MESON_S4_COMMON_DATA(82) }; @@ -203,6 +229,8 @@ static const struct of_device_id meson_irq_gpio_matches[] __maybe_unused = { { .compatible = "amlogic,a4-gpio-ao-intc", .data = &a4_ao_params }, { .compatible = "amlogic,a4-gpio-intc", .data = &a4_params }, { .compatible = "amlogic,a5-gpio-intc", .data = &a5_params }, + { .compatible = "amlogic,a9-gpio-ao-intc", .data = &a9_ao_params }, + { .compatible = "amlogic,a9-gpio-intc", .data = &a9_params }, { .compatible = "amlogic,s6-gpio-intc", .data = &s6_params }, { .compatible = "amlogic,s7-gpio-intc", .data = &s7_params }, { .compatible = "amlogic,s7d-gpio-intc", .data = &s7_params }, @@ -376,6 +404,55 @@ static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, } /* + * gpio irq relative registers for a9_ao + * -PADCTRL_GPIO_IRQ_CTRL0 + * bit[31]: enable/disable all the irq lines + * bit[0-19]: polarity trigger + * + * -PADCTRL_GPIO_IRQ_CTRL[X] + * bit[0-5]: 6 bits to choose gpio source for irq line 2*[X] - 2 + * bit[16-21]:6 bits to choose gpio source for irq line 2*[X] - 1 + * where X = 1-10 + * + * -PADCTRL_GPIO_IRQ_CTRL[11] + * bit[0-19]: both edge trigger + * + * -PADCTRL_GPIO_IRQ_CTRL[12] + * bit[0-19]: single edge trigger + */ +static int meson_a9_ao_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, + unsigned int type, u32 *channel_hwirq) +{ + const struct meson_gpio_irq_params *params = ctl->params; + unsigned int idx; + u32 val; + + idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq); + + type &= IRQ_TYPE_SENSE_MASK; + + meson_gpio_irq_update_bits(ctl, params->edge_pol_reg, BIT(idx), 0); + + if (type == IRQ_TYPE_EDGE_BOTH) { + val = BIT(ctl->params->edge_both_offset + idx); + meson_gpio_irq_update_bits(ctl, params->edge_pol_reg, val, val); + return 0; + } + + val = 0; + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) + val = BIT(idx); + meson_gpio_irq_update_bits(ctl, REG_A9_AO_POL, BIT(idx), val); + + val = 0; + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) + val = BIT(idx); + meson_gpio_irq_update_bits(ctl, REG_A9_AO_EDGE, BIT(idx), val); + + return 0; +}; + +/* * gpio irq relative registers for s4 * -PADCTRL_GPIO_IRQ_CTRL0 * bit[31]: enable/disable all the irq lines diff --git a/drivers/irqchip/irq-realtek-rtl.c b/drivers/irqchip/irq-realtek-rtl.c index 942c1f8c363d..2ae3be7fa633 100644 --- a/drivers/irqchip/irq-realtek-rtl.c +++ b/drivers/irqchip/irq-realtek-rtl.c @@ -23,10 +23,10 @@ #define RTL_ICTL_NUM_INPUTS 32 -#define REG(x) (realtek_ictl_base + x) +#define REG(cpu, x) (realtek_ictl_base[cpu] + x) static DEFINE_RAW_SPINLOCK(irq_lock); -static void __iomem *realtek_ictl_base; +static void __iomem *realtek_ictl_base[NR_CPUS]; /* * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering, @@ -37,10 +37,29 @@ static void __iomem *realtek_ictl_base; #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32)) #define IRR_SHIFT(idx) ((idx * 4) % 32) -static void write_irr(void __iomem *irr0, int idx, u32 value) +static inline void enable_gimr(unsigned int cpu, unsigned int hw_irq) { - unsigned int offset = IRR_OFFSET(idx); - unsigned int shift = IRR_SHIFT(idx); + u32 gimr; + + gimr = readl(REG(cpu, RTL_ICTL_GIMR)); + gimr |= BIT(hw_irq); + writel(gimr, REG(cpu, RTL_ICTL_GIMR)); +} + +static inline void disable_gimr(unsigned int cpu, unsigned int hw_irq) +{ + u32 gimr; + + gimr = readl(REG(cpu, RTL_ICTL_GIMR)); + gimr &= ~BIT(hw_irq); + writel(gimr, REG(cpu, RTL_ICTL_GIMR)); +} + +static void write_irr(unsigned int cpu, int hw_irq, u32 value) +{ + void __iomem *irr0 = REG(cpu, RTL_ICTL_IRR0); + unsigned int offset = IRR_OFFSET(hw_irq); + unsigned int shift = IRR_SHIFT(hw_irq); u32 irr; irr = readl(irr0 + offset) & ~(0xf << shift); @@ -50,47 +69,51 @@ static void write_irr(void __iomem *irr0, int idx, u32 value) static void realtek_ictl_unmask_irq(struct irq_data *i) { - unsigned long flags; - u32 value; + unsigned int cpu; - raw_spin_lock_irqsave(&irq_lock, flags); + guard(raw_spinlock)(&irq_lock); + for_each_cpu(cpu, irq_data_get_effective_affinity_mask(i)) + enable_gimr(cpu, i->hwirq); +} - value = readl(REG(RTL_ICTL_GIMR)); - value |= BIT(i->hwirq); - writel(value, REG(RTL_ICTL_GIMR)); +static void realtek_ictl_mask_irq(struct irq_data *i) +{ + unsigned int cpu; - raw_spin_unlock_irqrestore(&irq_lock, flags); + guard(raw_spinlock)(&irq_lock); + for_each_cpu(cpu, irq_data_get_effective_affinity_mask(i)) + disable_gimr(cpu, i->hwirq); } -static void realtek_ictl_mask_irq(struct irq_data *i) +static int realtek_ictl_irq_affinity(struct irq_data *i, const struct cpumask *dest, bool force) { - unsigned long flags; - u32 value; + if (!irqd_irq_masked(i)) + realtek_ictl_mask_irq(i); - raw_spin_lock_irqsave(&irq_lock, flags); + irq_data_update_effective_affinity(i, dest); - value = readl(REG(RTL_ICTL_GIMR)); - value &= ~BIT(i->hwirq); - writel(value, REG(RTL_ICTL_GIMR)); + if (!irqd_irq_masked(i)) + realtek_ictl_unmask_irq(i); - raw_spin_unlock_irqrestore(&irq_lock, flags); + return IRQ_SET_MASK_OK; } static struct irq_chip realtek_ictl_irq = { - .name = "realtek-rtl-intc", - .irq_mask = realtek_ictl_mask_irq, - .irq_unmask = realtek_ictl_unmask_irq, + .name = "realtek-rtl-intc", + .irq_mask = realtek_ictl_mask_irq, + .irq_unmask = realtek_ictl_unmask_irq, + .irq_set_affinity = realtek_ictl_irq_affinity, }; static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { - unsigned long flags; + unsigned int cpu; irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq); - raw_spin_lock_irqsave(&irq_lock, flags); - write_irr(REG(RTL_ICTL_IRR0), hw, 1); - raw_spin_unlock_irqrestore(&irq_lock, flags); + guard(raw_spinlock_irqsave)(&irq_lock); + for_each_present_cpu(cpu) + write_irr(cpu, hw, 1); return 0; } @@ -103,12 +126,13 @@ static const struct irq_domain_ops irq_domain_ops = { static void realtek_irq_dispatch(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int cpu = smp_processor_id(); struct irq_domain *domain; unsigned long pending; unsigned int soc_int; chained_irq_enter(chip, desc); - pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); + pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR)); if (unlikely(!pending)) { spurious_interrupt(); @@ -116,7 +140,7 @@ static void realtek_irq_dispatch(struct irq_desc *desc) } domain = irq_desc_get_handler_data(desc); - for_each_set_bit(soc_int, &pending, 32) + for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS) generic_handle_domain_irq(domain, soc_int); out: @@ -127,17 +151,19 @@ static int __init realtek_rtl_of_init(struct device_node *node, struct device_no { struct of_phandle_args oirq; struct irq_domain *domain; - unsigned int soc_irq; - int parent_irq; - - realtek_ictl_base = of_iomap(node, 0); - if (!realtek_ictl_base) - return -ENXIO; - - /* Disable all cascaded interrupts and clear routing */ - writel(0, REG(RTL_ICTL_GIMR)); - for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) - write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0); + int cpu, parent_irq; + + for_each_present_cpu(cpu) { + realtek_ictl_base[cpu] = of_iomap(node, cpu); + if (!realtek_ictl_base[cpu]) + return -ENXIO; + + /* Disable all cascaded interrupts and clear routing */ + for (unsigned int hw_irq = 0; hw_irq < RTL_ICTL_NUM_INPUTS; hw_irq++) { + disable_gimr(cpu, hw_irq); + write_irr(cpu, hw_irq, 0); + } + } if (WARN_ON(!of_irq_count(node))) { /* diff --git a/drivers/irqchip/irq-renesas-rzt2h.c b/drivers/irqchip/irq-renesas-rzt2h.c index ecb69da55508..e06264add3cc 100644 --- a/drivers/irqchip/irq-renesas-rzt2h.c +++ b/drivers/irqchip/irq-renesas-rzt2h.c @@ -2,6 +2,7 @@ #include <linux/bitfield.h> #include <linux/err.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/irqchip.h> #include <linux/irqchip/irq-renesas-rzt2h.h> @@ -30,16 +31,44 @@ RZT2H_ICU_IRQ_S_COUNT) #define RZT2H_ICU_SEI_COUNT 1 +#define RZT2H_ICU_CA55_ERR_START (RZT2H_ICU_SEI_START + \ + RZT2H_ICU_SEI_COUNT) +#define RZT2H_ICU_CA55_ERR_COUNT 2 + +#define RZT2H_ICU_CR52_ERR_START (RZT2H_ICU_CA55_ERR_START + \ + RZT2H_ICU_CA55_ERR_COUNT) +#define RZT2H_ICU_CR52_ERR_COUNT 4 + +#define RZT2H_ICU_PERI_ERR_START (RZT2H_ICU_CR52_ERR_START + \ + RZT2H_ICU_CR52_ERR_COUNT) +#define RZT2H_ICU_PERI_ERR_COUNT 2 + +#define RZT2H_ICU_DSMIF_ERR_START (RZT2H_ICU_PERI_ERR_START + \ + RZT2H_ICU_PERI_ERR_COUNT) +#define RZT2H_ICU_DSMIF_ERR_COUNT 2 + +#define RZT2H_ICU_ENCIF_ERR_START (RZT2H_ICU_DSMIF_ERR_START + \ + RZT2H_ICU_DSMIF_ERR_COUNT) +#define RZT2H_ICU_ENCIF_ERR_COUNT 2 + #define RZT2H_ICU_NUM_IRQ (RZT2H_ICU_INTCPU_NS_COUNT + \ RZT2H_ICU_INTCPU_S_COUNT + \ RZT2H_ICU_IRQ_NS_COUNT + \ RZT2H_ICU_IRQ_S_COUNT + \ - RZT2H_ICU_SEI_COUNT) + RZT2H_ICU_SEI_COUNT + \ + RZT2H_ICU_CA55_ERR_COUNT + \ + RZT2H_ICU_CR52_ERR_COUNT + \ + RZT2H_ICU_PERI_ERR_COUNT + \ + RZT2H_ICU_DSMIF_ERR_COUNT + \ + RZT2H_ICU_ENCIF_ERR_COUNT) #define RZT2H_ICU_IRQ_IN_RANGE(n, type) \ ((n) >= RZT2H_ICU_##type##_START && \ (n) < RZT2H_ICU_##type##_START + RZT2H_ICU_##type##_COUNT) +#define RZT2H_ICU_SWINT 0x0 +#define RZT2H_ICU_SWINT_IC_MASK(i) BIT(i) + #define RZT2H_ICU_PORTNF_MD 0xc #define RZT2H_ICU_PORTNF_MDi_MASK(i) (GENMASK(1, 0) << ((i) * 2)) #define RZT2H_ICU_PORTNF_MDi_PREP(i, val) (FIELD_PREP(GENMASK(1, 0), val) << ((i) * 2)) @@ -49,6 +78,29 @@ #define RZT2H_ICU_MD_RISING_EDGE 0b10 #define RZT2H_ICU_MD_BOTH_EDGES 0b11 +#define RZT2H_ICU_CA55ERR_E0MSK 0x50 +#define RZT2H_ICU_CA55ERR_CLR 0x60 +#define RZT2H_ICU_CA55ERR_STAT 0x64 +#define RZT2H_ICU_CA55ERR_MASK GENMASK(12, 0) + +#define RZT2H_ICU_PERIERR_E0MSKn(n) (0x98 + 0x4 * (n)) +#define RZT2H_ICU_PERIERR_CLRn(n) (0xc8 + 0x4 * (n)) +#define RZT2H_ICU_PERIERR_STAT 0xd4 +#define RZT2H_ICU_PERIERR_NUM 3 +#define RZT2H_ICU_PERIERR_MASK GENMASK(31, 0) + +#define RZT2H_ICU_DSMIFERR_E0MSKn(n) (0xe0 + 0x4 * (n)) +#define RZT2H_ICU_DSMIFERR_CLRn(n) (0x1a0 + 0x4 * (n)) +#define RZT2H_ICU_DSMIFERR_STAT 0x1d0 +#define RZT2H_ICU_DSMIFERR_NUM 12 +#define RZT2H_ICU_DSMIFERR_MASK GENMASK(31, 0) + +#define RZT2H_ICU_ENCIFERR_E0MSKn(n) (0x200 + 0x4 * (n)) +#define RZT2H_ICU_ENCIFERR_CLRn(n) (0x250 + 0x4 * (n)) +#define RZT2H_ICU_ENCIFERR_STAT 0x264 +#define RZT2H_ICU_ENCIFERR_NUM 5 +#define RZT2H_ICU_ENCIFERR_MASK GENMASK(31, 0) + #define RZT2H_ICU_DMACn_RSSELi(n, i) (0x7d0 + 0x18 * (n) + 0x4 * (i)) #define RZT2H_ICU_DMAC_REQ_SELx_MASK(x) (GENMASK(9, 0) << ((x) * 10)) #define RZT2H_ICU_DMAC_REQ_SELx_PREP(x, val) (FIELD_PREP(GENMASK(9, 0), val) << ((x) * 10)) @@ -99,6 +151,12 @@ static inline int rzt2h_icu_irq_to_offset(struct irq_data *d, void __iomem **bas } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, IRQ_S) || RZT2H_ICU_IRQ_IN_RANGE(hwirq, SEI)) { *offset = hwirq - RZT2H_ICU_IRQ_S_START; *base = priv->base_s; + } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_NS)) { + *offset = hwirq - RZT2H_ICU_INTCPU_NS_START; + *base = priv->base_ns; + } else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_S)) { + *offset = hwirq - RZT2H_ICU_INTCPU_S_START; + *base = priv->base_s; } else { return -EINVAL; } @@ -164,6 +222,28 @@ static int rzt2h_icu_set_type(struct irq_data *d, unsigned int type) return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING); } +static int rzt2h_icu_intcpu_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, + bool state) +{ + unsigned int offset; + void __iomem *base; + int ret; + + if (which != IRQCHIP_STATE_PENDING) + return irq_chip_set_parent_state(d, which, state); + + if (!state) + return 0; + + ret = rzt2h_icu_irq_to_offset(d, &base, &offset); + if (ret) + return ret; + + writel_relaxed(RZT2H_ICU_SWINT_IC_MASK(offset), base + RZT2H_ICU_SWINT); + + return 0; +} + static const struct irq_chip rzt2h_icu_chip = { .name = "rzt2h-icu", .irq_mask = irq_chip_mask_parent, @@ -180,10 +260,27 @@ static const struct irq_chip rzt2h_icu_chip = { IRQCHIP_SKIP_SET_WAKE, }; +static const struct irq_chip rzt2h_icu_intcpu_chip = { + .name = "rzt2h-icu", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = irq_chip_set_wake_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = rzt2h_icu_intcpu_set_irqchip_state, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { struct rzt2h_icu_priv *priv = domain->host_data; + const struct irq_chip *chip; irq_hw_number_t hwirq; unsigned int type; int ret; @@ -192,7 +289,12 @@ static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne if (ret) return ret; - ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzt2h_icu_chip, NULL); + if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_NS) || RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_S)) + chip = &rzt2h_icu_intcpu_chip; + else + chip = &rzt2h_icu_chip; + + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, NULL); if (ret) return ret; @@ -222,6 +324,155 @@ static int rzt2h_icu_parse_interrupts(struct rzt2h_icu_priv *priv, struct device return 0; } +static irqreturn_t rzt2h_icu_intcpu_irq(int irq, void *data) +{ + unsigned int intcpu = (uintptr_t)data; + + pr_info("INTCPU%u software interrupt\n", intcpu); + return IRQ_HANDLED; +} + +static irqreturn_t rzt2h_icu_err_irq(struct rzt2h_icu_priv *priv, const char *name, + unsigned int num, u32 stat_base, u32 clr_base) +{ + bool handled = false; + + for (unsigned int n = 0; n < num; n++) { + u32 stat = readl(priv->base_ns + stat_base + n * 0x4); + + if (!stat) + continue; + + handled = true; + + pr_err("rzt2h-icu: %s error n=%u status=0x%08x\n", name, n, stat); + + writel_relaxed(stat, priv->base_ns + clr_base + n * 0x4); + } + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static irqreturn_t rzt2h_icu_ca55_err_irq(int irq, void *data) +{ + return rzt2h_icu_err_irq(data, "CA55", 1, RZT2H_ICU_CA55ERR_STAT, RZT2H_ICU_CA55ERR_CLR); +} + +static irqreturn_t rzt2h_icu_peri_err_irq(int irq, void *data) +{ + return rzt2h_icu_err_irq(data, "peripheral", RZT2H_ICU_PERIERR_NUM, RZT2H_ICU_PERIERR_STAT, + RZT2H_ICU_PERIERR_CLRn(0)); +} + +static irqreturn_t rzt2h_icu_dsmif_err_irq(int irq, void *data) +{ + return rzt2h_icu_err_irq(data, "DSMIF", RZT2H_ICU_DSMIFERR_NUM, RZT2H_ICU_DSMIFERR_STAT, + RZT2H_ICU_DSMIFERR_CLRn(0)); +} + +static irqreturn_t rzt2h_icu_encif_err_irq(int irq, void *data) +{ + return rzt2h_icu_err_irq(data, "ENCIF", RZT2H_ICU_ENCIFERR_NUM, RZT2H_ICU_ENCIFERR_STAT, + RZT2H_ICU_ENCIFERR_CLRn(0)); +} + +static int rzt2h_icu_request_irqs(struct platform_device *pdev, struct irq_domain *irq_domain, + unsigned int start, unsigned int count, irq_handler_t handler, + void *data) +{ + struct device *dev = &pdev->dev; + unsigned int offset, virq; + struct irq_fwspec fwspec; + int ret; + + for (offset = start; offset < start + count; offset++) { + fwspec.fwnode = irq_domain->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + + virq = irq_create_fwspec_mapping(&fwspec); + if (!virq) + return dev_err_probe(dev, -EINVAL, "Failed to create IRQ %u mapping\n", offset); + + ret = devm_request_irq(dev, virq, handler, 0, dev_name(dev), + data ?: (void *)(uintptr_t)offset); + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ %u\n", offset); + } + + return 0; +} + +static int rzt2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain *irq_domain) +{ + struct rzt2h_icu_priv *priv = platform_get_drvdata(pdev); + unsigned int n; + int ret; + + if (IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION)) { + ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_INTCPU_NS_START, + RZT2H_ICU_INTCPU_NS_COUNT, rzt2h_icu_intcpu_irq, NULL); + if (ret) + return ret; + + ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_INTCPU_S_START, + RZT2H_ICU_INTCPU_S_COUNT, rzt2h_icu_intcpu_irq, NULL); + if (ret) + return ret; + } + + /* + * There are two error interrupts and two error masks that can be used + * separately for each error type. It would not be very useful to + * receive two interrupts for the same error, so use only the first one. + */ + + ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_CA55_ERR_START, 1, + rzt2h_icu_ca55_err_irq, priv); + if (ret) + return ret; + + ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_PERI_ERR_START, 1, + rzt2h_icu_peri_err_irq, priv); + if (ret) + return ret; + + ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_DSMIF_ERR_START, 1, + rzt2h_icu_dsmif_err_irq, priv); + if (ret) + return ret; + + ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_ENCIF_ERR_START, 1, + rzt2h_icu_encif_err_irq, priv); + if (ret) + return ret; + + /* Clear and unmask CA55 error events */ + writel_relaxed(RZT2H_ICU_CA55ERR_MASK, priv->base_ns + RZT2H_ICU_CA55ERR_CLR); + writel_relaxed(0, priv->base_ns + RZT2H_ICU_CA55ERR_E0MSK); + + /* Clear and unmask peripheral error events */ + for (n = 0; n < RZT2H_ICU_PERIERR_NUM; n++) { + writel_relaxed(RZT2H_ICU_PERIERR_MASK, priv->base_ns + RZT2H_ICU_PERIERR_CLRn(n)); + writel_relaxed(0, priv->base_ns + RZT2H_ICU_PERIERR_E0MSKn(n)); + } + + /* Clear and unmask DSMIF error events */ + for (n = 0; n < RZT2H_ICU_DSMIFERR_NUM; n++) { + writel_relaxed(RZT2H_ICU_DSMIFERR_MASK, priv->base_ns + RZT2H_ICU_DSMIFERR_CLRn(n)); + writel_relaxed(0, priv->base_ns + RZT2H_ICU_DSMIFERR_E0MSKn(n)); + } + + /* Clear and unmask ENCIF error events */ + for (n = 0; n < RZT2H_ICU_ENCIFERR_NUM; n++) { + writel_relaxed(RZT2H_ICU_ENCIFERR_MASK, priv->base_ns + RZT2H_ICU_ENCIFERR_CLRn(n)); + writel_relaxed(0, priv->base_ns + RZT2H_ICU_ENCIFERR_E0MSKn(n)); + } + + return 0; +} + static int rzt2h_icu_init(struct platform_device *pdev, struct device_node *parent) { struct irq_domain *irq_domain, *parent_domain; @@ -265,11 +516,20 @@ static int rzt2h_icu_init(struct platform_device *pdev, struct device_node *pare irq_domain = irq_domain_create_hierarchy(parent_domain, 0, RZT2H_ICU_NUM_IRQ, dev_fwnode(dev), &rzt2h_icu_domain_ops, priv); if (!irq_domain) { - pm_runtime_put_sync(dev); - return -ENOMEM; + ret = -ENOMEM; + goto err_pm_put; } + ret = rzt2h_icu_setup_irqs(pdev, irq_domain); + if (ret) + goto err_irq_domain_free; return 0; + +err_irq_domain_free: + irq_domain_remove(irq_domain); +err_pm_put: + pm_runtime_put_sync(dev); + return ret; } IRQCHIP_PLATFORM_DRIVER_BEGIN(rzt2h_icu) diff --git a/drivers/irqchip/irq-starfive-jh8100-intc.c b/drivers/irqchip/irq-starfive-jh8100-intc.c deleted file mode 100644 index bb62ef363d0b..000000000000 --- a/drivers/irqchip/irq-starfive-jh8100-intc.c +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * StarFive JH8100 External Interrupt Controller driver - * - * Copyright (C) 2023 StarFive Technology Co., Ltd. - * - * Author: Changhuang Liang <changhuang.liang@starfivetech.com> - */ - -#define pr_fmt(fmt) "irq-starfive-jh8100: " fmt - -#include <linux/bitops.h> -#include <linux/clk.h> -#include <linux/irq.h> -#include <linux/irqchip.h> -#include <linux/irqchip/chained_irq.h> -#include <linux/irqdomain.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/reset.h> -#include <linux/spinlock.h> - -#define STARFIVE_INTC_SRC0_CLEAR 0x10 -#define STARFIVE_INTC_SRC0_MASK 0x14 -#define STARFIVE_INTC_SRC0_INT 0x1c - -#define STARFIVE_INTC_SRC_IRQ_NUM 32 - -struct starfive_irq_chip { - void __iomem *base; - struct irq_domain *domain; - raw_spinlock_t lock; -}; - -static void starfive_intc_bit_set(struct starfive_irq_chip *irqc, - u32 reg, u32 bit_mask) -{ - u32 value; - - value = ioread32(irqc->base + reg); - value |= bit_mask; - iowrite32(value, irqc->base + reg); -} - -static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc, - u32 reg, u32 bit_mask) -{ - u32 value; - - value = ioread32(irqc->base + reg); - value &= ~bit_mask; - iowrite32(value, irqc->base + reg); -} - -static void starfive_intc_unmask(struct irq_data *d) -{ - struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); - - raw_spin_lock(&irqc->lock); - starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq)); - raw_spin_unlock(&irqc->lock); -} - -static void starfive_intc_mask(struct irq_data *d) -{ - struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); - - raw_spin_lock(&irqc->lock); - starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq)); - raw_spin_unlock(&irqc->lock); -} - -static struct irq_chip intc_dev = { - .name = "StarFive JH8100 INTC", - .irq_unmask = starfive_intc_unmask, - .irq_mask = starfive_intc_mask, -}; - -static int starfive_intc_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data, - handle_level_irq, NULL, NULL); - - return 0; -} - -static const struct irq_domain_ops starfive_intc_domain_ops = { - .xlate = irq_domain_xlate_onecell, - .map = starfive_intc_map, -}; - -static void starfive_intc_irq_handler(struct irq_desc *desc) -{ - struct starfive_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data); - struct irq_chip *chip = irq_desc_get_chip(desc); - unsigned long value; - int hwirq; - - chained_irq_enter(chip, desc); - - value = ioread32(irqc->base + STARFIVE_INTC_SRC0_INT); - while (value) { - hwirq = ffs(value) - 1; - - generic_handle_domain_irq(irqc->domain, hwirq); - - starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq)); - starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq)); - - __clear_bit(hwirq, &value); - } - - chained_irq_exit(chip, desc); -} - -static int starfive_intc_probe(struct platform_device *pdev, struct device_node *parent) -{ - struct device_node *intc = pdev->dev.of_node; - struct starfive_irq_chip *irqc; - struct reset_control *rst; - struct clk *clk; - int parent_irq; - int ret; - - irqc = kzalloc_obj(*irqc); - if (!irqc) - return -ENOMEM; - - irqc->base = of_iomap(intc, 0); - if (!irqc->base) { - pr_err("Unable to map registers\n"); - ret = -ENXIO; - goto err_free; - } - - rst = of_reset_control_get_exclusive(intc, NULL); - if (IS_ERR(rst)) { - pr_err("Unable to get reset control %pe\n", rst); - ret = PTR_ERR(rst); - goto err_unmap; - } - - clk = of_clk_get(intc, 0); - if (IS_ERR(clk)) { - pr_err("Unable to get clock %pe\n", clk); - ret = PTR_ERR(clk); - goto err_reset_put; - } - - ret = reset_control_deassert(rst); - if (ret) - goto err_clk_put; - - ret = clk_prepare_enable(clk); - if (ret) - goto err_reset_assert; - - raw_spin_lock_init(&irqc->lock); - - irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc), STARFIVE_INTC_SRC_IRQ_NUM, - &starfive_intc_domain_ops, irqc); - if (!irqc->domain) { - pr_err("Unable to create IRQ domain\n"); - ret = -EINVAL; - goto err_clk_disable; - } - - parent_irq = of_irq_get(intc, 0); - if (parent_irq < 0) { - pr_err("Failed to get main IRQ: %d\n", parent_irq); - ret = parent_irq; - goto err_remove_domain; - } - - irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler, - irqc); - - pr_info("Interrupt controller register, nr_irqs %d\n", - STARFIVE_INTC_SRC_IRQ_NUM); - - return 0; - -err_remove_domain: - irq_domain_remove(irqc->domain); -err_clk_disable: - clk_disable_unprepare(clk); -err_reset_assert: - reset_control_assert(rst); -err_clk_put: - clk_put(clk); -err_reset_put: - reset_control_put(rst); -err_unmap: - iounmap(irqc->base); -err_free: - kfree(irqc); - return ret; -} - -IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc) -IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_probe) -IRQCHIP_PLATFORM_DRIVER_END(starfive_intc) - -MODULE_DESCRIPTION("StarFive JH8100 External Interrupt Controller"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>"); diff --git a/drivers/irqchip/irq-starfive-jhb100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c new file mode 100644 index 000000000000..838885b02f34 --- /dev/null +++ b/drivers/irqchip/irq-starfive-jhb100-intc.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JHB100 External Interrupt Controller driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Changhuang Liang <changhuang.liang@starfivetech.com> + */ + +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/spinlock.h> + +#define STARFIVE_INTC_SRC_TYPE(n) (0x04 + ((n) * 0x20)) +#define STARFIVE_INTC_SRC_CLEAR(n) (0x10 + ((n) * 0x20)) +#define STARFIVE_INTC_SRC_MASK(n) (0x14 + ((n) * 0x20)) +#define STARFIVE_INTC_SRC_INT(n) (0x1c + ((n) * 0x20)) + +#define STARFIVE_INTC_TRIGGER_MASK 0x3 +#define STARFIVE_INTC_TRIGGER_HIGH 0 +#define STARFIVE_INTC_TRIGGER_LOW 1 +#define STARFIVE_INTC_TRIGGER_POSEDGE 2 +#define STARFIVE_INTC_TRIGGER_NEGEDGE 3 + +#define STARFIVE_INTC_NUM 2 +#define STARFIVE_INTC_SRC_IRQ_NUM 32 +#define STARFIVE_INTC_TYPE_NUM 16 + +struct starfive_irq_chip { + void __iomem *base; + struct irq_domain *domain; + raw_spinlock_t lock; +}; + +static void starfive_intc_mod(struct starfive_irq_chip *irqc, u32 reg, u32 mask, u32 data) +{ + u32 value; + + value = ioread32(irqc->base + reg) & ~mask; + data &= mask; + data |= value; + iowrite32(data, irqc->base + reg); +} + +static void starfive_intc_bit_set(struct starfive_irq_chip *irqc, + u32 reg, u32 bit_mask) +{ + u32 value; + + value = ioread32(irqc->base + reg); + value |= bit_mask; + iowrite32(value, irqc->base + reg); +} + +static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc, + u32 reg, u32 bit_mask) +{ + u32 value; + + value = ioread32(irqc->base + reg); + value &= ~bit_mask; + iowrite32(value, irqc->base + reg); +} + +static void starfive_intc_unmask(struct irq_data *d) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); + int i, bitpos; + + i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM; + bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM; + + guard(raw_spinlock)(&irqc->lock); + starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos)); +} + +static void starfive_intc_mask(struct irq_data *d) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); + int i, bitpos; + + i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM; + bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM; + + guard(raw_spinlock)(&irqc->lock); + starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos)); +} + +static void starfive_intc_ack(struct irq_data *d) +{ + /* for handle_edge_irq, nothing to do */ +} + +static int starfive_intc_set_type(struct irq_data *d, unsigned int type) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d); + u32 i, bitpos, ty_pos, ty_shift, trigger, typeval; + irq_flow_handler_t handler; + + i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM; + bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM; + ty_pos = bitpos / STARFIVE_INTC_TYPE_NUM; + ty_shift = (bitpos % STARFIVE_INTC_TYPE_NUM) * 2; + + switch (type) { + case IRQF_TRIGGER_LOW: + trigger = STARFIVE_INTC_TRIGGER_LOW; + handler = handle_level_irq; + break; + case IRQF_TRIGGER_HIGH: + trigger = STARFIVE_INTC_TRIGGER_HIGH; + handler = handle_level_irq; + break; + case IRQF_TRIGGER_FALLING: + trigger = STARFIVE_INTC_TRIGGER_NEGEDGE; + handler = handle_edge_irq; + break; + case IRQF_TRIGGER_RISING: + trigger = STARFIVE_INTC_TRIGGER_POSEDGE; + handler = handle_edge_irq; + break; + default: + return -EINVAL; + } + + irq_set_handler_locked(d, handler); + typeval = trigger << ty_shift; + + guard(raw_spinlock)(&irqc->lock); + + starfive_intc_mod(irqc, STARFIVE_INTC_SRC_TYPE(i) + 4 * ty_pos, + STARFIVE_INTC_TRIGGER_MASK << ty_shift, typeval); + + /* Once the type is updated, clear interrupt can help to reset the type value */ + starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos)); + starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos)); + + return 0; +} + +static struct irq_chip intc_dev = { + .name = "StarFive JHB100 INTC", + .irq_unmask = starfive_intc_unmask, + .irq_mask = starfive_intc_mask, + .irq_ack = starfive_intc_ack, + .irq_set_type = starfive_intc_set_type, +}; + +static int starfive_intc_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data, + handle_level_irq, NULL, NULL); + + return 0; +} + +static const struct irq_domain_ops starfive_intc_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = starfive_intc_map, +}; + +static void starfive_intc_irq_handler(struct irq_desc *desc) +{ + struct starfive_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long value; + int hwirq; + + chained_irq_enter(chip, desc); + + for (int i = 0; i < STARFIVE_INTC_NUM; i++) { + value = ioread32(irqc->base + STARFIVE_INTC_SRC_INT(i)); + while (value) { + hwirq = ffs(value) - 1; + + generic_handle_domain_irq(irqc->domain, + hwirq + i * STARFIVE_INTC_SRC_IRQ_NUM); + + starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq)); + starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq)); + + __clear_bit(hwirq, &value); + } + } + + chained_irq_exit(chip, desc); +} + +static int starfive_intc_probe(struct platform_device *pdev, struct device_node *parent) +{ + struct device_node *intc = pdev->dev.of_node; + struct reset_control *rst; + struct clk *clk; + int parent_irq; + + struct starfive_irq_chip *irqc __free(kfree) = kzalloc_obj(*irqc); + if (!irqc) + return -ENOMEM; + + irqc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(irqc->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(irqc->base), "unable to map registers\n"); + + rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Unable to get and deassert reset control\n"); + + clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), "Unable to get and enable clock\n"); + + + raw_spin_lock_init(&irqc->lock); + + irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc), + STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM, + &starfive_intc_domain_ops, irqc); + if (!irqc->domain) + return dev_err_probe(&pdev->dev, -EINVAL, "Unable to create IRQ domain\n"); + + parent_irq = of_irq_get(intc, 0); + if (parent_irq < 0) { + irq_domain_remove(irqc->domain); + return dev_err_probe(&pdev->dev, parent_irq, "Failed to get main IRQ\n"); + } + + irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler, + irqc); + + dev_info(&pdev->dev, "Interrupt controller register, nr_irqs %d\n", + STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM); + + retain_and_null_ptr(irqc); + return 0; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc) +IRQCHIP_MATCH("starfive,jhb100-intc", starfive_intc_probe) +IRQCHIP_PLATFORM_DRIVER_END(starfive_intc) + +MODULE_DESCRIPTION("StarFive JHB100 External Interrupt Controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>"); diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index 32b77fa93f73..2014dbb0bc43 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -3,6 +3,7 @@ * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. */ +#include <linux/bitfield.h> #include <linux/err.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -21,22 +22,30 @@ #include <linux/types.h> #define PDC_MAX_GPIO_IRQS 256 -#define PDC_DRV_OFFSET 0x10000 +#define PDC_DRV_SIZE 0x10000 /* Valid only on HW version < 3.2 */ #define IRQ_ENABLE_BANK 0x10 #define IRQ_ENABLE_BANK_MAX (IRQ_ENABLE_BANK + BITS_TO_BYTES(PDC_MAX_GPIO_IRQS)) +#define IRQ_ENABLE_BANK_INDEX_MASK GENMASK(31, 5) +#define IRQ_ENABLE_BANK_BIT_MASK GENMASK(4, 0) #define IRQ_i_CFG 0x110 /* Valid only on HW version >= 3.2 */ #define IRQ_i_CFG_IRQ_ENABLE 3 -#define IRQ_i_CFG_TYPE_MASK GENMASK(2, 0) +#define IRQ_i_CFG_TYPE_MASK GENMASK(2, 0) -#define PDC_VERSION_REG 0x1000 +#define PDC_VERSION_REG 0x1000 +#define PDC_VERSION_MAJOR GENMASK(23, 16) +#define PDC_VERSION_MINOR GENMASK(15, 8) +#define PDC_VERSION_STEP GENMASK(7, 0) +#define PDC_VERSION(maj, min, step) (FIELD_PREP(PDC_VERSION_MAJOR, (maj)) | \ + FIELD_PREP(PDC_VERSION_MINOR, (min)) | \ + FIELD_PREP(PDC_VERSION_STEP, (step))) /* Notable PDC versions */ -#define PDC_VERSION_3_2 0x30200 +#define PDC_VERSION_3_2 PDC_VERSION(3, 2, 0) struct pdc_pin_region { u32 pin_base; @@ -97,28 +106,37 @@ static void pdc_x1e_irq_enable_write(u32 bank, u32 enable) pdc_base_reg_write(base, IRQ_ENABLE_BANK, bank, enable); } -static void __pdc_enable_intr(int pin_out, bool on) +static void pdc_enable_intr_bank(int pin_out, bool on) { unsigned long enable; + u32 index, mask; - if (pdc_version < PDC_VERSION_3_2) { - u32 index, mask; + index = FIELD_GET(IRQ_ENABLE_BANK_INDEX_MASK, pin_out); + mask = FIELD_GET(IRQ_ENABLE_BANK_BIT_MASK, pin_out); - index = pin_out / 32; - mask = pin_out % 32; + enable = pdc_reg_read(IRQ_ENABLE_BANK, index); + __assign_bit(mask, &enable, on); - enable = pdc_reg_read(IRQ_ENABLE_BANK, index); - __assign_bit(mask, &enable, on); + if (pdc_x1e_quirk) + pdc_x1e_irq_enable_write(index, enable); + else + pdc_reg_write(IRQ_ENABLE_BANK, index, enable); +} - if (pdc_x1e_quirk) - pdc_x1e_irq_enable_write(index, enable); - else - pdc_reg_write(IRQ_ENABLE_BANK, index, enable); - } else { - enable = pdc_reg_read(IRQ_i_CFG, pin_out); - __assign_bit(IRQ_i_CFG_IRQ_ENABLE, &enable, on); - pdc_reg_write(IRQ_i_CFG, pin_out, enable); - } +static void pdc_enable_intr_cfg(int pin_out, bool on) +{ + unsigned long enable = pdc_reg_read(IRQ_i_CFG, pin_out); + + __assign_bit(IRQ_i_CFG_IRQ_ENABLE, &enable, on); + pdc_reg_write(IRQ_i_CFG, pin_out, enable); +} + +static void __pdc_enable_intr(int pin_out, bool on) +{ + if (pdc_version < PDC_VERSION_3_2) + pdc_enable_intr_bank(pin_out, on); + else + pdc_enable_intr_cfg(pin_out, on); } static void pdc_enable_intr(struct irq_data *d, bool on) @@ -348,7 +366,6 @@ static int pdc_setup_pin_mapping(struct device_node *np) return 0; } -#define QCOM_PDC_SIZE 0x30000 static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *parent) { @@ -362,7 +379,7 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare if (of_address_to_resource(node, 0, &res)) return -EINVAL; - res_size = max_t(resource_size_t, resource_size(&res), QCOM_PDC_SIZE); + res_size = max_t(resource_size_t, resource_size(&res), PDC_DRV_SIZE); if (res_size > resource_size(&res)) pr_warn("%pOF: invalid reg size, please fix DT\n", node); @@ -375,7 +392,7 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare * region with the expected offset to preserve support for old DTs. */ if (of_device_is_compatible(node, "qcom,x1e80100-pdc")) { - pdc_prev_base = ioremap(res.start - PDC_DRV_OFFSET, IRQ_ENABLE_BANK_MAX); + pdc_prev_base = ioremap(res.start - PDC_DRV_SIZE, IRQ_ENABLE_BANK_MAX); if (!pdc_prev_base) { pr_err("%pOF: unable to map previous PDC DRV region\n", node); return -ENXIO; diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index 8a1e2c08b096..26cb93191ede 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -87,7 +87,7 @@ static inline u64 get_cpu_idle_time(unsigned int cpu) kcpustat->cpustat[CPUTIME_IOWAIT]; if (rackmeter_ignore_nice) - retval += kcpustat_field(kcpustat, CPUTIME_NICE, cpu); + retval += kcpustat_field(CPUTIME_NICE, cpu); return retval; } diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index a3fcdca7e6db..df27c7d066d2 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -315,6 +315,17 @@ config DM_CRYPT If unsure, say N. +config DM_INLINECRYPT + tristate "Inline encryption target support" + depends on BLK_DEV_DM + depends on (KEYS || KEYS=n) + depends on BLK_INLINE_ENCRYPTION + help + This device-mapper target is similar to dm-crypt, but it uses the + blk-crypto API instead of the regular crypto API. This allows it to + take advantage of inline encryption hardware such as that commonly + built into UFS host controllers. + config DM_SNAPSHOT tristate "Snapshot target" depends on BLK_DEV_DM diff --git a/drivers/md/Makefile b/drivers/md/Makefile index c338cc6fbe2e..517d1f7d8288 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_DM_UNSTRIPED) += dm-unstripe.o obj-$(CONFIG_DM_BUFIO) += dm-bufio.o obj-$(CONFIG_DM_BIO_PRISON) += dm-bio-prison.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o +obj-$(CONFIG_DM_INLINECRYPT) += dm-inlinecrypt.o obj-$(CONFIG_DM_DELAY) += dm-delay.o obj-$(CONFIG_DM_DUST) += dm-dust.o obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o diff --git a/drivers/md/dm-cache-policy-smq.c b/drivers/md/dm-cache-policy-smq.c index dd77a93fd68d..7411a88885b8 100644 --- a/drivers/md/dm-cache-policy-smq.c +++ b/drivers/md/dm-cache-policy-smq.c @@ -22,6 +22,16 @@ /*----------------------------------------------------------------*/ /* + * Maximum number of concurrent background work items (promotions, + * demotions, writebacks) that can be queued in the background tracker. + * Tuneable via the module parameter smq_max_background_work. + * Only affects newly created cache devices. + */ +static unsigned int smq_max_background_work = 4096; +module_param(smq_max_background_work, uint, 0644); +MODULE_PARM_DESC(smq_max_background_work, "Max concurrent background work items"); + +/* * Safe division functions that return zero on divide by zero. */ static unsigned int safe_div(unsigned int n, unsigned int d) @@ -1590,18 +1600,22 @@ static int smq_invalidate_mapping(struct dm_cache_policy *p, dm_cblock_t cblock) struct smq_policy *mq = to_smq_policy(p); struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); unsigned long flags; - - if (!e->allocated) - return -ENODATA; + int r = 0; spin_lock_irqsave(&mq->lock, flags); + if (!e->allocated) { + r = -ENODATA; + goto out; + } // FIXME: what if this block has pending background work? del_queue(mq, e); h_remove(&mq->table, e); free_entry(&mq->cache_alloc, e); + +out: spin_unlock_irqrestore(&mq->lock, flags); - return 0; + return r; } static uint32_t smq_get_hint(struct dm_cache_policy *p, dm_cblock_t cblock) @@ -1816,7 +1830,7 @@ __smq_create(dm_cblock_t cache_size, sector_t origin_size, sector_t cache_block_ mq->next_hotspot_period = jiffies; mq->next_cache_period = jiffies; - mq->bg_work = btracker_create(4096); /* FIXME: hard coded value */ + mq->bg_work = btracker_create(max(1u, smq_max_background_work)); if (!mq->bg_work) goto bad_btracker; diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c index 9495ca035056..5e7232582feb 100644 --- a/drivers/md/dm-ima.c +++ b/drivers/md/dm-ima.c @@ -21,25 +21,32 @@ * character, so that they don't interfere with the construction of key-value pairs, * and clients can split the key1=val1,key2=val2,key3=val3; pairs properly. */ -static void fix_separator_chars(char **buf) +static void fix_separator_chars(char *buf) { - int l = strlen(*buf); + int l = strlen(buf); int i, j, sp = 0; for (i = 0; i < l; i++) - if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',') + if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',') sp++; if (!sp) return; + buf[l + sp] = '\0'; for (i = l-1, j = i+sp; i >= 0; i--) { - (*buf)[j--] = (*buf)[i]; - if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',') - (*buf)[j--] = '\\'; + buf[j--] = buf[i]; + if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',') + buf[j--] = '\\'; } } +static void fix_context_strings(struct dm_ima_context *context) +{ + fix_separator_chars(context->dev_name); + fix_separator_chars(context->dev_uuid); +} + /* * Internal function to allocate memory for IMA measurements. */ @@ -59,68 +66,85 @@ static void *dm_ima_alloc(size_t len, bool noio) return ptr; } -/* - * Internal function to allocate and copy name and uuid for IMA measurements. - */ -static int dm_ima_alloc_and_copy_name_uuid(struct mapped_device *md, char **dev_name, - char **dev_uuid, bool noio) +void dm_ima_init(struct mapped_device *md) { - int r; - *dev_name = dm_ima_alloc(DM_NAME_LEN*2, noio); - if (!(*dev_name)) { - r = -ENOMEM; - goto error; - } + md->ima.update_idx = 0; + md->ima.measure_idx = 0; + init_waitqueue_head(&md->ima.ima_wq); + spin_lock_init(&md->ima.ima_lock); +} - *dev_uuid = dm_ima_alloc(DM_UUID_LEN*2, noio); - if (!(*dev_uuid)) { - r = -ENOMEM; - goto error; - } +void dm_ima_alloc_context(struct dm_ima_context **context, bool noio) +{ + *context = dm_ima_alloc(sizeof(struct dm_ima_context), noio); +} - r = dm_copy_name_and_uuid(md, *dev_name, *dev_uuid); - if (r) - goto error; +void dm_ima_free_context(struct dm_ima_context *context) +{ + if (likely(context)) { + kfree(context->table.device_metadata); + kfree(context->table.hash); + kfree(context); + } +} - fix_separator_chars(dev_name); - fix_separator_chars(dev_uuid); +static void wait_to_measure(struct dm_ima_measurements *ima, + unsigned int update_idx) +{ + spin_lock_irq(&ima->ima_lock); + wait_event_lock_irq(ima->ima_wq, + ima->measure_idx == update_idx, + ima->ima_lock); + spin_unlock_irq(&ima->ima_lock); +} - return 0; -error: - kfree(*dev_name); - kfree(*dev_uuid); - *dev_name = NULL; - *dev_uuid = NULL; - return r; +static void wake_next_measure(struct dm_ima_measurements *ima) +{ + spin_lock_irq(&ima->ima_lock); + ima->measure_idx++; + spin_unlock_irq(&ima->ima_lock); + wake_up_all(&ima->ima_wq); } /* - * Internal function to allocate and copy device data for IMA measurements. + * Helper function for swapping the table, to make sure that the + * correct table metadata is saved and restored. */ -static int dm_ima_alloc_and_copy_device_data(struct mapped_device *md, char **device_data, - unsigned int num_targets, bool noio) +void dm_ima_context_table_op(struct mapped_device *md, + struct dm_ima_context *context, + enum dm_ima_table_op op) { - char *dev_name = NULL, *dev_uuid = NULL; - int r; + struct dm_ima_measurements *ima = &md->ima; - r = dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio); - if (r) - return r; + if (unlikely(!context)) + return; - *device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio); - if (!(*device_data)) { - r = -ENOMEM; - goto error; + wait_to_measure(ima, context->update_idx); + + if (op == DM_IMA_TABLE_SAVE) { + context->table = ima->inactive_table; + memset(&ima->inactive_table, 0, sizeof(ima->inactive_table)); + } else { + ima->inactive_table = context->table; + memset(&context->table, 0, sizeof(context->table)); } - scnprintf(*device_data, DM_IMA_DEVICE_BUF_LEN, + wake_next_measure(ima); +} + +/* + * Internal function to copy device data for IMA measurements. + */ +static void dm_ima_copy_device_data(struct mapped_device *md, char *device_data, + struct dm_ima_context *context, + unsigned int num_targets) +{ + memset(device_data, 0, DM_IMA_DEVICE_BUF_LEN); + scnprintf(device_data, DM_IMA_DEVICE_BUF_LEN, "name=%s,uuid=%s,major=%d,minor=%d,minor_count=%d,num_targets=%u;", - dev_name, dev_uuid, md->disk->major, md->disk->first_minor, - md->disk->minors, num_targets); -error: - kfree(dev_name); - kfree(dev_uuid); - return r; + context->dev_name, context->dev_uuid, md->disk->major, + md->disk->first_minor, md->disk->minors, num_targets); + } /* @@ -141,42 +165,21 @@ static void dm_ima_measure_data(const char *event_name, const void *buf, size_t memalloc_noio_restore(noio_flag); } -/* - * Internal function to allocate and copy current device capacity for IMA measurements. - */ -static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **capacity_str, - bool noio) -{ - sector_t capacity; - - capacity = get_capacity(md->disk); - - *capacity_str = dm_ima_alloc(DM_IMA_DEVICE_CAPACITY_BUF_LEN, noio); - if (!(*capacity_str)) - return -ENOMEM; - - return scnprintf(*capacity_str, DM_IMA_DEVICE_BUF_LEN, "current_device_capacity=%llu;", - capacity); -} - -/* - * Initialize/reset the dm ima related data structure variables. - */ -void dm_ima_reset_data(struct mapped_device *md) +static sector_t dm_ima_capacity(struct mapped_device *md) { - memset(&(md->ima), 0, sizeof(md->ima)); - md->ima.dm_version_str_len = strlen(DM_IMA_VERSION_STR); + return (md->ima.active_table.device_metadata) ? + md->ima.active_table.capacity : get_capacity(md->disk); } /* * Build up the IMA data for each target, and finally measure. */ -void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) +void dm_ima_measure_on_table_load(struct dm_table *table, + struct dm_ima_context *context) { size_t device_data_buf_len, target_metadata_buf_len, target_data_buf_len, l = 0; char *target_metadata_buf = NULL, *target_data_buf = NULL, *digest_buf = NULL; char *ima_buf = NULL, *device_data_buf = NULL; - int last_target_measured = -1; status_type_t type = STATUSTYPE_IMA; size_t cur_total_buf_len = 0; unsigned int num_targets, i; @@ -185,9 +188,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl bool noio = false; char table_load_event_name[] = "dm_table_load"; + if (unlikely(!context)) + return; + + wait_to_measure(&table->md->ima, context->update_idx); + ima_buf = dm_ima_alloc(DM_IMA_MEASUREMENT_BUF_LEN, noio); if (!ima_buf) - return; + goto error; target_metadata_buf = dm_ima_alloc(DM_IMA_TARGET_METADATA_BUF_LEN, noio); if (!target_metadata_buf) @@ -199,13 +207,18 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl num_targets = table->num_targets; - if (dm_ima_alloc_and_copy_device_data(table->md, &device_data_buf, num_targets, noio)) + device_data_buf = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio); + if (!device_data_buf) goto error; + fix_context_strings(context); + dm_ima_copy_device_data(table->md, device_data_buf, context, + num_targets); + sha256_init(&hash_ctx); - memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len); - l += table->md->ima.dm_version_str_len; + memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR)); + l += strlen(DM_IMA_VERSION_STR); device_data_buf_len = strlen(device_data_buf); memcpy(ima_buf + l, device_data_buf, device_data_buf_len); @@ -214,8 +227,6 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl for (i = 0; i < num_targets; i++) { struct dm_target *ti = dm_table_get_target(table, i); - last_target_measured = 0; - /* * First retrieve the target metadata. */ @@ -229,7 +240,7 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl * Then retrieve the actual target data. */ if (ti->type->status) - ti->type->status(ti, type, status_flags, target_data_buf, + ti->type->status(ti, type, 0, target_data_buf, DM_IMA_TARGET_DATA_BUF_LEN); else target_data_buf[0] = '\0'; @@ -260,19 +271,11 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl * prefix, so that multiple records from the same "dm_table_load" for * a given device can be linked together. */ - memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len); - l += table->md->ima.dm_version_str_len; + memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR)); + l += strlen(DM_IMA_VERSION_STR); memcpy(ima_buf + l, device_data_buf, device_data_buf_len); l += device_data_buf_len; - - /* - * If this iteration of the for loop turns out to be the last target - * in the table, dm_ima_measure_data("dm_table_load", ...) doesn't need - * to be called again, just the hash needs to be finalized. - * "last_target_measured" tracks this state. - */ - last_target_measured = 1; } /* @@ -286,11 +289,8 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl l += target_data_buf_len; } - if (!last_target_measured) { - dm_ima_measure_data(table_load_event_name, ima_buf, l, noio); - - sha256_update(&hash_ctx, (const u8 *)ima_buf, l); - } + dm_ima_measure_data(table_load_event_name, ima_buf, l, noio); + sha256_update(&hash_ctx, (const u8 *)ima_buf, l); /* * Finalize the table hash, and store it in table->md->ima.inactive_table.hash, @@ -304,17 +304,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl if (!digest_buf) goto error; - if (table->md->ima.active_table.hash != table->md->ima.inactive_table.hash) - kfree(table->md->ima.inactive_table.hash); - + kfree(table->md->ima.inactive_table.hash); table->md->ima.inactive_table.hash = digest_buf; table->md->ima.inactive_table.hash_len = strlen(digest_buf); table->md->ima.inactive_table.num_targets = num_targets; + table->md->ima.inactive_table.capacity = dm_table_get_size(table); - if (table->md->ima.active_table.device_metadata != - table->md->ima.inactive_table.device_metadata) - kfree(table->md->ima.inactive_table.device_metadata); + kfree(table->md->ima.inactive_table.device_metadata); table->md->ima.inactive_table.device_metadata = device_data_buf; table->md->ima.inactive_table.device_metadata_len = device_data_buf_len; @@ -326,66 +323,55 @@ exit: kfree(ima_buf); kfree(target_metadata_buf); kfree(target_data_buf); + + wake_next_measure(&table->md->ima); } /* * Measure IMA data on device resume. */ -void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) +void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap, + struct dm_ima_context *context) { - char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL; + char *device_table_data = NULL; char active[] = "active_table_hash="; unsigned int active_len = strlen(active); unsigned int l = 0; bool noio = true; bool nodata = true; - int capacity_len; - device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio); - if (!device_table_data) + if (unlikely(!context)) return; - capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio); - if (capacity_len < 0) - goto error; - - memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len); - l += md->ima.dm_version_str_len; + wait_to_measure(&md->ima, context->update_idx); if (swap) { - if (md->ima.active_table.hash != md->ima.inactive_table.hash) - kfree(md->ima.active_table.hash); - - md->ima.active_table.hash = NULL; - md->ima.active_table.hash_len = 0; - - if (md->ima.active_table.device_metadata != - md->ima.inactive_table.device_metadata) - kfree(md->ima.active_table.device_metadata); - - md->ima.active_table.device_metadata = NULL; - md->ima.active_table.device_metadata_len = 0; - md->ima.active_table.num_targets = 0; - - if (md->ima.inactive_table.hash) { - md->ima.active_table.hash = md->ima.inactive_table.hash; - md->ima.active_table.hash_len = md->ima.inactive_table.hash_len; - md->ima.inactive_table.hash = NULL; - md->ima.inactive_table.hash_len = 0; - } - - if (md->ima.inactive_table.device_metadata) { - md->ima.active_table.device_metadata = - md->ima.inactive_table.device_metadata; - md->ima.active_table.device_metadata_len = - md->ima.inactive_table.device_metadata_len; - md->ima.active_table.num_targets = md->ima.inactive_table.num_targets; - md->ima.inactive_table.device_metadata = NULL; - md->ima.inactive_table.device_metadata_len = 0; - md->ima.inactive_table.num_targets = 0; + kfree(md->ima.active_table.hash); + kfree(md->ima.active_table.device_metadata); + md->ima.active_table = context->table; + memset(&context->table, 0, sizeof(context->table)); + if (md->ima.active_table.device_metadata) { + /* + * A rename could have happened while the swap was + * going on. In that case, the saved table info would + * still have the old name. Update the metadata to be + * sure that it has the current name + */ + struct dm_ima_device_table_metadata *table = &md->ima.active_table; + fix_context_strings(context); + dm_ima_copy_device_data(md, table->device_metadata, + context, table->num_targets); + table->device_metadata_len = strlen(table->device_metadata); } } + device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio); + if (!device_table_data) + goto error; + + memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR)); + l += strlen(DM_IMA_VERSION_STR); + if (md->ima.active_table.device_metadata) { memcpy(device_table_data + l, md->ima.active_table.device_metadata, md->ima.active_table.device_metadata_len); @@ -409,59 +395,54 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) } if (nodata) { - if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio)) - goto error; - + fix_context_strings(context); l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN, "%sname=%s,uuid=%s;device_resume=no_data;", - DM_IMA_VERSION_STR, dev_name, dev_uuid); + DM_IMA_VERSION_STR, context->dev_name, + context->dev_uuid); } - - memcpy(device_table_data + l, capacity_str, capacity_len); - l += capacity_len; + l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l, + "current_device_capacity=%llu;", dm_ima_capacity(md)); dm_ima_measure_data("dm_device_resume", device_table_data, l, noio); - kfree(dev_name); - kfree(dev_uuid); error: - kfree(capacity_str); kfree(device_table_data); + + wake_next_measure(&md->ima); } /* * Measure IMA data on remove. */ -void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) +void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all, + struct dm_ima_context *context, + unsigned int idx) { - char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL; + char *device_table_data; char active_table_str[] = "active_table_hash="; char inactive_table_str[] = "inactive_table_hash="; char device_active_str[] = "device_active_metadata="; char device_inactive_str[] = "device_inactive_metadata="; - char remove_all_str[] = "remove_all="; unsigned int active_table_len = strlen(active_table_str); unsigned int inactive_table_len = strlen(inactive_table_str); unsigned int device_active_len = strlen(device_active_str); unsigned int device_inactive_len = strlen(device_inactive_str); - unsigned int remove_all_len = strlen(remove_all_str); unsigned int l = 0; bool noio = true; bool nodata = true; - int capacity_len; - device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, noio); - if (!device_table_data) + wait_to_measure(&md->ima, idx); + + if (unlikely(!context)) goto exit; - capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio); - if (capacity_len < 0) { - kfree(device_table_data); + device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, noio); + if (!device_table_data) goto exit; - } - memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len); - l += md->ima.dm_version_str_len; + memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR)); + l += strlen(DM_IMA_VERSION_STR); if (md->ima.active_table.device_metadata) { memcpy(device_table_data + l, device_active_str, device_active_len); @@ -518,68 +499,57 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) * in IMA measurements. */ if (nodata) { - if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio)) - goto error; - + fix_context_strings(context); l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN, "%sname=%s,uuid=%s;device_remove=no_data;", - DM_IMA_VERSION_STR, dev_name, dev_uuid); + DM_IMA_VERSION_STR, context->dev_name, + context->dev_uuid); } - memcpy(device_table_data + l, remove_all_str, remove_all_len); - l += remove_all_len; - memcpy(device_table_data + l, remove_all ? "y;" : "n;", 2); - l += 2; - - memcpy(device_table_data + l, capacity_str, capacity_len); - l += capacity_len; + l += scnprintf(device_table_data + l, (DM_IMA_DEVICE_BUF_LEN * 2) - l, + "remove_all=%c;current_device_capacity=%llu;", + remove_all ? 'y' : 'n', dm_ima_capacity(md)); dm_ima_measure_data("dm_device_remove", device_table_data, l, noio); -error: kfree(device_table_data); - kfree(capacity_str); exit: kfree(md->ima.active_table.device_metadata); - - if (md->ima.active_table.device_metadata != - md->ima.inactive_table.device_metadata) - kfree(md->ima.inactive_table.device_metadata); + kfree(md->ima.inactive_table.device_metadata); kfree(md->ima.active_table.hash); + kfree(md->ima.inactive_table.hash); - if (md->ima.active_table.hash != md->ima.inactive_table.hash) - kfree(md->ima.inactive_table.hash); - - dm_ima_reset_data(md); + memset(&md->ima.active_table, 0, sizeof(md->ima.active_table)); + memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table)); - kfree(dev_name); - kfree(dev_uuid); + wake_next_measure(&md->ima); } /* * Measure ima data on table clear. */ -void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) +void dm_ima_measure_on_table_clear(struct mapped_device *md, + struct dm_ima_context *context) { unsigned int l = 0; - char *device_table_data = NULL, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL; + char *device_table_data = NULL; char inactive_str[] = "inactive_table_hash="; unsigned int inactive_len = strlen(inactive_str); bool noio = true; bool nodata = true; - int capacity_len; - device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio); - if (!device_table_data) + if (unlikely(!context)) return; - capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio); - if (capacity_len < 0) - goto error1; + wait_to_measure(&md->ima, context->update_idx); - memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len); - l += md->ima.dm_version_str_len; + device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio); + if (!device_table_data) + goto error; + + memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR)); + l += strlen(DM_IMA_VERSION_STR); if (md->ima.inactive_table.device_metadata_len && md->ima.inactive_table.hash_len) { @@ -602,101 +572,79 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) } if (nodata) { - if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio)) - goto error2; - + fix_context_strings(context); l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN, "%sname=%s,uuid=%s;table_clear=no_data;", - DM_IMA_VERSION_STR, dev_name, dev_uuid); + DM_IMA_VERSION_STR, context->dev_name, + context->dev_uuid); } - memcpy(device_table_data + l, capacity_str, capacity_len); - l += capacity_len; + l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l, + "current_device_capacity=%llu;", dm_ima_capacity(md)); dm_ima_measure_data("dm_table_clear", device_table_data, l, noio); - if (new_map) { - if (md->ima.inactive_table.hash && - md->ima.inactive_table.hash != md->ima.active_table.hash) - kfree(md->ima.inactive_table.hash); - - md->ima.inactive_table.hash = NULL; - md->ima.inactive_table.hash_len = 0; - - if (md->ima.inactive_table.device_metadata && - md->ima.inactive_table.device_metadata != md->ima.active_table.device_metadata) - kfree(md->ima.inactive_table.device_metadata); - - md->ima.inactive_table.device_metadata = NULL; - md->ima.inactive_table.device_metadata_len = 0; - md->ima.inactive_table.num_targets = 0; - - if (md->ima.active_table.hash) { - md->ima.inactive_table.hash = md->ima.active_table.hash; - md->ima.inactive_table.hash_len = md->ima.active_table.hash_len; - } - - if (md->ima.active_table.device_metadata) { - md->ima.inactive_table.device_metadata = - md->ima.active_table.device_metadata; - md->ima.inactive_table.device_metadata_len = - md->ima.active_table.device_metadata_len; - md->ima.inactive_table.num_targets = - md->ima.active_table.num_targets; - } - } +error: + kfree(md->ima.inactive_table.hash); + kfree(md->ima.inactive_table.device_metadata); + memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table)); - kfree(dev_name); - kfree(dev_uuid); -error2: - kfree(capacity_str); -error1: kfree(device_table_data); + + wake_next_measure(&md->ima); } /* * Measure IMA data on device rename. */ -void dm_ima_measure_on_device_rename(struct mapped_device *md) +void dm_ima_measure_on_device_rename(struct mapped_device *md, + struct dm_ima_context *context) { - char *old_device_data = NULL, *new_device_data = NULL, *combined_device_data = NULL; - char *new_dev_name = NULL, *new_dev_uuid = NULL, *capacity_str = NULL; + char *old_device_data = NULL; + char *combined_device_data = NULL; bool noio = true; int len; + struct dm_ima_device_table_metadata *table; - if (dm_ima_alloc_and_copy_device_data(md, &new_device_data, - md->ima.active_table.num_targets, noio)) + if (unlikely(!context)) return; - if (dm_ima_alloc_and_copy_name_uuid(md, &new_dev_name, &new_dev_uuid, noio)) - goto error; + wait_to_measure(&md->ima, context->update_idx); + + fix_context_strings(context); combined_device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN * 2, noio); if (!combined_device_data) - goto error; - - if (dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio) < 0) - goto error; - - old_device_data = md->ima.active_table.device_metadata; - - md->ima.active_table.device_metadata = new_device_data; - md->ima.active_table.device_metadata_len = strlen(new_device_data); + goto exit; + if (md->ima.active_table.device_metadata) + old_device_data = md->ima.active_table.device_metadata; + else if (md->ima.inactive_table.device_metadata) + old_device_data = md->ima.inactive_table.device_metadata; + else + old_device_data = "device_rename=no_data;"; len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2, - "%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data, - new_dev_name, new_dev_uuid, capacity_str); + "%s%snew_name=%s,new_uuid=%s;current_device_capacity=%llu;", + DM_IMA_VERSION_STR, old_device_data, context->dev_name, + context->dev_uuid, dm_ima_capacity(md)); dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio); + kfree(combined_device_data); - goto exit; - -error: - kfree(new_device_data); exit: - kfree(capacity_str); - kfree(combined_device_data); - kfree(old_device_data); - kfree(new_dev_name); - kfree(new_dev_uuid); + if (md->ima.active_table.device_metadata) { + table = &md->ima.active_table; + dm_ima_copy_device_data(md, table->device_metadata, context, + table->num_targets); + table->device_metadata_len = strlen(table->device_metadata); + } + + if (md->ima.inactive_table.device_metadata) { + table = &md->ima.inactive_table; + dm_ima_copy_device_data(md, table->device_metadata, context, + table->num_targets); + table->device_metadata_len = strlen(table->device_metadata); + } + + wake_next_measure(&md->ima); } diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h index a403deca6093..0ec013d1545c 100644 --- a/drivers/md/dm-ima.h +++ b/drivers/md/dm-ima.h @@ -24,6 +24,11 @@ __dm_ima_str(DM_VERSION_MINOR) "." \ __dm_ima_str(DM_VERSION_PATCHLEVEL) ";" +enum dm_ima_table_op { + DM_IMA_TABLE_SAVE, + DM_IMA_TABLE_RESTORE, +}; + #ifdef CONFIG_IMA struct dm_ima_device_table_metadata { @@ -36,6 +41,7 @@ struct dm_ima_device_table_metadata { char *device_metadata; unsigned int device_metadata_len; unsigned int num_targets; + sector_t capacity; /* * Contains the sha256 hashes of the IMA measurements of the target @@ -45,31 +51,67 @@ struct dm_ima_device_table_metadata { unsigned int hash_len; }; +struct dm_ima_context { + struct dm_ima_device_table_metadata table; + unsigned int update_idx; + char dev_name[DM_NAME_LEN*2]; + char dev_uuid[DM_UUID_LEN*2]; +}; + /* * This structure contains device metadata, and table hash for * active and inactive tables for ima measurements. */ struct dm_ima_measurements { + unsigned int update_idx; + unsigned int measure_idx; + struct wait_queue_head ima_wq; + spinlock_t ima_lock; struct dm_ima_device_table_metadata active_table; struct dm_ima_device_table_metadata inactive_table; - unsigned int dm_version_str_len; }; -void dm_ima_reset_data(struct mapped_device *md); -void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags); -void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap); -void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all); -void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map); -void dm_ima_measure_on_device_rename(struct mapped_device *md); +void dm_ima_init(struct mapped_device *md); +void dm_ima_alloc_context(struct dm_ima_context **context, bool noio); +void dm_ima_free_context(struct dm_ima_context *context); +void dm_ima_context_table_op(struct mapped_device *md, + struct dm_ima_context *context, + enum dm_ima_table_op op); +void dm_ima_measure_on_table_load(struct dm_table *table, + struct dm_ima_context *context); +void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap, + struct dm_ima_context *context); +void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all, + struct dm_ima_context *context, + unsigned int idx); +void dm_ima_measure_on_table_clear(struct mapped_device *md, + struct dm_ima_context *context); +void dm_ima_measure_on_device_rename(struct mapped_device *md, + struct dm_ima_context *context); #else -static inline void dm_ima_reset_data(struct mapped_device *md) {} -static inline void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) {} -static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) {} -static inline void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) {} -static inline void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) {} -static inline void dm_ima_measure_on_device_rename(struct mapped_device *md) {} +struct dm_ima_context; + +static inline void dm_ima_init(struct mapped_device *md) {} +static inline void dm_ima_alloc_context(struct dm_ima_context **context, bool noio) {} +static inline void dm_ima_free_context(struct dm_ima_context *context) {} +static inline void dm_ima_context_table_op(struct mapped_device *md, + struct dm_ima_context *context, + enum dm_ima_table_op op) {} +static inline void dm_ima_measure_on_table_load(struct dm_table *table, + struct dm_ima_context *context) {} +static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, + bool swap, + struct dm_ima_context *context) {} +static inline void dm_ima_measure_on_device_remove(struct mapped_device *md, + bool remove_all, + struct dm_ima_context *context, + unsigned int idx) {} +static inline void dm_ima_measure_on_table_clear(struct mapped_device *md, + struct dm_ima_context *context) {} +static inline void dm_ima_measure_on_device_rename(struct mapped_device *md, + struct dm_ima_context *context) {} #endif /* CONFIG_IMA */ diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c new file mode 100644 index 000000000000..be1b4aa8f28b --- /dev/null +++ b/drivers/md/dm-inlinecrypt.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2024 Google LLC + */ + +#include <linux/blk-crypto.h> +#include <linux/ctype.h> +#include <linux/device-mapper.h> +#include <linux/hex.h> +#include <linux/module.h> +#include <keys/user-type.h> + +#define DM_MSG_PREFIX "inlinecrypt" + +static const struct dm_inlinecrypt_cipher { + const char *name; + enum blk_crypto_mode_num mode_num; +} dm_inlinecrypt_ciphers[] = { + { + .name = "aes-xts-plain64", + .mode_num = BLK_ENCRYPTION_MODE_AES_256_XTS, + }, +}; + +/** + * struct inlinecrypt_ctx - private data of an inlinecrypt target + * @dev: the underlying device + * @start: starting sector of the range of @dev which this target actually maps. + * For this purpose a "sector" is 512 bytes. + * @cipher_string: the name of the encryption algorithm being used + * @key_size: size of the encryption key in bytes + * @iv_offset: starting offset for IVs. IVs are generated as if the target were + * preceded by @iv_offset 512-byte sectors. + * @sector_size: crypto sector size in bytes (usually 4096) + * @sector_bits: log2(sector_size) + * @key_type: type of the key -- either raw or hardware-wrapped + * @key: the encryption key to use + * @max_dun: the maximum DUN that may be used (computed from other params) + */ +struct inlinecrypt_ctx { + struct dm_dev *dev; + sector_t start; + const char *cipher_string; + unsigned int key_size; + u64 iv_offset; + unsigned int sector_size; + unsigned int sector_bits; + enum blk_crypto_key_type key_type; + struct blk_crypto_key key; + u64 max_dun; +}; + +static const struct dm_inlinecrypt_cipher * +lookup_cipher(const char *cipher_string) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dm_inlinecrypt_ciphers); i++) { + if (strcmp(cipher_string, dm_inlinecrypt_ciphers[i].name) == 0) + return &dm_inlinecrypt_ciphers[i]; + } + return NULL; +} + +static void inlinecrypt_dtr(struct dm_target *ti) +{ + struct inlinecrypt_ctx *ctx = ti->private; + + if (ctx->dev) { + if (ctx->key.size) + blk_crypto_evict_key(ctx->dev->bdev, &ctx->key); + dm_put_device(ti, ctx->dev); + } + kfree_sensitive(ctx->cipher_string); + kfree_sensitive(ctx); +} + +#ifdef CONFIG_KEYS + +static bool contains_whitespace(const char *str) +{ + while (*str) + if (isspace(*str++)) + return true; + return false; +} + +static int set_key_user(struct key *key, char *key_bytes, + const unsigned int key_bytes_size) +{ + const struct user_key_payload *ukp; + + ukp = user_key_payload_locked(key); + if (!ukp) + return -EKEYREVOKED; + + if (key_bytes_size != ukp->datalen) + return -EINVAL; + + memcpy(key_bytes, ukp->data, key_bytes_size); + + return 0; +} + +static int inlinecrypt_get_keyring_key(const char *key_string, u8 *key_bytes, + const unsigned int key_bytes_size) +{ + char *key_desc; + int ret; + struct key_type *type; + struct key *key; + int (*set_key)(struct key *key, char *key_bytes, + const unsigned int key_bytes_size); + + /* + * Reject key_string with whitespace. dm core currently lacks code for + * proper whitespace escaping in arguments on DM_TABLE_STATUS path. + */ + if (contains_whitespace(key_string)) { + DMERR("whitespace chars not allowed in key string"); + return -EINVAL; + } + + /* look for next ':' separating key_type from key_description */ + key_desc = strchr(key_string, ':'); + if (!key_desc || key_desc == key_string || !strlen(key_desc + 1)) + return -EINVAL; + + if (!strncmp(key_string, "logon:", key_desc - key_string + 1)) { + type = &key_type_logon; + set_key = set_key_user; + } else { + return -EINVAL; + } + + key = request_key(type, key_desc + 1, NULL); + if (IS_ERR(key)) + return PTR_ERR(key); + + down_read(&key->sem); + + ret = set_key(key, (char *)key_bytes, key_bytes_size); + + up_read(&key->sem); + key_put(key); + + return ret; +} + +static int get_key_size(char **key_string) +{ + char *colon, dummy; + int ret; + + if (*key_string[0] != ':') { + ret = strlen(*key_string); + + if (ret > 2 * BLK_CRYPTO_MAX_ANY_KEY_SIZE + || ret % 2 + || !ret) { + DMERR("Invalid keysize"); + return -EINVAL; + } + return ret >> 1; + } + + /* look for next ':' in key string */ + colon = strpbrk(*key_string + 1, ":"); + if (!colon) + return -EINVAL; + + if (sscanf(*key_string + 1, "%u%c", &ret, &dummy) != 2 || dummy != ':') + return -EINVAL; + + /* remaining key string should be :<logon|user>:<key_desc> */ + *key_string = colon; + + return ret; +} + +#else + +static int inlinecrypt_get_keyring_key(const char *key_string, u8 *key_bytes, + const unsigned int key_bytes_size) +{ + return -EINVAL; +} + +static int get_key_size(char **key_string) +{ + int key_hex_size = strlen(*key_string); + + if (*key_string[0] == ':') + return -EINVAL; + + if (key_hex_size > 2 * BLK_CRYPTO_MAX_ANY_KEY_SIZE + || key_hex_size % 2 + || !key_hex_size) { + DMERR("Invalid keysize"); + return -EINVAL; + } + + return key_hex_size >> 1; +} + +#endif /* CONFIG_KEYS */ + +static int inlinecrypt_get_key(const char *key_string, + u8 key[BLK_CRYPTO_MAX_ANY_KEY_SIZE], + const unsigned int key_size) +{ + int ret = 0; + + if (key_size > BLK_CRYPTO_MAX_ANY_KEY_SIZE) { + DMERR("Invalid keysize"); + return -EINVAL; + } + + /* ':' means the key is in kernel keyring, short-circuit normal key processing */ + if (key_string[0] == ':') { + /* key string should be :<logon|user>:<key_desc> */ + ret = inlinecrypt_get_keyring_key(key_string + 1, key, key_size); + goto out; + } + + if (hex2bin(key, key_string, key_size) != 0) + ret = -EINVAL; + +out: + return ret; +} + +static int inlinecrypt_ctr_optional(struct dm_target *ti, + unsigned int argc, char **argv) +{ + struct inlinecrypt_ctx *ctx = ti->private; + struct dm_arg_set as; + static const struct dm_arg _args[] = { + {0, 4, "Invalid number of feature args"}, + }; + unsigned int opt_params; + const char *opt_string; + bool iv_large_sectors = false; + char dummy; + int err; + + as.argc = argc; + as.argv = argv; + + err = dm_read_arg_group(_args, &as, &opt_params, &ti->error); + if (err) + return err; + + while (opt_params--) { + opt_string = dm_shift_arg(&as); + if (!opt_string) { + ti->error = "Not enough feature arguments"; + return -EINVAL; + } + if (str_has_prefix(opt_string, "keytype:")) { + const char *val = opt_string + strlen("keytype:"); + + if (!*val) { + ti->error = "Invalid block key type"; + return -EINVAL; + } + + if (!strcmp(val, "raw")) { + ctx->key_type = BLK_CRYPTO_KEY_TYPE_RAW; + } else if (!strcmp(val, "hw-wrapped")) { + ctx->key_type = BLK_CRYPTO_KEY_TYPE_HW_WRAPPED; + } else { + ti->error = "Invalid block key type"; + return -EINVAL; + } + } else if (!strcmp(opt_string, "allow_discards")) { + ti->num_discard_bios = 1; + } else if (sscanf(opt_string, "sector_size:%u%c", + &ctx->sector_size, &dummy) == 1) { + if (ctx->sector_size < SECTOR_SIZE || + ctx->sector_size > 4096 || + !is_power_of_2(ctx->sector_size)) { + ti->error = "Invalid sector_size"; + return -EINVAL; + } + } else if (!strcmp(opt_string, "iv_large_sectors")) { + iv_large_sectors = true; + } else { + ti->error = "Invalid feature arguments"; + return -EINVAL; + } + } + + /* dm-inlinecrypt doesn't implement iv_large_sectors=false. */ + if (ctx->sector_size != SECTOR_SIZE && !iv_large_sectors) { + ti->error = "iv_large_sectors must be specified"; + return -EINVAL; + } + + return 0; +} + +/* + * Construct an inlinecrypt mapping: + * <cipher> [<key>|:<key_size>:<logon>:<key_description>] <iv_offset> <dev_path> <start> + * + * This syntax matches dm-crypt's, but the set of supported functionality has + * been stripped down. + */ +static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) +{ + struct inlinecrypt_ctx *ctx; + const struct dm_inlinecrypt_cipher *cipher; + u8 key_bytes[BLK_CRYPTO_MAX_ANY_KEY_SIZE]; + unsigned int dun_bytes; + unsigned long long tmpll; + char dummy; + int err; + + if (argc < 5) { + ti->error = "Not enough arguments"; + return -EINVAL; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ti->error = "Out of memory"; + return -ENOMEM; + } + ti->private = ctx; + + /* <cipher> */ + ctx->cipher_string = kstrdup(argv[0], GFP_KERNEL); + if (!ctx->cipher_string) { + ti->error = "Out of memory"; + err = -ENOMEM; + goto bad; + } + cipher = lookup_cipher(ctx->cipher_string); + if (!cipher) { + ti->error = "Unsupported cipher"; + err = -EINVAL; + goto bad; + } + + /* <key> */ + err = get_key_size(&argv[1]); + if (err < 0) { + ti->error = "Cannot parse key size"; + return -EINVAL; + } + ctx->key_size = err; + + err = inlinecrypt_get_key(argv[1], key_bytes, ctx->key_size); + if (err) { + ti->error = "Malformed key string"; + goto bad; + } + + /* <iv_offset> */ + if (sscanf(argv[2], "%llu%c", &ctx->iv_offset, &dummy) != 1) { + ti->error = "Invalid iv_offset sector"; + err = -EINVAL; + goto bad; + } + + /* <dev_path> */ + err = dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), + &ctx->dev); + if (err) { + ti->error = "Device lookup failed"; + goto bad; + } + + /* <start> */ + if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1 || + tmpll != (sector_t)tmpll) { + ti->error = "Invalid start sector"; + err = -EINVAL; + goto bad; + } + ctx->start = tmpll; + + /* optional arguments */ + ctx->sector_size = SECTOR_SIZE; + ctx->key_type = BLK_CRYPTO_KEY_TYPE_RAW; + if (argc > 5) { + err = inlinecrypt_ctr_optional(ti, argc - 5, &argv[5]); + if (err) + goto bad; + } + ctx->sector_bits = ilog2(ctx->sector_size); + if (ti->len & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) { + ti->error = "Device size is not a multiple of sector_size"; + err = -EINVAL; + goto bad; + } + if (ctx->iv_offset & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) { + ti->error = "Wrong alignment of iv_offset sector"; + err = -EINVAL; + } + + ctx->max_dun = (ctx->iv_offset + ti->len - 1) >> + (ctx->sector_bits - SECTOR_SHIFT); + dun_bytes = DIV_ROUND_UP(fls64(ctx->max_dun), 8); + + err = blk_crypto_init_key(&ctx->key, key_bytes, ctx->key_size, + ctx->key_type, cipher->mode_num, + dun_bytes, ctx->sector_size); + if (err) { + ti->error = "Error initializing blk-crypto key"; + goto bad; + } + + err = blk_crypto_start_using_key(ctx->dev->bdev, &ctx->key); + if (err) { + ti->error = "Error starting to use blk-crypto"; + goto bad; + } + + ti->num_flush_bios = 1; + + err = 0; + goto out; + +bad: + inlinecrypt_dtr(ti); +out: + memzero_explicit(key_bytes, sizeof(key_bytes)); + return err; +} + +static int inlinecrypt_map(struct dm_target *ti, struct bio *bio) +{ + const struct inlinecrypt_ctx *ctx = ti->private; + sector_t sector_in_target; + u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = {}; + + bio_set_dev(bio, ctx->dev->bdev); + + /* + * If the bio is a device-level request which doesn't target a specific + * sector, there's nothing more to do. + */ + if (bio_sectors(bio) == 0) + return DM_MAPIO_REMAPPED; + + /* + * The bio should never have an encryption context already, since + * dm-inlinecrypt doesn't pass through any inline encryption + * capabilities to the layer above it. + */ + if (WARN_ON_ONCE(bio_has_crypt_ctx(bio))) + return DM_MAPIO_KILL; + + /* Map the bio's sector to the underlying device. (512-byte sectors) */ + sector_in_target = dm_target_offset(ti, bio->bi_iter.bi_sector); + bio->bi_iter.bi_sector = ctx->start + sector_in_target; + /* + * If the bio doesn't have any data (e.g. if it's a DISCARD request), + * there's nothing more to do. + */ + if (!bio_has_data(bio)) + return DM_MAPIO_REMAPPED; + + /* Calculate the DUN and enforce data-unit (crypto sector) alignment. */ + dun[0] = ctx->iv_offset + sector_in_target; /* 512-byte sectors */ + if (dun[0] & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) + return DM_MAPIO_KILL; + dun[0] >>= ctx->sector_bits - SECTOR_SHIFT; /* crypto sectors */ + + /* + * This check isn't necessary as we should have calculated max_dun + * correctly, but be safe. + */ + if (WARN_ON_ONCE(dun[0] > ctx->max_dun)) + return DM_MAPIO_KILL; + + bio_crypt_set_ctx(bio, &ctx->key, dun, GFP_NOIO); + + /* + * Since we've added an encryption context to the bio and + * blk-crypto-fallback may be needed to process it, it's necessary to + * use the fallback-aware bio submission code rather than + * unconditionally returning DM_MAPIO_REMAPPED. + * + * To get the correct accounting for a dm target in the case where + * __blk_crypto_submit_bio() doesn't take ownership of the bio (returns + * true), call __blk_crypto_submit_bio() directly and return + * DM_MAPIO_REMAPPED in that case, rather than relying on + * blk_crypto_submit_bio() which calls submit_bio() in that case. + * + * TODO: blk-crypto fallback write slow-path currently double-accounts + * IO in vmstat, as encrypted bios are submitted via submit_bio(). + * This does not affect data correctness. Consider fixing this if + * a cleaner accounting model for derived bios is introduced. + */ + if (__blk_crypto_submit_bio(bio)) + return DM_MAPIO_REMAPPED; + return DM_MAPIO_SUBMITTED; +} + +static void inlinecrypt_status(struct dm_target *ti, status_type_t type, + unsigned int status_flags, char *result, + unsigned int maxlen) +{ + const struct inlinecrypt_ctx *ctx = ti->private; + unsigned int sz = 0; + int num_feature_args = 0; + + switch (type) { + case STATUSTYPE_INFO: + case STATUSTYPE_IMA: + result[0] = '\0'; + break; + + case STATUSTYPE_TABLE: + /* + * Warning: like dm-crypt, dm-inlinecrypt includes the key in + * the returned table. Userspace is responsible for redacting + * the key when needed. + */ + DMEMIT("%s %*phN %u %llu %s %llu", ctx->cipher_string, + ctx->key.size, ctx->key.bytes, + ctx->key_type, ctx->iv_offset, + ctx->dev->name, ctx->start); + num_feature_args += !!ti->num_discard_bios; + if (ctx->sector_size != SECTOR_SIZE) + num_feature_args += 2; + if (num_feature_args != 0) { + DMEMIT(" %d", num_feature_args); + if (ti->num_discard_bios) + DMEMIT(" allow_discards"); + if (ctx->sector_size != SECTOR_SIZE) { + DMEMIT(" sector_size:%u", ctx->sector_size); + DMEMIT(" iv_large_sectors"); + } + } + break; + } +} + +static int inlinecrypt_prepare_ioctl(struct dm_target *ti, + struct block_device **bdev, unsigned int cmd, + unsigned long arg, bool *forward) +{ + const struct inlinecrypt_ctx *ctx = ti->private; + const struct dm_dev *dev = ctx->dev; + + *bdev = dev->bdev; + + /* Only pass ioctls through if the device sizes match exactly. */ + return ctx->start != 0 || ti->len != bdev_nr_sectors(dev->bdev); +} + +static int inlinecrypt_iterate_devices(struct dm_target *ti, + iterate_devices_callout_fn fn, + void *data) +{ + const struct inlinecrypt_ctx *ctx = ti->private; + + return fn(ti, ctx->dev, ctx->start, ti->len, data); +} + +#ifdef CONFIG_BLK_DEV_ZONED +static int inlinecrypt_report_zones(struct dm_target *ti, + struct dm_report_zones_args *args, + unsigned int nr_zones) +{ + const struct inlinecrypt_ctx *ctx = ti->private; + + return dm_report_zones(ctx->dev->bdev, ctx->start, + ctx->start + dm_target_offset(ti, args->next_sector), + args, nr_zones); +} +#else +#define inlinecrypt_report_zones NULL +#endif + +static void inlinecrypt_io_hints(struct dm_target *ti, + struct queue_limits *limits) +{ + const struct inlinecrypt_ctx *ctx = ti->private; + const unsigned int sector_size = ctx->sector_size; + + limits->logical_block_size = + max_t(unsigned int, limits->logical_block_size, sector_size); + limits->physical_block_size = + max_t(unsigned int, limits->physical_block_size, sector_size); + limits->io_min = max_t(unsigned int, limits->io_min, sector_size); + limits->dma_alignment = limits->logical_block_size - 1; +} + +static struct target_type inlinecrypt_target = { + .name = "inlinecrypt", + .version = {1, 0, 0}, + /* + * Do not set DM_TARGET_PASSES_CRYPTO, since dm-inlinecrypt consumes the + * crypto capability itself. + */ + .features = DM_TARGET_ZONED_HM, + .module = THIS_MODULE, + .ctr = inlinecrypt_ctr, + .dtr = inlinecrypt_dtr, + .map = inlinecrypt_map, + .status = inlinecrypt_status, + .prepare_ioctl = inlinecrypt_prepare_ioctl, + .iterate_devices = inlinecrypt_iterate_devices, + .report_zones = inlinecrypt_report_zones, + .io_hints = inlinecrypt_io_hints, +}; + +module_dm(inlinecrypt); + +MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>"); +MODULE_AUTHOR("Linlin Zhang <linlin.zhang@oss.qualcomm.com>"); +MODULE_DESCRIPTION(DM_NAME " target for inline encryption"); +MODULE_LICENSE("GPL"); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a529174c94cf..ac77dc0ca225 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -259,6 +259,80 @@ static void free_cell(struct hash_cell *hc) } } +#ifdef CONFIG_IMA + +/* + * Called while holding to _hash_lock, to guarantee the ordering of the + * following dm_ima_measure_on_* functions, which should be called + * right after dropping the _hash_lock + */ +static unsigned int dm_ima_init_context(struct hash_cell *hc, + struct dm_ima_context *context, + bool need_idx) +{ + lockdep_assert_held(&_hash_lock); + + if (unlikely(!context)) + return need_idx ? hc->md->ima.update_idx++ : 0; + + context->update_idx = hc->md->ima.update_idx++; + strcpy(context->dev_name, hc->name); + strcpy(context->dev_uuid, hc->uuid ? : ""); + + return context->update_idx; +} + +/* + * Called by do_resume() to guarantee correct ordering, since do_resume() + * does not grab the _hash_lock when the table is not getting swapped or + * when actually swapping the active table + */ +static bool dm_ima_need_measure(struct mapped_device *md, + struct dm_table *table, + struct dm_ima_context *context) +{ + int srcu_idx; + struct hash_cell *hc; + bool need_measure = false; + + if (unlikely(!context)) + return false; + + down_write(&_hash_lock); + /* Check if the device has been removed */ + hc = dm_get_mdptr(md); + if (hc) { + /* + * If we have a table, we need to make sure that it's the + * active table. Otherwise we raced with another process + * setting the active table and it will do the measurement + */ + if (!table || dm_get_live_table(md, &srcu_idx) == table) { + dm_ima_init_context(hc, context, false); + need_measure = true; + } + if (table) + dm_put_live_table(md, srcu_idx); + } + up_write(&_hash_lock); + + return need_measure; +} +#else +static inline unsigned int dm_ima_init_context(struct hash_cell *hc, + struct dm_ima_context *context, + bool neex_idx) +{ + return 0; +} +static inline bool dm_ima_need_measure(struct mapped_device *md, + struct dm_table *table, + struct dm_ima_context *context) +{ + return false; +} +#endif + /* * The kdev_t and uuid of a device can never change once it is * initially inserted. @@ -344,7 +418,10 @@ static int dm_hash_remove_all(unsigned flags) struct hash_cell *hc; struct mapped_device *md; struct dm_table *t; + struct dm_ima_context *ima_context = NULL; + unsigned int ima_idx; + dm_ima_alloc_context(&ima_context, true); retry: dev_skipped = 0; @@ -353,6 +430,7 @@ retry: for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) { if (flags & DM_REMOVE_INTERRUPTIBLE && fatal_signal_pending(current)) { up_write(&_hash_lock); + dm_ima_free_context(ima_context); return -EINTR; } @@ -367,6 +445,7 @@ retry: continue; } + ima_idx = dm_ima_init_context(hc, ima_context, true); t = __hash_remove(hc); up_write(&_hash_lock); @@ -375,7 +454,7 @@ retry: dm_sync_table(md); dm_table_destroy(t); } - dm_ima_measure_on_device_remove(md, true); + dm_ima_measure_on_device_remove(md, true, ima_context, ima_idx); dm_put(md); if (likely(flags & DM_REMOVE_KEEP_OPEN_DEVICES)) dm_destroy(md); @@ -396,6 +475,7 @@ retry: if (dev_skipped && !(flags & DM_REMOVE_ONLY_DEFERRED)) DMWARN("remove_all left %d open device(s)", dev_skipped); + dm_ima_free_context(ima_context); return 0; } @@ -443,6 +523,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, struct mapped_device *md; unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; int srcu_idx; + struct dm_ima_context *ima_context = NULL; /* * duplicate new. @@ -451,6 +532,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, if (!new_data) return ERR_PTR(-ENOMEM); + dm_ima_alloc_context(&ima_context, true); down_write(&_hash_lock); /* @@ -467,6 +549,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, param->name, new); dm_put(hc->md); up_write(&_hash_lock); + dm_ima_free_context(ima_context); kfree(new_data); return ERR_PTR(-EBUSY); } @@ -479,6 +562,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, DMERR("Unable to rename non-existent device, %s to %s%s", param->name, change_uuid ? "uuid " : "", new); up_write(&_hash_lock); + dm_ima_free_context(ima_context); kfree(new_data); return ERR_PTR(-ENXIO); } @@ -492,6 +576,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, param->name, new, hc->uuid); dm_put(hc->md); up_write(&_hash_lock); + dm_ima_free_context(ima_context); kfree(new_data); return ERR_PTR(-EINVAL); } @@ -514,9 +599,11 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, md = hc->md; - dm_ima_measure_on_device_rename(md); + dm_ima_init_context(hc, ima_context, false); up_write(&_hash_lock); + dm_ima_measure_on_device_rename(md, ima_context); + dm_ima_free_context(ima_context); kfree(old_name); return md; @@ -995,13 +1082,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si struct mapped_device *md; int r; struct dm_table *t; + struct dm_ima_context *ima_context = NULL; + unsigned int ima_idx; + dm_ima_alloc_context(&ima_context, true); down_write(&_hash_lock); hc = __find_device_hash_cell(param); if (!hc) { DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); up_write(&_hash_lock); + dm_ima_free_context(ima_context); return -ENXIO; } @@ -1015,14 +1106,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) { up_write(&_hash_lock); dm_put(md); + dm_ima_free_context(ima_context); return 0; } DMDEBUG_LIMIT("unable to remove open device %s", hc->name); up_write(&_hash_lock); dm_put(md); + dm_ima_free_context(ima_context); return r; } + ima_idx = dm_ima_init_context(hc, ima_context, true); t = __hash_remove(hc); up_write(&_hash_lock); @@ -1033,7 +1127,8 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si param->flags &= ~DM_DEFERRED_REMOVE; - dm_ima_measure_on_device_remove(md, false); + dm_ima_measure_on_device_remove(md, false, ima_context, ima_idx); + dm_ima_free_context(ima_context); if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false)) param->flags |= DM_UEVENT_GENERATED_FLAG; @@ -1169,13 +1264,16 @@ static int do_resume(struct dm_ioctl *param) struct mapped_device *md; struct dm_table *new_map, *old_map = NULL; bool need_resize_uevent = false; + struct dm_ima_context *ima_context = NULL; + dm_ima_alloc_context(&ima_context, true); down_write(&_hash_lock); hc = __find_device_hash_cell(param); if (!hc) { DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); up_write(&_hash_lock); + dm_ima_free_context(ima_context); return -ENXIO; } @@ -1184,13 +1282,15 @@ static int do_resume(struct dm_ioctl *param) new_map = hc->new_map; hc->new_map = NULL; param->flags &= ~DM_INACTIVE_PRESENT_FLAG; - + if (new_map) + dm_ima_init_context(hc, ima_context, false); up_write(&_hash_lock); /* Do we need to load a new map ? */ if (new_map) { sector_t old_size, new_size; + dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_SAVE); /* Suspend if it isn't already suspended */ if (param->flags & DM_SKIP_LOCKFS_FLAG) suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; @@ -1204,6 +1304,8 @@ static int do_resume(struct dm_ioctl *param) if (hc && !hc->new_map) { hc->new_map = new_map; new_map = NULL; + dm_ima_init_context(hc, ima_context, + false); } else { r = -ENXIO; } @@ -1211,7 +1313,9 @@ static int do_resume(struct dm_ioctl *param) if (new_map) { dm_sync_table(md); dm_table_destroy(new_map); - } + } else + dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_RESTORE); + dm_ima_free_context(ima_context); dm_put(md); return r; } @@ -1222,9 +1326,12 @@ static int do_resume(struct dm_ioctl *param) if (IS_ERR(old_map)) { dm_sync_table(md); dm_table_destroy(new_map); + dm_ima_free_context(ima_context); dm_put(md); return PTR_ERR(old_map); } + if (dm_ima_need_measure(md, new_map, ima_context)) + dm_ima_measure_on_device_resume(md, true, ima_context); new_size = dm_get_size(md); if (old_size && new_size && old_size != new_size) need_resize_uevent = true; @@ -1238,7 +1345,10 @@ static int do_resume(struct dm_ioctl *param) if (dm_suspended_md(md)) { r = dm_resume(md); if (!r) { - dm_ima_measure_on_device_resume(md, new_map ? true : false); + if (!new_map && dm_ima_need_measure(md, NULL, + ima_context)) + dm_ima_measure_on_device_resume(md, false, + ima_context); if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent)) param->flags |= DM_UEVENT_GENERATED_FLAG; @@ -1255,6 +1365,7 @@ static int do_resume(struct dm_ioctl *param) if (!r) __dev_status(md, param); + dm_ima_free_context(ima_context); dm_put(md); return r; } @@ -1532,11 +1643,12 @@ static bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new) static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size) { - int r; + int r, srcu_idx; struct hash_cell *hc; struct dm_table *t, *old_map = NULL; struct mapped_device *md; struct target_type *immutable_target_type; + struct dm_ima_context *ima_context = NULL; md = find_device(param); if (!md) @@ -1552,8 +1664,6 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si if (r) goto err_unlock_md_type; - dm_ima_measure_on_table_load(t, STATUSTYPE_IMA); - immutable_target_type = dm_get_immutable_target_type(md); if (immutable_target_type && (immutable_target_type != dm_table_get_immutable_target_type(t)) && @@ -1580,12 +1690,14 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si dm_unlock_md_type(md); + dm_ima_alloc_context(&ima_context, false); /* stage inactive table */ down_write(&_hash_lock); hc = dm_get_mdptr(md); if (!hc) { DMERR("device has been removed from the dev hash table."); up_write(&_hash_lock); + dm_ima_free_context(ima_context); r = -ENXIO; goto err_destroy_table; } @@ -1593,8 +1705,15 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si if (hc->new_map) old_map = hc->new_map; hc->new_map = t; + dm_ima_init_context(hc, ima_context, false); + /* Make sure new_map doesn't get freed before we measure it*/ + dm_get_live_table(md, &srcu_idx); up_write(&_hash_lock); + dm_ima_measure_on_table_load(t, ima_context); + dm_ima_free_context(ima_context); + dm_put_live_table(md, srcu_idx); + param->flags |= DM_INACTIVE_PRESENT_FLAG; __dev_status(md, param); @@ -1622,25 +1741,29 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s struct hash_cell *hc; struct mapped_device *md; struct dm_table *old_map = NULL; - bool has_new_map = false; + struct dm_ima_context *ima_context = NULL; + dm_ima_alloc_context(&ima_context, true); down_write(&_hash_lock); hc = __find_device_hash_cell(param); if (!hc) { DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); up_write(&_hash_lock); + dm_ima_free_context(ima_context); return -ENXIO; } if (hc->new_map) { old_map = hc->new_map; hc->new_map = NULL; - has_new_map = true; } + dm_ima_init_context(hc, ima_context, false); md = hc->md; up_write(&_hash_lock); + dm_ima_measure_on_table_clear(md, ima_context); + dm_ima_free_context(ima_context); param->flags &= ~DM_INACTIVE_PRESENT_FLAG; __dev_status(md, param); @@ -1649,7 +1772,6 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s dm_sync_table(md); dm_table_destroy(old_map); } - dm_ima_measure_on_table_clear(md, has_new_map); dm_put(md); return 0; @@ -1816,8 +1938,11 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para goto out_argv; table = dm_get_live_table(md, &srcu_idx); - if (!table) + if (!table) { + DMERR("The device has no table."); + r = -EINVAL; goto out_table; + } if (dm_deleting_md(md)) { r = -ENXIO; diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index c5dc083c7244..8f5a5e1342a9 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -3831,6 +3831,7 @@ static void raid_presuspend(struct dm_target *ti) * resume, raid_postsuspend() is too late. */ set_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags); + set_bit(MD_DM_SUSPENDING, &mddev->flags); if (!reshape_interrupted(mddev)) return; @@ -3847,13 +3848,16 @@ static void raid_presuspend(struct dm_target *ti) static void raid_presuspend_undo(struct dm_target *ti) { struct raid_set *rs = ti->private; + struct mddev *mddev = &rs->md; + clear_bit(MD_DM_SUSPENDING, &mddev->flags); clear_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags); } static void raid_postsuspend(struct dm_target *ti) { struct raid_set *rs = ti->private; + struct mddev *mddev = &rs->md; if (!test_and_set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags)) { /* @@ -3864,6 +3868,8 @@ static void raid_postsuspend(struct dm_target *ti) mddev_suspend(&rs->md, false); rs->md.ro = MD_RDONLY; } + clear_bit(MD_DM_SUSPENDING, &mddev->flags); + } static void attempt_restore_of_faulty_devices(struct raid_set *rs) diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c index 9703b3ae364e..9a386254d836 100644 --- a/drivers/md/dm-rq.c +++ b/drivers/md/dm-rq.c @@ -462,7 +462,7 @@ static void dm_start_request(struct mapped_device *md, struct request *orig) } static int dm_mq_init_request(struct blk_mq_tag_set *set, struct request *rq, - unsigned int hctx_idx, unsigned int numa_node) + unsigned int hctx_idx, int numa_node) { struct mapped_device *md = set->driver_data; struct dm_rq_target_io *tio = blk_mq_rq_to_pdu(rq); diff --git a/drivers/md/dm-vdo/indexer/index-layout.c b/drivers/md/dm-vdo/indexer/index-layout.c index 5f4ce4ab1b1e..2d529250000e 100644 --- a/drivers/md/dm-vdo/indexer/index-layout.c +++ b/drivers/md/dm-vdo/indexer/index-layout.c @@ -282,7 +282,7 @@ static void create_unique_nonce_data(u8 *buffer) u32 rand; size_t offset = 0; - get_random_bytes(&rand, sizeof(u32)); + rand = get_random_u32(); memcpy(buffer + offset, &now, sizeof(now)); offset += sizeof(now); memcpy(buffer + offset, &rand, sizeof(rand)); diff --git a/drivers/md/dm-vdo/vdo.c b/drivers/md/dm-vdo/vdo.c index 7bec2418c121..d0d4e0262be2 100644 --- a/drivers/md/dm-vdo/vdo.c +++ b/drivers/md/dm-vdo/vdo.c @@ -965,7 +965,7 @@ static int __must_check clear_partition(struct vdo *vdo, enum partition_id id) return blkdev_issue_zeroout(vdo_get_backing_device(vdo), partition->offset * VDO_SECTORS_PER_BLOCK, partition->count * VDO_SECTORS_PER_BLOCK, - GFP_NOWAIT, 0); + GFP_NOIO, 0); } int vdo_clear_layout(struct vdo *vdo) @@ -976,7 +976,7 @@ int vdo_clear_layout(struct vdo *vdo) result = blkdev_issue_zeroout(vdo_get_backing_device(vdo), VDO_SECTORS_PER_BLOCK, VDO_SECTORS_PER_BLOCK, - GFP_NOWAIT, 0); + GFP_NOIO, 0); if (result != VDO_SUCCESS) return result; diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index ec605acddd88..f4f81c79a080 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -2871,7 +2871,7 @@ int dmz_ctr_metadata(struct dmz_dev *dev, int num_dev, if (!zmd) return -ENOMEM; - strcpy(zmd->devname, devname); + strscpy(zmd->devname, devname); zmd->dev = dev; zmd->nr_devs = num_dev; zmd->mblk_rbtree = RB_ROOT; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index e178fe19973e..7287bed6eb64 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2098,8 +2098,17 @@ static bool dm_poll_dm_io(struct dm_io *io, struct io_comp_batch *iob, WARN_ON_ONCE(!dm_tio_is_normal(&io->tio)); /* don't poll if the mapped io is done */ - if (atomic_read(&io->io_count) > 1) - bio_poll(&io->tio.clone, iob, flags); + if (atomic_read(&io->io_count) > 1) { + /* + * DM hides the target queues from the upper poller, which may + * decide it is safe to spin on a single stacked queue. Do not + * pass that spinning policy down to a target queue: one slow + * clone could keep the task inside dm_poll_bio() for a long + * time. Poll target bios once and let the caller decide + * whether to keep polling, reap completions or reschedule. + */ + bio_poll(&io->tio.clone, iob, flags | BLK_POLL_ONESHOT); + } /* bio_poll holds the last reference */ return atomic_read(&io->io_count) == 1; @@ -2546,7 +2555,7 @@ int dm_create(int minor, struct mapped_device **result) if (!md) return -ENXIO; - dm_ima_reset_data(md); + dm_ima_init(md); *result = md; return 0; diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 028b9ca8ce52..7d778fe1c47c 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -502,6 +502,18 @@ static void write_sb_page(struct bitmap *bitmap, unsigned long pg_index, static void md_bitmap_file_kick(struct bitmap *bitmap); #ifdef CONFIG_MD_BITMAP_FILE +static void end_bitmap_write(struct bio *bio) +{ + struct buffer_head *bh; + bool uptodate = bio_endio_bh(bio, &bh); + struct bitmap *bitmap = bh->b_private; + + if (!uptodate) + set_bit(BITMAP_WRITE_ERROR, &bitmap->flags); + if (atomic_dec_and_test(&bitmap->pending_writes)) + wake_up(&bitmap->write_wait); +} + static void write_file_page(struct bitmap *bitmap, struct page *page, int wait) { struct buffer_head *bh = page_buffers(page); @@ -510,7 +522,7 @@ static void write_file_page(struct bitmap *bitmap, struct page *page, int wait) atomic_inc(&bitmap->pending_writes); set_buffer_locked(bh); set_buffer_mapped(bh); - submit_bh(REQ_OP_WRITE | REQ_SYNC, bh); + bh_submit(bh, REQ_OP_WRITE | REQ_SYNC, end_bitmap_write); bh = bh->b_this_page; } @@ -519,16 +531,6 @@ static void write_file_page(struct bitmap *bitmap, struct page *page, int wait) atomic_read(&bitmap->pending_writes) == 0); } -static void end_bitmap_write(struct buffer_head *bh, int uptodate) -{ - struct bitmap *bitmap = bh->b_private; - - if (!uptodate) - set_bit(BITMAP_WRITE_ERROR, &bitmap->flags); - if (atomic_dec_and_test(&bitmap->pending_writes)) - wake_up(&bitmap->write_wait); -} - static void free_buffers(struct page *page) { struct buffer_head *bh; @@ -592,12 +594,11 @@ static int read_file_page(struct file *file, unsigned long index, else count -= blocksize; - bh->b_end_io = end_bitmap_write; bh->b_private = bitmap; atomic_inc(&bitmap->pending_writes); set_buffer_locked(bh); set_buffer_mapped(bh); - submit_bh(REQ_OP_READ, bh); + bh_submit(bh, REQ_OP_READ, end_bitmap_write); } blk_cur++; bh = bh->b_this_page; diff --git a/drivers/md/md.c b/drivers/md/md.c index 8b568eee8743..096bb64e87bd 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -395,17 +395,24 @@ static bool is_suspended(struct mddev *mddev, struct bio *bio) bool md_handle_request(struct mddev *mddev, struct bio *bio) { check_suspended: - if (is_suspended(mddev, bio)) { - /* Bail out if REQ_NOWAIT is set for the bio */ - if (bio->bi_opf & REQ_NOWAIT) { - bio_wouldblock_error(bio); - return true; + if (unlikely(md_cloned_bio(mddev, bio))) { + /* + * This bio is an MD cloned bio and already holds an + * active_io reference, so percpu_ref_get() is safe here. + */ + percpu_ref_get(&mddev->active_io); + } else { + if (is_suspended(mddev, bio)) { + /* Bail out if REQ_NOWAIT is set for the bio */ + if (bio->bi_opf & REQ_NOWAIT) { + bio_wouldblock_error(bio); + return true; + } + wait_event(mddev->sb_wait, !is_suspended(mddev, bio)); } - wait_event(mddev->sb_wait, !is_suspended(mddev, bio)); + if (!percpu_ref_tryget_live(&mddev->active_io)) + goto check_suspended; } - if (!percpu_ref_tryget_live(&mddev->active_io)) - goto check_suspended; - if (!mddev->pers->make_request(mddev, bio)) { percpu_ref_put(&mddev->active_io); if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend) @@ -4414,9 +4421,10 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len) err = mddev_suspend_and_lock(mddev); if (err) return err; - if (mddev->pers) - err = update_raid_disks(mddev, n); - else if (mddev->reshape_position != MaxSector) { + if (mddev->pers) { + if (n != mddev->raid_disks) + err = update_raid_disks(mddev, n); + } else if (mddev->reshape_position != MaxSector) { struct md_rdev *rdev; int olddisks = mddev->raid_disks - mddev->delta_disks; diff --git a/drivers/md/md.h b/drivers/md/md.h index 52c378086046..d8daf0f75cbb 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -346,6 +346,7 @@ struct md_cluster_operations; * @MD_HAS_SUPERBLOCK: There is persistence sb in member disks. * @MD_FAILLAST_DEV: Allow last rdev to be removed. * @MD_SERIALIZE_POLICY: Enforce write IO is not reordered, just used by raid1. + * @MD_DM_SUSPENDING: This DM raid device is suspending. * * change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added */ @@ -365,6 +366,7 @@ enum mddev_flags { MD_HAS_SUPERBLOCK, MD_FAILLAST_DEV, MD_SERIALIZE_POLICY, + MD_DM_SUSPENDING, }; enum mddev_sb_flags { @@ -1042,6 +1044,11 @@ void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes); extern const struct block_device_operations md_fops; +static inline bool md_cloned_bio(struct mddev *mddev, struct bio *bio) +{ + return bio->bi_pool == &mddev->io_clone_set; +} + /* * MD devices can be used undeneath by DM, in which case ->gendisk is NULL. */ diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 5e38a51e349a..2c000b3a5f49 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -14,6 +14,7 @@ #include <linux/seq_file.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <trace/events/block.h> #include "md.h" #include "raid0.h" @@ -43,7 +44,7 @@ static void dump_zones(struct mddev *mddev) int raid_disks = conf->strip_zone[0].nb_dev; pr_debug("md: RAID0 configuration for %s - %d zone%s\n", mdname(mddev), - conf->nr_strip_zones, conf->nr_strip_zones==1?"":"s"); + conf->nr_strip_zones, str_plural(conf->nr_strip_zones)); for (j = 0; j < conf->nr_strip_zones; j++) { char line[200]; int len = 0; @@ -392,6 +393,7 @@ static int raid0_set_limits(struct mddev *mddev) lim.io_opt = lim.io_min * mddev->raid_disks; lim.chunk_sectors = mddev->chunk_sectors; lim.features |= BLK_FEAT_ATOMIC_WRITES; + lim.features |= BLK_FEAT_PCI_P2PDMA; err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY); if (err) return err; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 64d970e2ef50..5b9368bd9e70 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1343,11 +1343,18 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, bool r1bio_existed = !!r1_bio; /* - * If r1_bio is set, we are blocking the raid1d thread - * so there is a tiny risk of deadlock. So ask for + * An md cloned bio indicates we are in the error path. + * This is more reliable than checking r1_bio, which might + * be NULL even in the error path if a failed bio was split. + */ + bool err_path = md_cloned_bio(mddev, bio); + + /* + * If we are in the error path, we are blocking the raid1d + * thread so there is a tiny risk of deadlock. So ask for * emergency memory if needed. */ - gfp_t gfp = r1_bio ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO; + gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO; /* * Still need barrier for READ in case that whole @@ -1411,7 +1418,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, } r1_bio->read_disk = rdisk; - if (!r1bio_existed) { + if (likely(!md_cloned_bio(mddev, bio))) { md_account_bio(mddev, &bio); r1_bio->master_bio = bio; } @@ -1596,8 +1603,10 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, * complexity of supporting that is not worth * the benefit. */ - if (bio->bi_opf & REQ_ATOMIC) + if (bio->bi_opf & REQ_ATOMIC) { + rdev_dec_pending(rdev, mddev); goto err_handle; + } good_sectors = first_bad - r1_bio->sector; if (good_sectors < max_sectors) @@ -2411,11 +2420,6 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio) struct mddev *mddev = conf->mddev; struct md_rdev *rdev = conf->mirrors[read_disk].rdev; - if (exceed_read_errors(mddev, rdev)) { - r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED; - return; - } - while(sectors) { int s = sectors; int d = read_disk; @@ -2627,35 +2631,36 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio) static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio) { + struct md_rdev *rdev = conf->mirrors[r1_bio->read_disk].rdev; + struct bio *bio = r1_bio->bios[r1_bio->read_disk]; struct mddev *mddev = conf->mddev; - struct bio *bio; - struct md_rdev *rdev; sector_t sector; clear_bit(R1BIO_ReadError, &r1_bio->state); - /* we got a read error. Maybe the drive is bad. Maybe just - * the block and we can fix it. - * We freeze all other IO, and try reading the block from - * other devices. When we find one, we re-write - * and check it that fixes the read error. - * This is all done synchronously while the array is - * frozen - */ - bio = r1_bio->bios[r1_bio->read_disk]; bio_put(bio); r1_bio->bios[r1_bio->read_disk] = NULL; - rdev = conf->mirrors[r1_bio->read_disk].rdev; - if (mddev->ro == 0 - && !test_bit(FailFast, &rdev->flags)) { - freeze_array(conf, 1); - fix_read_error(conf, r1_bio); - unfreeze_array(conf); - } else if (mddev->ro == 0 && test_bit(FailFast, &rdev->flags)) { + /* + * We got a read error. Maybe the drive is bad. Maybe just the block + * and we can fix it. + * + * If allowed, freeze all other IO, and try reading the block from other + * devices. If we find one, we re-write and check it that fixes the + * read error. This is all done synchronously while the array is + * frozen. + */ + if (mddev->ro) { + r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED; + } else if (test_bit(FailFast, &rdev->flags)) { md_error(mddev, rdev); } else { - r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED; + freeze_array(conf, 1); + if (exceed_read_errors(mddev, rdev)) + r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED; + else + fix_read_error(conf, r1_bio); + unfreeze_array(conf); } rdev_dec_pending(rdev, conf->mddev); @@ -3208,6 +3213,7 @@ static int raid1_set_limits(struct mddev *mddev) lim.max_hw_wzeroes_unmap_sectors = 0; lim.logical_block_size = mddev->logical_block_size; lim.features |= BLK_FEAT_ATOMIC_WRITES; + lim.features |= BLK_FEAT_PCI_P2PDMA; err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY); if (err) return err; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 39085e7dd6d2..cee5a253a281 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1146,7 +1146,7 @@ static bool regular_request_wait(struct mddev *mddev, struct r10conf *conf, } static void raid10_read_request(struct mddev *mddev, struct bio *bio, - struct r10bio *r10_bio, bool io_accounting) + struct r10bio *r10_bio) { struct r10conf *conf = mddev->private; struct bio *read_bio; @@ -1155,7 +1155,20 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, char b[BDEVNAME_SIZE]; int slot = r10_bio->read_slot; struct md_rdev *err_rdev = NULL; - gfp_t gfp = GFP_NOIO; + + /* + * An md cloned bio indicates we are in the error path. + * This is more reliable than checking slot, which might + * be -1 even in the error path if a failed bio was split. + */ + bool err_path = md_cloned_bio(mddev, bio); + + /* + * If we are in the error path, we are blocking the raid10d + * thread so there is a tiny risk of deadlock. So ask for + * emergency memory if needed. + */ + gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO; if (slot >= 0 && r10_bio->devs[slot].rdev) { /* @@ -1166,11 +1179,6 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, * we lose the device name in error messages. */ int disk; - /* - * As we are blocking raid10, it is a little safer to - * use __GFP_HIGH. - */ - gfp = GFP_NOIO | __GFP_HIGH; disk = r10_bio->devs[slot].devnum; err_rdev = conf->mirrors[disk].rdev; @@ -1218,7 +1226,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, } slot = r10_bio->read_slot; - if (io_accounting) { + if (likely(!md_cloned_bio(mddev, bio))) { md_account_bio(mddev, &bio); r10_bio->master_bio = bio; } @@ -1544,7 +1552,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors) conf->geo.raid_disks); if (bio_data_dir(bio) == READ) - raid10_read_request(mddev, bio, r10_bio, true); + raid10_read_request(mddev, bio, r10_bio); else raid10_write_request(mddev, bio, r10_bio); } @@ -1727,6 +1735,7 @@ retry_discard: r10_bio->mddev = mddev; r10_bio->state = 0; r10_bio->sectors = 0; + r10_bio->read_slot = -1; memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks); wait_blocked_dev(mddev, r10_bio); @@ -2858,7 +2867,7 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio) rdev_dec_pending(rdev, mddev); r10_bio->state = 0; - raid10_read_request(mddev, r10_bio->master_bio, r10_bio, false); + raid10_read_request(mddev, r10_bio->master_bio, r10_bio); /* * allow_barrier after re-submit to ensure no sync io * can be issued while regular io pending. @@ -3941,6 +3950,7 @@ static int raid10_set_queue_limits(struct mddev *mddev) lim.chunk_sectors = mddev->chunk_sectors; lim.io_opt = lim.io_min * raid10_nr_stripes(conf); lim.features |= BLK_FEAT_ATOMIC_WRITES; + lim.features |= BLK_FEAT_PCI_P2PDMA; err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY); if (err) return err; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 0d76e82f4506..65ae7d8930fc 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -6042,8 +6042,11 @@ out_release: raid5_release_stripe(sh); out: if (ret == STRIPE_SCHEDULE_AND_RETRY && reshape_interrupted(mddev)) { - bi->bi_status = BLK_STS_RESOURCE; - ret = STRIPE_WAIT_RESHAPE; + if (!mddev_is_dm(mddev) || + test_bit(MD_DM_SUSPENDING, &mddev->flags)) { + bi->bi_status = BLK_STS_RESOURCE; + ret = STRIPE_WAIT_RESHAPE; + } pr_err_ratelimited("dm-raid456: io across reshape position while reshape can't make progress"); } return ret; diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 1c7b710fc9c1..1dfa60a41d91 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -38,7 +38,7 @@ * Clean -> Dirty - on compute_parity to satisfy write/sync (RECONSTRUCT or RMW) * * The Want->Empty, Want->Clean, Dirty->Clean, transitions - * all happen in b_end_io at interrupt time. + * all happen in end_io at interrupt time. * Each sets the Uptodate bit before releasing the Lock bit. * This leaves one multi-stage transition: * Want->Dirty->Clean @@ -64,7 +64,7 @@ * together, but we are not guaranteed of that so we allow for more. * * If a buffer is on the read list when the associated cache buffer is - * Uptodate, the data is copied into the read buffer and it's b_end_io + * Uptodate, the data is copied into the read buffer and it's end_io * routine is called. This may happen in the end_request routine only * if the buffer has just successfully been read. end_request should * remove the buffers from the list and then set the Uptodate bit on @@ -76,7 +76,7 @@ * into the cache buffer, which is then marked dirty, and moved onto a * third list, the written list (bh_written). Once both the parity * block and the cached buffer are successfully written, any buffer on - * a written list can be returned with b_end_io. + * a written list can be returned with end_io. * * The write list and read list both act as fifos. The read list, * write list and written list are protected by the device_lock. diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index b1a2c68e9944..9d93cb8b8e82 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -156,20 +156,14 @@ static int brx_set_format(struct v4l2_subdev *subdev, compose->height = format->height; } - /* - * Propagate the format code to all pads, and the whole format to the - * source pad. - */ + /* Propagate the format code to all pads. */ if (fmt->pad == BRX_PAD_SINK(0)) { unsigned int i; - for (i = 0; i < brx->entity.source_pad; ++i) { + for (i = 0; i <= brx->entity.source_pad; ++i) { format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } - - format = v4l2_subdev_state_get_format(state, i); - *format = fmt->format; } done: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 1dad9589768c..839b75b62ceb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -380,7 +380,7 @@ static int vsp1_entity_init_state(struct v4l2_subdev *subdev, unsigned int pad; /* Initialize all pad formats with default values. */ - for (pad = 0; pad < subdev->entity.num_pads; ++pad) { + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { struct v4l2_subdev_format format = { .pad = pad, .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index 3e10f6fe89f8..b5117ee9f5fa 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -184,7 +184,7 @@ static int igorplugusb_probe(struct usb_interface *intf, if (!ir->buf_in) goto fail; usb_fill_control_urb(ir->urb, udev, - usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, + usb_rcvctrlpipe(udev, 0), (uint8_t *)ir->request, ir->buf_in, MAX_PACKET, igorplugusb_callback, ir); usb_make_path(udev, ir->phys, sizeof(ir->phys)); diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 1ff98956b680..5c512d61dc1b 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -575,6 +575,7 @@ static void msi2500_disconnect(struct usb_interface *intf) v4l2_device_disconnect(&dev->v4l2_dev); video_unregister_device(&dev->vdev); spi_unregister_controller(dev->ctlr); + spi_controller_put(dev->ctlr); mutex_unlock(&dev->v4l2_lock); mutex_unlock(&dev->vb_queue_lock); @@ -1230,10 +1231,8 @@ static int msi2500_probe(struct usb_interface *intf, ctlr->transfer_one_message = msi2500_transfer_one_message; spi_controller_set_devdata(ctlr, dev); ret = spi_register_controller(ctlr); - if (ret) { - spi_controller_put(ctlr); - goto err_unregister_v4l2_dev; - } + if (ret) + goto err_put_controller; /* load v4l2 subdevice */ sd = v4l2_spi_new_subdev(&dev->v4l2_dev, ctlr, &board_info); @@ -1276,6 +1275,8 @@ err_free_controls: v4l2_ctrl_handler_free(&dev->hdl); err_unregister_controller: spi_unregister_controller(dev->ctlr); +err_put_controller: + spi_controller_put(dev->ctlr); err_unregister_v4l2_dev: v4l2_device_unregister(&dev->v4l2_dev); err_free_mem: diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 8db970da9af9..1e8e8aba2542 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -628,10 +628,11 @@ static __maybe_unused int atmel_ebi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(atmel_ebi_pm_ops, NULL, atmel_ebi_resume); static struct platform_driver atmel_ebi_driver = { + .probe = atmel_ebi_probe, .driver = { .name = "atmel-ebi", .of_match_table = atmel_ebi_id_table, .pm = &atmel_ebi_pm_ops, }, }; -builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe); +builtin_platform_driver(atmel_ebi_driver); diff --git a/drivers/memory/tegra/tegra210-emc-core.c b/drivers/memory/tegra/tegra210-emc-core.c index e96ca4157d48..065ae8bc2830 100644 --- a/drivers/memory/tegra/tegra210-emc-core.c +++ b/drivers/memory/tegra/tegra210-emc-core.c @@ -1966,8 +1966,8 @@ static int tegra210_emc_probe(struct platform_device *pdev) tegra210_emc_debugfs_init(emc); - cd = devm_thermal_of_cooling_device_register(emc->dev, np, "emc", emc, - &tegra210_emc_cd_ops); + cd = devm_thermal_of_child_cooling_device_register(emc->dev, np, "emc", emc, + &tegra210_emc_cd_ops); if (IS_ERR(cd)) { err = PTR_ERR(cd); dev_err(emc->dev, "failed to register cooling device: %d\n", diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c index 0b6a90661eee..0a54586aa54e 100644 --- a/drivers/memstick/host/tifm_ms.c +++ b/drivers/memstick/host/tifm_ms.c @@ -647,7 +647,7 @@ static int tifm_ms_resume(struct tifm_dev *sock) #endif /* CONFIG_PM */ -static struct tifm_device_id tifm_ms_id_tbl[] = { +static const struct tifm_device_id tifm_ms_id_tbl[] = { { TIFM_TYPE_MS }, { 0 } }; diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index a4d9c070d481..e75e1d6851ab 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -15,8 +15,6 @@ #include <linux/property.h> #include <linux/slab.h> -#include <linux/timb_gpio.h> - #include <linux/i2c.h> #include <linux/platform_data/i2c-ocores.h> #include <linux/platform_data/i2c-xiic.h> @@ -37,6 +35,10 @@ #define DRIVER_NAME "timberdale" +#define GPIO_NR_PINS 16 +#define GPIO_BASE 0 +#define IRQ_BASE 200 + struct timberdale_device { resource_size_t ctl_mapbase; unsigned char __iomem *ctl_membase; @@ -174,11 +176,16 @@ static const struct resource timberdale_eth_resources[] = { }, }; -static struct timbgpio_platform_data - timberdale_gpio_platform_data = { - .gpio_base = 0, - .nr_pins = GPIO_NR_PINS, - .irq_base = 200, +static const struct property_entry timberdale_gpio_properties[] = { + PROPERTY_ENTRY_U32("ngpios", GPIO_NR_PINS), + PROPERTY_ENTRY_U32("gpio-base", GPIO_BASE), + PROPERTY_ENTRY_U32("irq-base", IRQ_BASE), + { } +}; + +static const struct software_node timberdale_gpio_swnode = { + .name = "timb-gpio", + .properties = timberdale_gpio_properties, }; static const struct resource timberdale_gpio_resources[] = { @@ -390,8 +397,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-video", @@ -452,8 +458,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-mlogicore", @@ -514,8 +519,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-video", @@ -564,8 +568,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .pdata_size = sizeof(timberdale_gpio_platform_data), + .swnode = &timberdale_gpio_swnode, }, { .name = "timb-video", diff --git a/drivers/mfd/timberdale.h b/drivers/mfd/timberdale.h index b01d2388e1af..db7b434f766d 100644 --- a/drivers/mfd/timberdale.h +++ b/drivers/mfd/timberdale.h @@ -113,7 +113,6 @@ #define GPIO_PIN_ASCB 8 #define GPIO_PIN_INIC_RST 14 #define GPIO_PIN_BT_RST 15 -#define GPIO_NR_PINS 16 /* DMA Channels */ #define DMA_UART_RX 0 diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 0200288d3a7a..5d5f357a1996 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -215,37 +215,37 @@ AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); static const struct i2c_device_id at24_ids[] = { - { "24c00", (kernel_ulong_t)&at24_data_24c00 }, - { "24c01", (kernel_ulong_t)&at24_data_24c01 }, - { "24cs01", (kernel_ulong_t)&at24_data_24cs01 }, - { "24c02", (kernel_ulong_t)&at24_data_24c02 }, - { "24cs02", (kernel_ulong_t)&at24_data_24cs02 }, - { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, - { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, - { "24aa025e48", (kernel_ulong_t)&at24_data_24aa025e48 }, - { "24aa025e64", (kernel_ulong_t)&at24_data_24aa025e64 }, - { "spd", (kernel_ulong_t)&at24_data_spd }, - { "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio }, - { "24c04", (kernel_ulong_t)&at24_data_24c04 }, - { "24cs04", (kernel_ulong_t)&at24_data_24cs04 }, - { "24c08", (kernel_ulong_t)&at24_data_24c08 }, - { "24cs08", (kernel_ulong_t)&at24_data_24cs08 }, - { "24c16", (kernel_ulong_t)&at24_data_24c16 }, - { "24cs16", (kernel_ulong_t)&at24_data_24cs16 }, - { "24c32", (kernel_ulong_t)&at24_data_24c32 }, - { "24c32d-wl", (kernel_ulong_t)&at24_data_24c32d_wlp }, - { "24cs32", (kernel_ulong_t)&at24_data_24cs32 }, - { "24c64", (kernel_ulong_t)&at24_data_24c64 }, - { "24c64-wl", (kernel_ulong_t)&at24_data_24c64d_wlp }, - { "24cs64", (kernel_ulong_t)&at24_data_24cs64 }, - { "24c128", (kernel_ulong_t)&at24_data_24c128 }, - { "24c256", (kernel_ulong_t)&at24_data_24c256 }, - { "24256e-wl", (kernel_ulong_t)&at24_data_24256e_wlp }, - { "24c512", (kernel_ulong_t)&at24_data_24c512 }, - { "24c1024", (kernel_ulong_t)&at24_data_24c1024 }, - { "24c1025", (kernel_ulong_t)&at24_data_24c1025 }, - { "24c2048", (kernel_ulong_t)&at24_data_24c2048 }, - { "at24", 0 }, + { .name = "24c00", .driver_data = (kernel_ulong_t)&at24_data_24c00 }, + { .name = "24c01", .driver_data = (kernel_ulong_t)&at24_data_24c01 }, + { .name = "24cs01", .driver_data = (kernel_ulong_t)&at24_data_24cs01 }, + { .name = "24c02", .driver_data = (kernel_ulong_t)&at24_data_24c02 }, + { .name = "24cs02", .driver_data = (kernel_ulong_t)&at24_data_24cs02 }, + { .name = "24mac402", .driver_data = (kernel_ulong_t)&at24_data_24mac402 }, + { .name = "24mac602", .driver_data = (kernel_ulong_t)&at24_data_24mac602 }, + { .name = "24aa025e48", .driver_data = (kernel_ulong_t)&at24_data_24aa025e48 }, + { .name = "24aa025e64", .driver_data = (kernel_ulong_t)&at24_data_24aa025e64 }, + { .name = "spd", .driver_data = (kernel_ulong_t)&at24_data_spd }, + { .name = "24c02-vaio", .driver_data = (kernel_ulong_t)&at24_data_24c02_vaio }, + { .name = "24c04", .driver_data = (kernel_ulong_t)&at24_data_24c04 }, + { .name = "24cs04", .driver_data = (kernel_ulong_t)&at24_data_24cs04 }, + { .name = "24c08", .driver_data = (kernel_ulong_t)&at24_data_24c08 }, + { .name = "24cs08", .driver_data = (kernel_ulong_t)&at24_data_24cs08 }, + { .name = "24c16", .driver_data = (kernel_ulong_t)&at24_data_24c16 }, + { .name = "24cs16", .driver_data = (kernel_ulong_t)&at24_data_24cs16 }, + { .name = "24c32", .driver_data = (kernel_ulong_t)&at24_data_24c32 }, + { .name = "24c32d-wl", .driver_data = (kernel_ulong_t)&at24_data_24c32d_wlp }, + { .name = "24cs32", .driver_data = (kernel_ulong_t)&at24_data_24cs32 }, + { .name = "24c64", .driver_data = (kernel_ulong_t)&at24_data_24c64 }, + { .name = "24c64-wl", .driver_data = (kernel_ulong_t)&at24_data_24c64d_wlp }, + { .name = "24cs64", .driver_data = (kernel_ulong_t)&at24_data_24cs64 }, + { .name = "24c128", .driver_data = (kernel_ulong_t)&at24_data_24c128 }, + { .name = "24c256", .driver_data = (kernel_ulong_t)&at24_data_24c256 }, + { .name = "24256e-wl", .driver_data = (kernel_ulong_t)&at24_data_24256e_wlp }, + { .name = "24c512", .driver_data = (kernel_ulong_t)&at24_data_24c512 }, + { .name = "24c1024", .driver_data = (kernel_ulong_t)&at24_data_24c1024 }, + { .name = "24c1025", .driver_data = (kernel_ulong_t)&at24_data_24c1025 }, + { .name = "24c2048", .driver_data = (kernel_ulong_t)&at24_data_24c2048 }, + { .name = "at24", .driver_data = 0 }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, at24_ids); diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 1080f9acf70a..f3a49384586d 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -310,6 +310,8 @@ struct fastrpc_user { spinlock_t lock; /* lock for allocations */ struct mutex mutex; + /* Reference count */ + struct kref refcount; }; /* Extract SMMU PA from consolidated IOVA */ @@ -386,7 +388,7 @@ static int fastrpc_map_get(struct fastrpc_map *map) static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, - struct fastrpc_map **ppmap) + struct fastrpc_map **ppmap, bool take_ref) { struct fastrpc_map *map = NULL; struct dma_buf *buf; @@ -401,6 +403,12 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, if (map->fd != fd || map->buf != buf) continue; + if (take_ref) { + ret = fastrpc_map_get(map); + if (ret) + break; + } + *ppmap = map; ret = 0; break; @@ -497,15 +505,57 @@ static void fastrpc_channel_ctx_put(struct fastrpc_channel_ctx *cctx) kref_put(&cctx->refcount, fastrpc_channel_ctx_free); } +static void fastrpc_context_put(struct fastrpc_invoke_ctx *ctx); + +static void fastrpc_user_free(struct kref *ref) +{ + struct fastrpc_user *fl = container_of(ref, struct fastrpc_user, refcount); + struct fastrpc_invoke_ctx *ctx, *n; + struct fastrpc_map *map, *m; + struct fastrpc_buf *buf, *b; + + if (fl->init_mem) + fastrpc_buf_free(fl->init_mem); + + list_for_each_entry_safe(ctx, n, &fl->pending, node) { + list_del(&ctx->node); + fastrpc_context_put(ctx); + } + + list_for_each_entry_safe(map, m, &fl->maps, node) + fastrpc_map_put(map); + + list_for_each_entry_safe(buf, b, &fl->mmaps, node) { + list_del(&buf->node); + fastrpc_buf_free(buf); + } + + fastrpc_channel_ctx_put(fl->cctx); + mutex_destroy(&fl->mutex); + kfree(fl); +} + +static void fastrpc_user_get(struct fastrpc_user *fl) +{ + kref_get(&fl->refcount); +} + +static void fastrpc_user_put(struct fastrpc_user *fl) +{ + kref_put(&fl->refcount, fastrpc_user_free); +} + static void fastrpc_context_free(struct kref *ref) { struct fastrpc_invoke_ctx *ctx; struct fastrpc_channel_ctx *cctx; + struct fastrpc_user *fl; unsigned long flags; int i; ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount); cctx = ctx->cctx; + fl = ctx->fl; for (i = 0; i < ctx->nbufs; i++) fastrpc_map_put(ctx->maps[i]); @@ -521,6 +571,8 @@ static void fastrpc_context_free(struct kref *ref) kfree(ctx->olaps); kfree(ctx); + /* Release the reference taken in fastrpc_context_alloc() */ + fastrpc_user_put(fl); fastrpc_channel_ctx_put(cctx); } @@ -628,6 +680,8 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( /* Released in fastrpc_context_put() */ fastrpc_channel_ctx_get(cctx); + /* Take a reference to user, released in fastrpc_context_free() */ + fastrpc_user_get(user); ctx->sc = sc; ctx->retval = -1; @@ -658,6 +712,7 @@ err_idr: spin_lock(&user->lock); list_del(&ctx->node); spin_unlock(&user->lock); + fastrpc_user_put(user); fastrpc_channel_ctx_put(cctx); kfree(ctx->maps); kfree(ctx->olaps); @@ -871,19 +926,10 @@ get_err: static int fastrpc_map_create(struct fastrpc_user *fl, int fd, u64 len, u32 attr, struct fastrpc_map **ppmap) { - struct fastrpc_session_ctx *sess = fl->sctx; - int err = 0; - - if (!fastrpc_map_lookup(fl, fd, ppmap)) { - if (!fastrpc_map_get(*ppmap)) - return 0; - dev_dbg(sess->dev, "%s: Failed to get map fd=%d\n", - __func__, fd); - } - - err = fastrpc_map_attach(fl, fd, len, attr, ppmap); + if (!fastrpc_map_lookup(fl, fd, ppmap, true)) + return 0; - return err; + return fastrpc_map_attach(fl, fd, len, attr, ppmap); } /* @@ -1041,7 +1087,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) pages[i].addr = ctx->maps[i]->dma_addr; mmap_read_lock(current->mm); - vma = find_vma(current->mm, ctx->args[i].ptr); + vma = vma_lookup(current->mm, ctx->args[i].ptr); if (vma) pages[i].addr += (ctx->args[i].ptr & PAGE_MASK) - vma->vm_start; @@ -1153,7 +1199,7 @@ cleanup_fdlist: for (i = 0; i < FASTRPC_MAX_FDLIST; i++) { if (!fdlist[i]) break; - if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap)) + if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false)) fastrpc_map_put(mmap); } @@ -1579,9 +1625,6 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) { struct fastrpc_user *fl = (struct fastrpc_user *)file->private_data; struct fastrpc_channel_ctx *cctx = fl->cctx; - struct fastrpc_invoke_ctx *ctx, *n; - struct fastrpc_map *map, *m; - struct fastrpc_buf *buf, *b; unsigned long flags; fastrpc_release_current_dsp_process(fl); @@ -1590,28 +1633,10 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) list_del(&fl->user); spin_unlock_irqrestore(&cctx->lock, flags); - if (fl->init_mem) - fastrpc_buf_free(fl->init_mem); - - list_for_each_entry_safe(ctx, n, &fl->pending, node) { - list_del(&ctx->node); - fastrpc_context_put(ctx); - } - - list_for_each_entry_safe(map, m, &fl->maps, node) - fastrpc_map_put(map); - - list_for_each_entry_safe(buf, b, &fl->mmaps, node) { - list_del(&buf->node); - fastrpc_buf_free(buf); - } - fastrpc_session_free(cctx, fl->sctx); - fastrpc_channel_ctx_put(cctx); - - mutex_destroy(&fl->mutex); - kfree(fl); file->private_data = NULL; + /* Release the reference taken in fastrpc_device_open */ + fastrpc_user_put(fl); return 0; } @@ -1655,6 +1680,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) spin_lock_irqsave(&cctx->lock, flags); list_add_tail(&fl->user, &cctx->users); spin_unlock_irqrestore(&cctx->lock, flags); + kref_init(&fl->refcount); return 0; } @@ -2431,7 +2457,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) kref_init(&data->refcount); - dev_set_drvdata(&rpdev->dev, data); rdev->dma_mask = &data->dma_mask; dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32)); INIT_LIST_HEAD(&data->users); @@ -2440,6 +2465,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) idr_init(&data->ctx_idr); data->domain_id = domain_id; data->rpdev = rpdev; + dev_set_drvdata(&rpdev->dev, data); err = of_platform_populate(rdev->of_node, NULL, NULL, rdev); if (err) @@ -2513,6 +2539,9 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data, if (len < sizeof(*rsp)) return -EINVAL; + if (!cctx) + return -ENODEV; + ctxid = ((rsp->ctx & FASTRPC_CTXID_MASK) >> 4); spin_lock_irqsave(&cctx->lock, flags); diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index 03ebe33185f9..4e58d16fc01e 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile @@ -11,7 +11,7 @@ lkdtm-$(CONFIG_LKDTM) += usercopy.o lkdtm-$(CONFIG_LKDTM) += kstack_erase.o lkdtm-$(CONFIG_LKDTM) += cfi.o lkdtm-$(CONFIG_LKDTM) += fortify.o -lkdtm-$(CONFIG_PPC_64S_HASH_MMU) += powerpc.o +lkdtm-$(CONFIG_PPC_BOOK3S_64) += powerpc.o KASAN_SANITIZE_stackleak.o := n diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index e0098f314570..3eca2ef64aff 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -7,6 +7,7 @@ */ #include "lkdtm.h" #include <linux/cpu.h> +#include <linux/efi.h> #include <linux/list.h> #include <linux/hrtimer.h> #include <linux/sched.h> @@ -817,6 +818,29 @@ static noinline void lkdtm_CORRUPT_PAC(void) #endif } +static void __maybe_unused lkdtm_EFI_RUNTIME_CRASH(void) +{ + static unsigned long size __ro_after_init = sizeof(efi_char16_t); + efi_status_t status; + + if (!efi.get_next_variable || + !efi_enabled(EFI_RUNTIME_SERVICES) || + !efi_rt_services_supported(EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) { + pr_err("FAIL: EFI GetNextVariableName() is not available\n"); + return; + } + + /* + * Provoke a fault by asking the firmware to write to a read-only + * variable. + */ + status = efi.get_next_variable(&size, L"", &(efi_guid_t){}); + + if (status != EFI_ABORTED || efi_enabled(EFI_RUNTIME_SERVICES)) + pr_err("FAIL: EFI GetNextVariable() did not abort (%#lx)\n", + status); +} + static struct crashtype crashtypes[] = { CRASHTYPE(PANIC), CRASHTYPE(PANIC_STOP_IRQOFF), @@ -850,6 +874,9 @@ static struct crashtype crashtypes[] = { CRASHTYPE(UNSET_SMEP), CRASHTYPE(DOUBLE_FAULT), CRASHTYPE(CORRUPT_PAC), +#ifdef CONFIG_EFI + CRASHTYPE(EFI_RUNTIME_CRASH), +#endif }; struct crashtype_category bugs_crashtypes = { diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 5732fd59a227..ededa32d6744 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -96,7 +96,7 @@ static const struct crashtype_category *crashtype_categories[] = { &stackleak_crashtypes, &cfi_crashtypes, &fortify_crashtypes, -#ifdef CONFIG_PPC_64S_HASH_MMU +#ifdef CONFIG_PPC_BOOK3S_64 &powerpc_crashtypes, #endif }; diff --git a/drivers/misc/lkdtm/powerpc.c b/drivers/misc/lkdtm/powerpc.c index be385449911a..6eaac79ea26b 100644 --- a/drivers/misc/lkdtm/powerpc.c +++ b/drivers/misc/lkdtm/powerpc.c @@ -5,6 +5,7 @@ #include <linux/vmalloc.h> #include <asm/mmu.h> +#ifdef CONFIG_PPC_64S_HASH_MMU /* Inserts new slb entries */ static void insert_slb_entry(unsigned long p, int ssize, int page_size) { @@ -17,11 +18,14 @@ static void insert_slb_entry(unsigned long p, int ssize, int page_size) : "r" (mk_vsid_data(p, ssize, flags)), "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED)) : "memory"); + isync(); asm volatile("slbmte %0,%1" : : "r" (mk_vsid_data(p, ssize, flags)), "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED + 1)) : "memory"); + isync(); + preempt_enable(); } @@ -84,6 +88,7 @@ static void insert_dup_slb_entry_0(void) : "r" (vsid), "r" (esid | SLB_NUM_BOLTED) : "memory"); + isync(); asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i)); asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i)); @@ -93,15 +98,42 @@ static void insert_dup_slb_entry_0(void) : "r" (vsid), "r" (esid | (SLB_NUM_BOLTED + 1)) : "memory"); + isync(); pr_info("%s accessing test address 0x%lx: 0x%lx\n", __func__, test_address, *test_ptr); preempt_enable(); } +#endif /* CONFIG_PPC_64S_HASH_MMU */ + +static __always_inline void tlbiel_va(unsigned long va, + unsigned long pid, + unsigned long ap, + unsigned long ric) +{ + unsigned long rb, rs, prs, r; + + rb = va & ~(PPC_BITMASK(52, 63)); + rb |= ap << PPC_BITLSHIFT(58); + rs = pid << PPC_BITLSHIFT(31); + + prs = 1; /* process scoped */ + r = 1; /* radix format */ + + /* + * Trigger an MCE by issuing radix tlbiel with an invalid operand combination. + * The combination of RIC = 2 with IS = 0 (Invalidation selector specified + * in the RB register) is invalid. + * This invalid combination causes hardware to raise a machine check. + */ + asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1) + : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); +} static void lkdtm_PPC_SLB_MULTIHIT(void) { +#ifdef CONFIG_PPC_64S_HASH_MMU if (!radix_enabled()) { pr_info("Injecting SLB multihit errors\n"); /* @@ -117,10 +149,27 @@ static void lkdtm_PPC_SLB_MULTIHIT(void) } else { pr_err("XFAIL: This test is for ppc64 and with hash mode MMU only\n"); } +#else + pr_err("XFAIL: This test requires CONFIG_PPC_64S_HASH_MMU\n"); +#endif +} + +static void lkdtm_PPC_RADIX_TLBIEL(void) +{ + unsigned long addr = PAGE_OFFSET; + + if (radix_enabled()) { + pr_info("Injecting Radix TLB invalidation MCE\n"); + tlbiel_va(addr, 0, 0, RIC_FLUSH_ALL); + pr_info("Recovered from radix tlbiel attempt\n"); + } else { + pr_err("XFAIL: This test is for ppc64 and with radix mode MMU only\n"); + } } static struct crashtype crashtypes[] = { CRASHTYPE(PPC_SLB_MULTIHIT), + CRASHTYPE(PPC_RADIX_TLBIEL), }; struct crashtype_category powerpc_crashtypes = { diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 30af282262ef..02c9d1192812 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -19,6 +19,7 @@ #include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/time_namespace.h> #include <uapi/linux/ntsync.h> #define NTSYNC_NAME "ntsync" @@ -836,6 +837,8 @@ static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_ar if (args->flags & NTSYNC_WAIT_REALTIME) clock = CLOCK_REALTIME; + else + timeout = timens_ktime_to_host(clock, timeout); do { if (signal_pending(current)) { diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index d210da84c30a..81685e3f3296 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -143,6 +143,7 @@ static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, struct rp1_dev *rp1 = d->host_data; msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); + msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_IACK); return 0; } diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index da0827724a61..aac8f0933e43 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -31,7 +31,7 @@ static const char *tifm_media_type_name(unsigned char type, unsigned char nt) return card_type_name[nt][type - 1]; } -static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id) +static int tifm_dev_match(struct tifm_dev *sock, const struct tifm_device_id *id) { if (sock->type == id->type) return 1; @@ -43,7 +43,7 @@ static int tifm_bus_match(struct device *dev, const struct device_driver *drv) struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); const struct tifm_driver *fm_drv = container_of_const(drv, struct tifm_driver, driver); - struct tifm_device_id *ids = fm_drv->id_table; + const struct tifm_device_id *ids = fm_drv->id_table; if (ids) { while (ids->type) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 8846550a8892..05444ecf3909 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1371,7 +1371,9 @@ static void mmc_select_driver_type(struct mmc_card *card) card->drive_strength = drive_strength; - if (drv_type) + if (fixed_drv_type >= 0 && drive_strength) + mmc_set_driver_type(card->host, drive_strength); + else if (drv_type) mmc_set_driver_type(card->host, drv_type); } diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 39fcb662c43f..5d187e063da8 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -208,14 +208,20 @@ static unsigned short mmc_get_max_segments(struct mmc_host *host) } static int mmc_mq_init_request(struct blk_mq_tag_set *set, struct request *req, - unsigned int hctx_idx, unsigned int numa_node) + unsigned int hctx_idx, int numa_node) { struct mmc_queue_req *mq_rq = req_to_mmc_queue_req(req); struct mmc_queue *mq = set->driver_data; struct mmc_card *card = mq->card; struct mmc_host *host = card->host; + u16 sg_len = mmc_get_max_segments(host); - mq_rq->sg = mmc_alloc_sg(mmc_get_max_segments(host), GFP_KERNEL); + if (!sg_len) { + dev_err(mmc_dev(host), "Wrong max_segs assigned\n"); + return -EINVAL; + } + + mq_rq->sg = mmc_alloc_sg(sg_len, GFP_KERNEL); if (!mq_rq->sg) return -ENOMEM; diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c index 1373deb3f531..84ff6d82ae3c 100644 --- a/drivers/mmc/host/cavium-thunderx.c +++ b/drivers/mmc/host/cavium-thunderx.c @@ -188,6 +188,7 @@ static const struct pci_device_id thunder_mmc_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa010) }, { 0, } /* end of table */ }; +MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table); static struct pci_driver thunder_mmc_driver = { .name = KBUILD_MODNAME, @@ -201,4 +202,3 @@ module_pci_driver(thunder_mmc_driver); MODULE_AUTHOR("Cavium Inc."); MODULE_DESCRIPTION("Cavium ThunderX eMMC Driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table); diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 42b0118a45a8..cdb9fa94b56d 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -928,7 +928,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) } } - if (qstatus & MMCST0_TOUTRD) { + if (data && (qstatus & MMCST0_TOUTRD)) { /* Read data timeout */ data->error = -ETIMEDOUT; end_transfer = 1; @@ -940,7 +940,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) davinci_abort_data(host, data); } - if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) { + if (data && (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD))) { /* Data CRC error */ data->error = -EILSEQ; end_transfer = 1; @@ -1294,14 +1294,10 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) goto cpu_freq_fail; } - ret = mmc_add_host(mmc); - if (ret < 0) - goto mmc_add_host_fail; - ret = devm_request_irq(&pdev->dev, irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host); if (ret) - goto request_irq_fail; + goto mmc_add_host_fail; if (host->sdio_irq >= 0) { ret = devm_request_irq(&pdev->dev, host->sdio_irq, @@ -1311,6 +1307,10 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_SDIO_IRQ; } + ret = mmc_add_host(mmc); + if (ret < 0) + goto mmc_add_host_fail; + rename_region(mem, mmc_hostname(mmc)); if (mmc->caps & MMC_CAP_8_BIT_DATA) @@ -1324,8 +1324,6 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) return 0; -request_irq_fail: - mmc_remove_host(mmc); mmc_add_host_fail: mmc_davinci_cpufreq_deregister(host); cpu_freq_fail: diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 261344d3a8cf..4b76b997ddc1 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -141,6 +141,7 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) { /* Quirk needed for certain Exynos SoCs */ host->quirks |= DW_MMC_QUIRK_FIFO64_32; + host->dma_threshold = 512; } if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) { diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index c6eece4ec3fd..75c82ff20f17 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -441,6 +441,22 @@ static int dw_mci_common_parse_dt(struct dw_mci *host) return 0; } +static int dw_mci_rk2928_parse_dt(struct dw_mci *host) +{ + struct dw_mci_rockchip_priv_data *priv; + int err; + + err = dw_mci_common_parse_dt(host); + if (err) + return err; + + priv = host->priv; + + priv->internal_phase = false; + + return 0; +} + static int dw_mci_rk3288_parse_dt(struct dw_mci *host) { struct dw_mci_rockchip_priv_data *priv; @@ -514,6 +530,7 @@ static int dw_mci_rockchip_init(struct dw_mci *host) static const struct dw_mci_drv_data rk2928_drv_data = { .init = dw_mci_rockchip_init, + .parse_dt = dw_mci_rk2928_parse_dt, }; static const struct dw_mci_drv_data rk3288_drv_data = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 20193ee7b73e..d734d010444d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -40,7 +40,6 @@ SDMMC_INT_RESP_ERR | SDMMC_INT_HLE) #define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ DW_MCI_CMD_ERROR_FLAGS) -#define DW_MCI_DMA_THRESHOLD 16 #define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ #define DW_MCI_FREQ_MIN 100000 /* unit: HZ */ @@ -491,12 +490,12 @@ static int dw_mci_idmac_init(struct dw_mci *host) if (host->dma_64bit_address == 1) { struct idmac_desc_64addr *p; - /* Number of descriptors in the ring buffer */ - host->ring_size = + + host->desc_num = DESC_RING_BUF_SZ / sizeof(struct idmac_desc_64addr); /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; + for (i = 0, p = host->sg_cpu; i < host->desc_num - 1; i++, p++) { p->des6 = (host->sg_dma + (sizeof(struct idmac_desc_64addr) * @@ -519,13 +518,13 @@ static int dw_mci_idmac_init(struct dw_mci *host) } else { struct idmac_desc *p; - /* Number of descriptors in the ring buffer */ - host->ring_size = + + host->desc_num = DESC_RING_BUF_SZ / sizeof(struct idmac_desc); /* Forward link the descriptor list */ for (i = 0, p = host->sg_cpu; - i < host->ring_size - 1; + i < host->desc_num - 1; i++, p++) { p->des3 = cpu_to_le32(host->sg_dma + (sizeof(struct idmac_desc) * (i + 1))); @@ -821,7 +820,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host, * non-word-aligned buffers or lengths. Also, we don't bother * with all the DMA setup overhead for short transfers. */ - if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD) + if (data->blocks * data->blksz < host->dma_threshold) return -EINVAL; if (data->blksz & 3) @@ -2858,10 +2857,10 @@ static int dw_mci_init_host(struct dw_mci *host) /* Useful defaults if platform data is unset. */ if (host->use_dma == TRANS_MODE_IDMAC) { - mmc->max_segs = host->ring_size; + mmc->max_segs = host->desc_num; mmc->max_blk_size = 65535; mmc->max_seg_size = 0x1000; - mmc->max_req_size = mmc->max_seg_size * host->ring_size; + mmc->max_req_size = mmc->max_seg_size * host->desc_num; mmc->max_blk_count = mmc->max_req_size / 512; } else if (host->use_dma == TRANS_MODE_EDMAC) { mmc->max_segs = 64; @@ -3185,6 +3184,7 @@ struct dw_mci *dw_mci_alloc_host(struct device *dev) host = mmc_priv(mmc); host->mmc = mmc; host->dev = dev; + host->dma_threshold = 16; return host; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 42e58be74ce0..9ffcd3946cff 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -78,8 +78,8 @@ struct dw_mci_dma_slave { * @sg_cpu: Virtual address of DMA buffer. * @dma_ops: Pointer to DMA callbacks. * @cmd_status: Snapshot of SR taken upon completion of the current - * @ring_size: Buffer size for idma descriptors. * command. Only valid when EVENT_CMD_COMPLETE is pending. + * @desc_num: Number of idmac descriptors available. * @dms: structure of slave-dma private data. * @phy_regs: physical address of controller's register map * @data_status: Snapshot of SR taken upon completion of the current @@ -107,6 +107,7 @@ struct dw_mci_dma_slave { * @ciu_clk: Pointer to card interface unit clock instance. * @fifo_depth: depth of FIFO. * @data_addr_override: override fifo reg offset with this value. + * @dma_threshold: data threshold value in bytes to carry out a DMA transfer. * @wm_aligned: force fifo watermark equal with data length in PIO mode. * Set as true if alignment is needed. * @data_shift: log2 of FIFO item size. @@ -163,6 +164,7 @@ struct dw_mci { void __iomem *regs; void __iomem *fifo_reg; u32 data_addr_override; + u32 dma_threshold; bool wm_aligned; struct scatterlist *sg; @@ -184,7 +186,7 @@ struct dw_mci { void *sg_cpu; const struct dw_mci_dma_ops *dma_ops; /* For idmac */ - unsigned int ring_size; + unsigned short desc_num; /* For edmac */ struct dw_mci_dma_slave *dms; diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index d2f19c2dc673..3655542ca998 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/litex.h> +#include <linux/math.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -68,6 +69,9 @@ #define SD_SLEEP_US 5 #define SD_TIMEOUT_US 20000 +#define SD_INIT_DELAY_US 1000 +#define SD_INIT_CLK_HZ 400000 + #define SDIRQ_CARD_DETECT 1 #define SDIRQ_SD_TO_MEM_DONE 2 #define SDIRQ_MEM_TO_SD_DONE 4 @@ -436,11 +440,10 @@ static void litex_mmc_setclk(struct litex_mmc_host *host, unsigned int freq) struct device *dev = mmc_dev(host->mmc); u32 div; - div = freq ? host->ref_clk / freq : 256U; - div = roundup_pow_of_two(div); + div = freq ? DIV_ROUND_UP(host->ref_clk, freq) : 256U; div = clamp(div, 2U, 256U); dev_dbg(dev, "sd_clk_freq=%d: set to %d via div=%d\n", - freq, host->ref_clk / div, div); + freq, host->ref_clk / ((div + 1) & ~1U), div); litex_write16(host->sdphy + LITEX_PHY_CLOCKERDIV, div); host->sd_clk = freq; } @@ -450,6 +453,17 @@ static void litex_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct litex_mmc_host *host = mmc_priv(mmc); /* + * The SD specification requires at least 74 idle clocks before CMD0. + * These dummy cycles is generated by writing LITEX_PHY_INITIALIZE. + */ + if (ios->chip_select == MMC_CS_HIGH) { + litex_mmc_setclk(host, SD_INIT_CLK_HZ); + litex_write8(host->sdphy + LITEX_PHY_INITIALIZE, 1); + fsleep(SD_INIT_DELAY_US); + return; + } + + /* * NOTE: Ignore any ios->bus_width updates; they occur right after * the mmc core sends its own acmd6 bus-width change notification, * which is redundant since we snoop on the command flow and inject diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index f6ebb7bc7ede..024edc4e5fe6 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -279,12 +279,16 @@ static const struct renesas_sdhi_of_data_with_quirks of_rza2_compatible = { static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible = "renesas,sdhi-r7s9210", .data = &of_rza2_compatible, }, { .compatible = "renesas,sdhi-mmc-r8a77470", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a774b1", .data = &of_r8a77965_compatible, }, + { .compatible = "renesas,sdhi-r8a774c0", .data = &of_r8a77990_compatible, }, + { .compatible = "renesas,sdhi-r8a774e1", .data = &of_r8a7795_compatible, }, { .compatible = "renesas,sdhi-r8a7795", .data = &of_r8a7795_compatible, }, { .compatible = "renesas,sdhi-r8a77961", .data = &of_r8a77961_compatible, }, { .compatible = "renesas,sdhi-r8a77965", .data = &of_r8a77965_compatible, }, { .compatible = "renesas,sdhi-r8a77970", .data = &of_r8a77970_compatible, }, { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, }, { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, }, + { .compatible = "renesas,sdhi-r8a779md", .data = &of_rcar_gen3_nohs400_compatible, }, { .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,sdhi-r9a09g057", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, }, diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 633462c0be5f..0882ce74e0c9 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1918,14 +1918,14 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, return 0; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); + dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; + return 0; } - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); - msm_host->ice = ice; /* Initialize the blk_crypto_profile */ diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 0b2158a7e409..eef53455b8ee 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -277,6 +277,7 @@ #define PHY_DELAY_CODE_MAX 0x7f #define PHY_DELAY_CODE_EMMC 0x17 #define PHY_DELAY_CODE_SD 0x55 +#define PHY_DELAY_CODE_SDIO 0x29 struct rk35xx_priv { struct reset_control *reset; @@ -917,11 +918,9 @@ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, return -ENOMEM; priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); - if (IS_ERR(priv->reset)) { - err = PTR_ERR(priv->reset); - dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err); - return err; - } + if (IS_ERR(priv->reset)) + return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->reset), + "failed to get reset control\n"); err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, ARRAY_SIZE(clk_ids), clk_ids); @@ -1433,10 +1432,7 @@ static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) clk_set_rate(pltfm_host->clk, clock); clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk |= SDHCI_CLOCK_INT_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - dwcmshc_enable_card_clk(host); + sdhci_enable_clk(host, clk); } static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay) @@ -1497,7 +1493,7 @@ static void sdhci_eic7700_config_phy(struct sdhci_host *host) static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask) { - sdhci_reset(host, mask); + dwcmshc_reset(host, mask); /* after reset all, the phy's config will be clear */ if (mask == SDHCI_RESET_ALL) @@ -1594,18 +1590,17 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; int phase_code = -1; int code_range = -1; - bool is_sd = false; int code_min = -1; int code_max = -1; int cmd_error = 0; + bool is_emmc; int ret = 0; int i = 0; - if ((host->mmc->caps2 & sd_caps) == sd_caps) - is_sd = true; + is_emmc = (host->mmc->caps2 & emmc_caps) == emmc_caps; for (i = 0; i <= MAX_PHASE_CODE; i++) { /* Centered Phase code */ @@ -1614,8 +1609,8 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); if (ret) { - /* SD specific range tracking */ - if (is_sd && code_min != -1 && code_max != -1) { + /* SD/SDIO specific range tracking */ + if (!is_emmc && code_min != -1 && code_max != -1) { if (code_max - code_min > code_range) { code_range = code_max - code_min; phase_code = (code_min + code_max) / 2; @@ -1626,17 +1621,17 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) code_max = -1; } /* EMMC breaks after first valid range */ - if (!is_sd && code_min != -1 && code_max != -1) + if (is_emmc && code_min != -1 && code_max != -1) break; } else { /* Track valid phase code range */ if (code_min == -1) { code_min = i; - if (!is_sd) + if (is_emmc) continue; } code_max = i; - if (is_sd && i == MAX_PHASE_CODE) { + if (!is_emmc && i == MAX_PHASE_CODE) { if (code_max - code_min > code_range) { code_range = code_max - code_min; phase_code = (code_min + code_max) / 2; @@ -1646,19 +1641,19 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) } /* Handle tuning failure case */ - if ((is_sd && phase_code == -1) || - (!is_sd && code_min == -1 && code_max == -1)) { + if ((!is_emmc && phase_code == -1) || + (is_emmc && code_min == -1 && code_max == -1)) { pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc)); sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); return -EIO; } - if (!is_sd) + if (is_emmc) phase_code = (code_min + code_max) / 2; sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); - /* SD specific final verification */ - if (is_sd) { + /* SD/SDIO specific final verification */ + if (!is_emmc) { ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); if (ret) { @@ -1756,9 +1751,9 @@ static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned in static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing) { - u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; - if ((host->mmc->caps2 & sd_caps) == sd_caps) + if ((host->mmc->caps2 & emmc_caps) != emmc_caps) sdhci_set_uhs_signaling(host, timing); else sdhci_eic7700_set_uhs_signaling(host, timing); @@ -1767,6 +1762,7 @@ static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; unsigned int val, hsp_int_status, hsp_pwr_ctrl; static const char * const clk_ids[] = {"axi"}; struct of_phandle_args args; @@ -1781,10 +1777,8 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm dwc_priv->priv = priv; ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv); - if (ret) { - dev_err(dev, "failed to reset\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to reset\n"); ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, ARRAY_SIZE(clk_ids), clk_ids); @@ -1792,16 +1786,14 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm return ret; ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args); - if (ret) { - dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Fail to parse 'eswin,hsp-sp-csr' phandle\n"); hsp_regmap = syscon_node_to_regmap(args.np); if (IS_ERR(hsp_regmap)) { - dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n"); of_node_put(args.np); - return PTR_ERR(hsp_regmap); + return dev_err_probe(dev, PTR_ERR(hsp_regmap), + "Failed to get regmap for 'eswin,hsp-sp-csr'\n"); } hsp_int_status = args.args[0]; hsp_pwr_ctrl = args.args[1]; @@ -1821,8 +1813,10 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm if ((host->mmc->caps2 & emmc_caps) == emmc_caps) dwc_priv->delay_line = PHY_DELAY_CODE_EMMC; - else + else if ((host->mmc->caps2 & sd_caps) == sd_caps) dwc_priv->delay_line = PHY_DELAY_CODE_SD; + else + dwc_priv->delay_line = PHY_DELAY_CODE_SDIO; if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val)) priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val); @@ -2408,10 +2402,8 @@ static int dwcmshc_probe(struct platform_device *pdev) u32 extra, caps; pltfm_data = device_get_match_data(&pdev->dev); - if (!pltfm_data) { - dev_err(&pdev->dev, "Error: No device match data found\n"); - return -ENODEV; - } + if (!pltfm_data) + return dev_err_probe(&pdev->dev, -ENODEV, "No device match data found\n"); host = sdhci_pltfm_init(pdev, &pltfm_data->pdata, sizeof(struct dwcmshc_priv)); @@ -2564,8 +2556,7 @@ static int dwcmshc_suspend(struct device *dev) return ret; clk_disable_unprepare(pltfm_host->clk); - if (!IS_ERR(priv->bus_clk)) - clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); @@ -2608,8 +2599,7 @@ static int dwcmshc_resume(struct device *dev) disable_other_clks: clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); disable_bus_clk: - if (!IS_ERR(priv->bus_clk)) - clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->bus_clk); disable_clk: clk_disable_unprepare(pltfm_host->clk); return ret; diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index 455656f9842d..37b0911e7cf2 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -16,11 +16,19 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/reset.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include "sdhci.h" #include "sdhci-pltfm.h" +#define SPACEMIT_SDHC_OP_EXT_REG 0x108 +#define SDHC_OVRRD_CLK_OEN BIT(11) +#define SDHC_FORCE_CLK_ON BIT(12) + +#define SPACEMIT_SDHC_LEGACY_CTRL_REG 0x10C +#define SDHC_GEN_PAD_CLK_ON BIT(6) + #define SPACEMIT_SDHC_MMC_CTRL_REG 0x114 #define SDHC_MISC_INT_EN BIT(1) #define SDHC_MISC_INT BIT(2) @@ -61,9 +69,34 @@ #define SDHC_PHY_DRIVE_SEL GENMASK(2, 0) #define SDHC_RX_BIAS_CTRL BIT(5) +#define SPACEMIT_SDHC_RX_CFG_REG 0x118 +#define SDHC_RX_SDCLK_SEL0_MASK GENMASK(1, 0) +#define SDHC_RX_SDCLK_SEL1_MASK GENMASK(3, 2) +#define SDHC_RX_SDCLK_SEL1 FIELD_PREP(SDHC_RX_SDCLK_SEL1_MASK, 1) + +#define SPACEMIT_SDHC_DLINE_CTRL_REG 0x130 +#define SDHC_DLINE_PU BIT(0) +#define SDHC_RX_DLINE_CODE_MASK GENMASK(23, 16) +#define SDHC_TX_DLINE_CODE_MASK GENMASK(31, 24) + +#define SPACEMIT_SDHC_DLINE_CFG_REG 0x134 +#define SDHC_RX_DLINE_REG_MASK GENMASK(7, 0) +#define SDHC_RX_DLINE_GAIN BIT(8) +#define SDHC_TX_DLINE_REG_MASK GENMASK(23, 16) + +#define SPACEMIT_RX_DLINE_REG 9 +#define SPACEMIT_RX_TUNE_DELAY_MIN 0x0 +#define SPACEMIT_RX_TUNE_DELAY_MAX 0xFF + +#define SPACEMIT_TX_TUNING_DLINE_REG 0x00 +#define SPACEMIT_TX_TUNING_DELAYCODE 127 + struct spacemit_sdhci_host { struct clk *clk_core; struct clk *clk_io; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_default; + struct pinctrl_state *pinctrl_uhs; }; /* All helper functions will update clr/set while preserve rest bits */ @@ -85,6 +118,50 @@ static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u sdhci_writel(host, val, reg); } +static void spacemit_sdhci_set_rx_delay(struct sdhci_host *host, u8 delay) +{ + spacemit_sdhci_clrsetbits(host, SDHC_RX_DLINE_CODE_MASK, + FIELD_PREP(SDHC_RX_DLINE_CODE_MASK, delay), + SPACEMIT_SDHC_DLINE_CTRL_REG); +} + +static void spacemit_sdhci_set_tx_delay(struct sdhci_host *host, u8 delay) +{ + spacemit_sdhci_clrsetbits(host, SDHC_TX_DLINE_CODE_MASK, + FIELD_PREP(SDHC_TX_DLINE_CODE_MASK, delay), + SPACEMIT_SDHC_DLINE_CTRL_REG); +} + +static void spacemit_sdhci_set_tx_dline_reg(struct sdhci_host *host, u8 dline_reg) +{ + spacemit_sdhci_clrsetbits(host, SDHC_TX_DLINE_REG_MASK, + FIELD_PREP(SDHC_TX_DLINE_REG_MASK, dline_reg), + SPACEMIT_SDHC_DLINE_CFG_REG); +} + +static void spacemit_sdhci_tx_tuning_prepare(struct sdhci_host *host) +{ + spacemit_sdhci_setbits(host, SDHC_TX_MUX_SEL, SPACEMIT_SDHC_TX_CFG_REG); + spacemit_sdhci_setbits(host, SDHC_DLINE_PU, SPACEMIT_SDHC_DLINE_CTRL_REG); + udelay(5); +} + +static void spacemit_sdhci_prepare_tuning(struct sdhci_host *host) +{ + spacemit_sdhci_clrsetbits(host, SDHC_RX_DLINE_REG_MASK, + FIELD_PREP(SDHC_RX_DLINE_REG_MASK, SPACEMIT_RX_DLINE_REG), + SPACEMIT_SDHC_DLINE_CFG_REG); + + spacemit_sdhci_setbits(host, SDHC_DLINE_PU, SPACEMIT_SDHC_DLINE_CTRL_REG); + udelay(5); + + spacemit_sdhci_clrsetbits(host, SDHC_RX_SDCLK_SEL1_MASK, SDHC_RX_SDCLK_SEL1, + SPACEMIT_SDHC_RX_CFG_REG); + + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200) + spacemit_sdhci_setbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG); +} + static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask) { sdhci_reset(host, mask); @@ -101,6 +178,12 @@ static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask) if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) spacemit_sdhci_setbits(host, SDHC_MMC_CARD_MODE, SPACEMIT_SDHC_MMC_CTRL_REG); + + spacemit_sdhci_setbits(host, SDHC_GEN_PAD_CLK_ON, SPACEMIT_SDHC_LEGACY_CTRL_REG); + + if (host->mmc->caps2 & MMC_CAP2_NO_MMC) + spacemit_sdhci_setbits(host, SDHC_OVRRD_CLK_OEN | SDHC_FORCE_CLK_ON, + SPACEMIT_SDHC_OP_EXT_REG); } static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) @@ -174,6 +257,111 @@ static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host) return clk_get_rate(pltfm_host->clk); } +static int spacemit_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int current_len = 0, current_start = 0; + int max_pass_len = 0, max_pass_start = 0; + struct mmc_host *mmc = host->mmc; + struct mmc_ios ios = mmc->ios; + u8 final_delay; + int ret = 0; + int i; + + /* + * Tuning is required for SDR50/SDR104, HS200/HS400 cards and + * if clock frequency is greater than 100MHz in these modes. + */ + if (host->clock < 100 * 1000 * 1000 || + !(ios.timing == MMC_TIMING_MMC_HS200 || + ios.timing == MMC_TIMING_UHS_SDR50 || + ios.timing == MMC_TIMING_UHS_SDR104)) + return 0; + + if (mmc->caps2 & MMC_CAP2_NO_MMC) { + spacemit_sdhci_set_tx_dline_reg(host, SPACEMIT_TX_TUNING_DLINE_REG); + spacemit_sdhci_set_tx_delay(host, SPACEMIT_TX_TUNING_DELAYCODE); + spacemit_sdhci_tx_tuning_prepare(host); + + dev_dbg(mmc_dev(host->mmc), "TX tuning: dline_reg=%d, delaycode=%d\n", + SPACEMIT_TX_TUNING_DLINE_REG, SPACEMIT_TX_TUNING_DELAYCODE); + } + + spacemit_sdhci_prepare_tuning(host); + + for (i = SPACEMIT_RX_TUNE_DELAY_MIN; i <= SPACEMIT_RX_TUNE_DELAY_MAX; i++) { + spacemit_sdhci_set_rx_delay(host, i); + ret = mmc_send_tuning(host->mmc, opcode, NULL); + + dev_dbg(mmc_dev(host->mmc), "RX delay %d: %s\n", + i, ret == 0 ? "pass" : "fail"); + + if (ret == 0) { + /* Test passed - extend current window */ + if (current_len == 0) + current_start = i; + current_len++; + } else { + /* Test failed - check if current window is best so far */ + if (current_len > max_pass_len) { + max_pass_len = current_len; + max_pass_start = current_start; + } + current_len = 0; + } + } + + if (current_len > max_pass_len) { + max_pass_len = current_len; + max_pass_start = current_start; + } + + if (max_pass_len < 3) { + dev_err(mmc_dev(host->mmc), "Tuning failed: no stable window found\n"); + return -EIO; + } + + final_delay = max_pass_start + max_pass_len / 2; + spacemit_sdhci_set_rx_delay(host, final_delay); + ret = mmc_send_tuning(host->mmc, opcode, NULL); + if (ret) { + u8 retry_delays[] = { + max_pass_start + max_pass_len / 4, + max_pass_start + (3 * max_pass_len) / 4, + max_pass_start, + max_pass_start + max_pass_len - 1 + }; + int retry_count = ARRAY_SIZE(retry_delays); + + dev_warn(mmc_dev(mmc), "Primary delay %d failed, trying alternatives\n", + final_delay); + + for (i = 0; i < retry_count; i++) { + if (retry_delays[i] >= SPACEMIT_RX_TUNE_DELAY_MIN && + retry_delays[i] <= SPACEMIT_RX_TUNE_DELAY_MAX) { + spacemit_sdhci_set_rx_delay(host, retry_delays[i]); + ret = mmc_send_tuning(host->mmc, opcode, NULL); + if (!ret) { + final_delay = retry_delays[i]; + dev_info(mmc_dev(mmc), "Retry successful with delay %d\n", + final_delay); + break; + } + } + } + + if (ret) { + dev_err(mmc_dev(mmc), "All retry attempts failed\n"); + return -EIO; + } + } + + dev_dbg(mmc_dev(host->mmc), + "Tuning successful: window %d-%d, using delay %d\n", + max_pass_start, max_pass_start + max_pass_len - 1, final_delay); + + return 0; +} + static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); @@ -206,6 +394,46 @@ static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc) SPACEMIT_SDHC_PHY_CTRL_REG); } +static int spacemit_sdhci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host); + struct pinctrl_state *state; + int ret; + + ret = sdhci_start_signal_voltage_switch(mmc, ios); + if (ret) + return ret; + + if (!sdhst->pinctrl) + return 0; + + /* Select appropriate pinctrl state based on signal voltage */ + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + state = sdhst->pinctrl_default; + break; + case MMC_SIGNAL_VOLTAGE_180: + state = sdhst->pinctrl_uhs; + break; + default: + dev_warn(mmc_dev(mmc), "unsupported voltage %d\n", ios->signal_voltage); + return 0; + } + + ret = pinctrl_select_state(sdhst->pinctrl, state); + if (ret) { + dev_warn(mmc_dev(mmc), "failed to select pinctrl state: %d\n", ret); + return 0; + } + dev_dbg(mmc_dev(mmc), "switched to %s pinctrl state\n", + ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180 ? "UHS" : "default"); + + return 0; +} + static inline int spacemit_sdhci_get_clocks(struct device *dev, struct sdhci_pltfm_host *pltfm_host) { @@ -239,12 +467,37 @@ static inline int spacemit_sdhci_get_resets(struct device *dev) return 0; } +static inline void spacemit_sdhci_get_pins(struct device *dev, + struct sdhci_pltfm_host *pltfm_host) +{ + struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host); + + sdhst->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(sdhst->pinctrl)) { + sdhst->pinctrl = NULL; + dev_dbg(dev, "pinctrl not available, voltage switching will work without it\n"); + return; + } + + sdhst->pinctrl_default = pinctrl_lookup_state(sdhst->pinctrl, "default"); + if (IS_ERR(sdhst->pinctrl_default)) + sdhst->pinctrl_default = NULL; + + sdhst->pinctrl_uhs = pinctrl_lookup_state(sdhst->pinctrl, "uhs"); + if (IS_ERR(sdhst->pinctrl_uhs)) + sdhst->pinctrl_uhs = NULL; + + dev_dbg(dev, "pinctrl setup: default=%p, uhs=%p\n", + sdhst->pinctrl_default, sdhst->pinctrl_uhs); +} + static const struct sdhci_ops spacemit_sdhci_ops = { .get_max_clock = spacemit_sdhci_clk_get_max_clock, .reset = spacemit_sdhci_reset, .set_bus_width = sdhci_set_bus_width, .set_clock = spacemit_sdhci_set_clock, .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling, + .platform_execute_tuning = spacemit_sdhci_execute_tuning, }; static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = { @@ -311,6 +564,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev) host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY; + spacemit_sdhci_get_pins(dev, pltfm_host); + + host->mmc_host_ops.start_signal_voltage_switch = spacemit_sdhci_start_signal_voltage_switch; + ret = spacemit_sdhci_get_clocks(dev, pltfm_host); if (ret) goto err_pltfm; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 605be55f8d2d..e3bf901b10aa 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3836,6 +3836,7 @@ int sdhci_resume_host(struct sdhci_host *host) host->pwr = 0; host->clock = 0; host->reinit_uhs = true; + mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios); mmc->ops->set_ios(mmc, &mmc->ios); } else { sdhci_init(host, (mmc->pm_flags & MMC_PM_KEEP_POWER)); diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index c1f7d5b37911..28ab2526dab1 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -1041,9 +1041,10 @@ static int tifm_sd_resume(struct tifm_dev *sock) #endif /* CONFIG_PM */ -static struct tifm_device_id tifm_sd_id_tbl[] = { +static const struct tifm_device_id tifm_sd_id_tbl[] = { { TIFM_TYPE_SD }, { } }; +MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); static struct tifm_driver tifm_sd_driver = { .driver = { @@ -1070,7 +1071,6 @@ static void __exit tifm_sd_exit(void) MODULE_AUTHOR("Alex Dubov"); MODULE_DESCRIPTION("TI FlashMedia SD driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); MODULE_VERSION(DRIVER_VERSION); module_init(tifm_sd_init); diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index c628b3bbfd7a..8c049f8355cd 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -323,9 +323,8 @@ struct via_crdr_mmc_host { #define VIA_CMD_TIMEOUT_MS 1000 static const struct pci_device_id via_ids[] = { - {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, - {0,} + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_9530) }, + { } }; MODULE_DEVICE_TABLE(pci, via_ids); diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 1b1d691e19fc..489daee4f4fc 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -744,6 +744,7 @@ static const struct of_device_id wmt_mci_dt_ids[] = { { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps }, { /* Sentinel */ }, }; +MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids); static int wmt_mci_probe(struct platform_device *pdev) { @@ -980,4 +981,3 @@ module_platform_driver(wmt_mci_driver); MODULE_DESCRIPTION("Wondermedia MMC/SD Driver"); MODULE_AUTHOR("Tony Prisk"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids); diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 0b076790bd9d..786e3fb4874d 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -501,10 +501,13 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, } } - if (req->mode == MTD_OPS_RAW) - rdesc = spinand->dirmaps[req->pos.plane].rdesc; + rdesc = spinand->dirmaps[req->pos.plane].rdesc; + + if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && + req->mode != MTD_OPS_RAW) + rdesc->info.op_tmpl->data.ecc = true; else - rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc; + rdesc->info.op_tmpl->data.ecc = false; if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT) column |= req->pos.plane << fls(nanddev_page_size(nand)); @@ -593,10 +596,13 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, req->ooblen); } - if (req->mode == MTD_OPS_RAW) - wdesc = spinand->dirmaps[req->pos.plane].wdesc; + wdesc = spinand->dirmaps[req->pos.plane].wdesc; + + if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && + req->mode != MTD_OPS_RAW) + wdesc->info.op_tmpl->data.ecc = true; else - wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc; + wdesc->info.op_tmpl->data.ecc = false; if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT) column |= req->pos.plane << fls(nanddev_page_size(nand)); @@ -892,6 +898,12 @@ static int spinand_mtd_continuous_page_read(struct mtd_info *mtd, loff_t from, * Each data read must be a multiple of 4-bytes and full pages should be read; * otherwise, the data output might get out of sequence from one read command * to another. + * + * Continuous reads never cross LUN boundaries. Some devices don't + * support crossing planes boundaries. Some devices don't even support + * crossing blocks boundaries. The common case being to read through UBI, + * we will very rarely read two consequent blocks or more, so let's only enable + * continuous reads when reading within the same erase block. */ nanddev_io_for_each_block(nand, NAND_PAGE_READ, from, ops, &iter) { ret = spinand_select_target(spinand, iter.req.pos.target); @@ -982,19 +994,6 @@ static bool spinand_use_cont_read(struct mtd_info *mtd, loff_t from, nanddev_offs_to_pos(nand, from, &start_pos); nanddev_offs_to_pos(nand, from + ops->len - 1, &end_pos); - /* - * Continuous reads never cross LUN boundaries. Some devices don't - * support crossing planes boundaries. Some devices don't even support - * crossing blocks boundaries. The common case being to read through UBI, - * we will very rarely read two consequent blocks or more, so it is safer - * and easier (can be improved) to only enable continuous reads when - * reading within the same erase block. - */ - if (start_pos.target != end_pos.target || - start_pos.plane != end_pos.plane || - start_pos.eraseblock != end_pos.eraseblock) - return false; - return start_pos.page < end_pos.page; } @@ -1252,12 +1251,18 @@ static int spinand_create_dirmap(struct spinand_device *spinand, struct nand_device *nand = spinand_to_nand(spinand); struct spi_mem_dirmap_info info = { 0 }; struct spi_mem_dirmap_desc *desc; + bool enable_ecc = false; + + if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED) + enable_ecc = true; /* The plane number is passed in MSB just above the column address */ info.offset = plane << fls(nand->memorg.pagesize); + /* Write descriptor */ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - info.op_tmpl = *spinand->op_templates->update_cache; + info.primary_op_tmpl = *spinand->op_templates->update_cache; + info.primary_op_tmpl.data.ecc = enable_ecc; desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, spinand->spimem, &info); if (IS_ERR(desc)) @@ -1265,38 +1270,15 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc = desc; - info.op_tmpl = *spinand->op_templates->read_cache; + /* Read descriptor */ + info.primary_op_tmpl = *spinand->op_templates->read_cache; + info.primary_op_tmpl.data.ecc = enable_ecc; desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); spinand->dirmaps[plane].rdesc = desc; - if (nand->ecc.engine->integration != NAND_ECC_ENGINE_INTEGRATION_PIPELINED) { - spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc; - spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc; - - return 0; - } - - info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - info.op_tmpl = *spinand->op_templates->update_cache; - info.op_tmpl.data.ecc = true; - desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, - spinand->spimem, &info); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - spinand->dirmaps[plane].wdesc_ecc = desc; - - info.op_tmpl = *spinand->op_templates->read_cache; - info.op_tmpl.data.ecc = true; - desc = spinand_create_rdesc(spinand, &info); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - spinand->dirmaps[plane].rdesc_ecc = desc; - return 0; } @@ -1421,7 +1403,7 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand) return spinand->manufacturer->ops->cleanup(spinand); } -static bool spinand_op_is_odtr(const struct spi_mem_op *op) +bool spinand_op_is_odtr(const struct spi_mem_op *op) { return op->cmd.dtr && op->cmd.buswidth == 8; } diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 5dd0b3cb5250..a7bc458edc5c 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3641,14 +3641,15 @@ EXPORT_SYMBOL_GPL(spi_nor_scan); static int spi_nor_create_read_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), - SPI_MEM_OP_DUMMY(nor->read_dummy, 0), - SPI_MEM_OP_DATA_IN(0, NULL, 0)), + .op_tmpl = &info.primary_op_tmpl, + .primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), + SPI_MEM_OP_DATA_IN(0, NULL, 0)), .offset = 0, .length = nor->params->size, }; - struct spi_mem_op *op = &info.op_tmpl; + struct spi_mem_op *op = info.op_tmpl; spi_nor_spimem_setup_op(nor, op, nor->read_proto); @@ -3672,14 +3673,15 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor) static int spi_nor_create_write_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(0, NULL, 0)), + .op_tmpl = &info.primary_op_tmpl, + .primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 0)), .offset = 0, .length = nor->params->size, }; - struct spi_mem_op *op = &info.op_tmpl; + struct spi_mem_op *op = info.op_tmpl; if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op->addr.nbytes = 0; diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index 8880a783c3bc..29c0d6941a81 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -312,7 +312,7 @@ static blk_status_t ubiblock_queue_rq(struct blk_mq_hw_ctx *hctx, static int ubiblock_init_request(struct blk_mq_tag_set *set, struct request *req, unsigned int hctx_idx, - unsigned int numa_node) + int numa_node) { struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index f0aa7d2f2171..985ef66dc333 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1386,8 +1386,8 @@ static void ad_churn_machine(struct port *port) { if (port->sm_vars & AD_PORT_CHURNED) { port->sm_vars &= ~AD_PORT_CHURNED; - port->sm_churn_actor_state = AD_CHURN_MONITOR; - port->sm_churn_partner_state = AD_CHURN_MONITOR; + WRITE_ONCE(port->sm_churn_actor_state, AD_CHURN_MONITOR); + WRITE_ONCE(port->sm_churn_partner_state, AD_CHURN_MONITOR); port->sm_churn_actor_timer_counter = __ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0); port->sm_churn_partner_timer_counter = @@ -1398,20 +1398,22 @@ static void ad_churn_machine(struct port *port) !(--port->sm_churn_actor_timer_counter) && port->sm_churn_actor_state == AD_CHURN_MONITOR) { if (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION) { - port->sm_churn_actor_state = AD_NO_CHURN; + WRITE_ONCE(port->sm_churn_actor_state, AD_NO_CHURN); } else { - port->churn_actor_count++; - port->sm_churn_actor_state = AD_CHURN; + WRITE_ONCE(port->churn_actor_count, + port->churn_actor_count + 1); + WRITE_ONCE(port->sm_churn_actor_state, AD_CHURN); } } if (port->sm_churn_partner_timer_counter && !(--port->sm_churn_partner_timer_counter) && port->sm_churn_partner_state == AD_CHURN_MONITOR) { if (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) { - port->sm_churn_partner_state = AD_NO_CHURN; + WRITE_ONCE(port->sm_churn_partner_state, AD_NO_CHURN); } else { - port->churn_partner_count++; - port->sm_churn_partner_state = AD_CHURN; + WRITE_ONCE(port->churn_partner_count, + port->churn_partner_count + 1); + WRITE_ONCE(port->sm_churn_partner_state, AD_CHURN); } } } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index af82a3df2c5d..8e75453ce0ef 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1890,6 +1890,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, struct sockaddr_storage ss; int res = 0, i; + if (slave_dev->type == ARPHRD_CAN) { + BOND_NL_ERR(bond_dev, extack, + "CAN devices cannot be enslaved"); + return -EPERM; + } + if (slave_dev->flags & IFF_MASTER && !netif_is_bond_master(slave_dev)) { BOND_NL_ERR(bond_dev, extack, @@ -4615,11 +4621,11 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd slave_dev = __dev_get_by_name(net, ifr->ifr_slave); - slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev); - if (!slave_dev) return -ENODEV; + slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev); + switch (cmd) { case SIOCBONDENSLAVE: res = bond_enslave(bond_dev, slave_dev, NULL); diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index c7d3e0602c83..90365d3f7ebf 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -82,10 +82,10 @@ static int bond_fill_slave_info(struct sk_buff *skb, goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE, - ad_port->sm_churn_actor_state)) + READ_ONCE(ad_port->sm_churn_actor_state))) goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE, - ad_port->sm_churn_partner_state)) + READ_ONCE(ad_port->sm_churn_partner_state))) goto nla_put_failure_rcu; } rcu_read_unlock(); diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 3714aab1a3d9..3607b62f9b63 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -221,13 +221,13 @@ static void bond_info_show_slave(struct seq_file *seq, seq_printf(seq, "Aggregator ID: %d\n", agg->aggregator_identifier); seq_printf(seq, "Actor Churn State: %s\n", - bond_3ad_churn_desc(port->sm_churn_actor_state)); + bond_3ad_churn_desc(READ_ONCE(port->sm_churn_actor_state))); seq_printf(seq, "Partner Churn State: %s\n", - bond_3ad_churn_desc(port->sm_churn_partner_state)); + bond_3ad_churn_desc(READ_ONCE(port->sm_churn_partner_state))); seq_printf(seq, "Actor Churned Count: %d\n", - port->churn_actor_count); + READ_ONCE(port->churn_actor_count)); seq_printf(seq, "Partner Churned Count: %d\n", - port->churn_partner_count); + READ_ONCE(port->churn_partner_count)); if (capable(CAP_NET_ADMIN)) { seq_puts(seq, "details actor lacp pdu:\n"); diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index c72c2bfdcffb..2697073dbf90 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -2310,10 +2310,10 @@ int sja1105_static_config_reload(struct sja1105_private *priv, goto out; } - t1 = timespec64_to_ns(&ptp_sts_before.pre_ts); - t2 = timespec64_to_ns(&ptp_sts_before.post_ts); - t3 = timespec64_to_ns(&ptp_sts_after.pre_ts); - t4 = timespec64_to_ns(&ptp_sts_after.post_ts); + t1 = ktime_to_ns(ptp_sts_before.pre_sts.systime); + t2 = ktime_to_ns(ptp_sts_before.post_sts.systime); + t3 = ktime_to_ns(ptp_sts_after.pre_sts.systime); + t4 = ktime_to_ns(ptp_sts_after.post_sts.systime); /* Mid point, corresponds to pre-reset PTPCLKVAL */ t12 = t1 + (t2 - t1) / 2; /* Mid point, corresponds to post-reset PTPCLKVAL, aka 0 */ diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index cecd66251dba..31cdb11cd78d 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1153,6 +1153,9 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) rmem = of_reserved_mem_lookup(np); of_node_put(np); + if (!rmem) + return -ENODEV; + dma_addr = rmem->base; /* Compute the number of hw descriptors according to the * reserved memory size and the payload buffer size @@ -2936,7 +2939,7 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port) if (!port->dsa_meta[i]) continue; - metadata_dst_free(port->dsa_meta[i]); + dst_release(&port->dsa_meta[i]->dst); } } diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index 8c86789d867a..297fb36ab8c1 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -1880,6 +1880,11 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp) continue; } + /* Ensure PHC payload (timestamp, error_flags) is read + * after req_id update is observed + */ + dma_rmb(); + /* req_id was updated by the device which indicates that * PHC timestamp and error_flags are updated too, * checking errors before retrieving timestamp diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index 911808ab13a7..4f3076d4ea34 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -1407,8 +1407,10 @@ static int pcnet32_poll(struct napi_struct *napi, int budget) pcnet32_restart(dev, CSR0_START); netif_wake_queue(dev); } + spin_unlock_irqrestore(&lp->lock, flags); if (work_done < budget && napi_complete_done(napi, work_done)) { + spin_lock_irqsave(&lp->lock, flags); /* clear interrupt masks */ val = lp->a->read_csr(ioaddr, CSR3); val &= 0x00ff; @@ -1416,9 +1418,9 @@ static int pcnet32_poll(struct napi_struct *napi, int budget) /* Set interrupt enable. */ lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN); + spin_unlock_irqrestore(&lp->lock, flags); } - spin_unlock_irqrestore(&lp->lock, flags); return work_done; } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c index eb11800f5573..1c9cfec1b633 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c @@ -277,7 +277,7 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) struct hwrm_func_backing_store_qcaps_v2_output *resp; struct hwrm_func_backing_store_qcaps_v2_input *req; struct bnge_ctx_mem_info *ctx; - u16 type; + u16 type, next_type; int rc; if (bd->ctx) @@ -294,8 +294,8 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) resp = bnge_hwrm_req_hold(bd, req); - for (type = 0; type < BNGE_CTX_V2_MAX; ) { - struct bnge_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; + for (type = 0; type < BNGE_CTX_INV; type = next_type) { + struct bnge_ctx_mem_type *ctxm; u8 init_val, init_off, i; __le32 *p; u32 flags; @@ -304,8 +304,14 @@ int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd) rc = bnge_hwrm_req_send(bd, req); if (rc) goto ctx_done; + + next_type = le16_to_cpu(resp->next_valid_type); + if (type >= BNGE_CTX_V2_MAX) + continue; + + ctxm = &ctx->ctx_arr[type]; flags = le32_to_cpu(resp->flags); - type = le16_to_cpu(resp->next_valid_type); + if (!(flags & FUNC_BACKING_STORE_QCAPS_V2_RESP_FLAGS_TYPE_VALID)) continue; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 008c34cff7b4..c999f9733326 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5748,7 +5748,7 @@ static void bnxt_disable_int_sync(struct bnxt *bp) { int i; - if (!bp->irq_tbl) + if (!bp->irq_tbl || !bp->bnapi) return; atomic_inc(&bp->intr_sem); @@ -14388,13 +14388,28 @@ static void bnxt_unlock_sp(struct bnxt *bp) netdev_unlock(bp->dev); } +/* Same as bnxt_lock_sp() with additional rtnl_lock */ +static void bnxt_rtnl_lock_sp(struct bnxt *bp) +{ + clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state); + rtnl_lock(); + netdev_lock(bp->dev); +} + +static void bnxt_rtnl_unlock_sp(struct bnxt *bp) +{ + set_bit(BNXT_STATE_IN_SP_TASK, &bp->state); + netdev_unlock(bp->dev); + rtnl_unlock(); +} + /* Only called from bnxt_sp_task() */ static void bnxt_reset(struct bnxt *bp, bool silent) { - bnxt_lock_sp(bp); + bnxt_rtnl_lock_sp(bp); if (test_bit(BNXT_STATE_OPEN, &bp->state)) bnxt_reset_task(bp, silent); - bnxt_unlock_sp(bp); + bnxt_rtnl_unlock_sp(bp); } /* Only called from bnxt_sp_task() */ @@ -14402,9 +14417,9 @@ static void bnxt_rx_ring_reset(struct bnxt *bp) { int i; - bnxt_lock_sp(bp); + bnxt_rtnl_lock_sp(bp); if (!test_bit(BNXT_STATE_OPEN, &bp->state)) { - bnxt_unlock_sp(bp); + bnxt_rtnl_unlock_sp(bp); return; } /* Disable and flush TPA before resetting the RX ring */ @@ -14443,7 +14458,7 @@ static void bnxt_rx_ring_reset(struct bnxt *bp) } if (bp->flags & BNXT_FLAG_TPA) bnxt_set_tpa(bp, true); - bnxt_unlock_sp(bp); + bnxt_rtnl_unlock_sp(bp); } static void bnxt_fw_fatal_close(struct bnxt *bp) @@ -15358,15 +15373,17 @@ static void bnxt_fw_reset_task(struct work_struct *work) bp->fw_reset_state = BNXT_FW_RESET_STATE_OPENING; fallthrough; case BNXT_FW_RESET_STATE_OPENING: - while (!netdev_trylock(bp->dev)) { + while (!rtnl_trylock()) { bnxt_queue_fw_reset_work(bp, HZ / 10); return; } + netdev_lock(bp->dev); rc = bnxt_open(bp->dev); if (rc) { netdev_err(bp->dev, "bnxt_open() failed during FW reset\n"); bnxt_fw_reset_abort(bp, rc); netdev_unlock(bp->dev); + rtnl_unlock(); goto ulp_start; } @@ -15386,6 +15403,7 @@ static void bnxt_fw_reset_task(struct work_struct *work) bnxt_dl_health_fw_status_update(bp, true); } netdev_unlock(bp->dev); + rtnl_unlock(); bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); netdev_lock(bp->dev); @@ -16379,7 +16397,7 @@ err_reset: rc); napi_enable_locked(&bnapi->napi); bnxt_db_nq_arm(bp, &cpr->cp_db, cpr->cp_raw_cons); - bnxt_reset_task(bp, true); + netif_close(dev); return rc; } @@ -17230,6 +17248,7 @@ static int bnxt_resume(struct device *device) struct bnxt *bp = netdev_priv(dev); int rc = 0; + rtnl_lock(); netdev_lock(dev); rc = pci_enable_device(bp->pdev); if (rc) { @@ -17274,6 +17293,7 @@ static int bnxt_resume(struct device *device) resume_exit: netdev_unlock(bp->dev); + rtnl_unlock(); if (!rc) { bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); @@ -17445,6 +17465,7 @@ static void bnxt_io_resume(struct pci_dev *pdev) int err; netdev_info(bp->dev, "PCI Slot Resume\n"); + rtnl_lock(); netdev_lock(netdev); err = bnxt_hwrm_func_qcaps(bp); @@ -17462,6 +17483,7 @@ static void bnxt_io_resume(struct pci_dev *pdev) netif_device_attach(netdev); netdev_unlock(netdev); + rtnl_unlock(); if (!err) { bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index f89aa94ce020..6ebde65d7f1b 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -5594,6 +5594,7 @@ static int fec_resume(struct device *dev) if (fep->rpm_active) pm_runtime_force_resume(dev); + pinctrl_pm_select_default_state(&fep->pdev->dev); ret = fec_enet_clk_enable(ndev, true); if (ret) { rtnl_unlock(); @@ -5610,8 +5611,6 @@ static int fec_resume(struct device *dev) val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); writel(val, fep->hwp + FEC_ECNTRL); fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; - } else { - pinctrl_pm_select_default_state(&fep->pdev->dev); } fec_restart(ndev); netif_tx_lock_bh(ndev); diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c index 068da2fd1fea..f721e9893804 100644 --- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c +++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c @@ -420,6 +420,9 @@ static int hbg_pci_init(struct pci_dev *pdev) return -ENOMEM; pci_set_master(pdev); + pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_RELAX_EN); + pci_save_state(pdev); return 0; } diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c index a4ea92c31c2f..0ae314994676 100644 --- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c +++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c @@ -452,12 +452,12 @@ static bool hbg_sync_data_from_hw(struct hbg_priv *priv, { struct hbg_rx_desc *rx_desc; - /* make sure HW write desc complete */ - dma_rmb(); - dma_sync_single_for_cpu(&priv->pdev->dev, buffer->page_dma, buffer->page_size, DMA_FROM_DEVICE); + /* make sure HW write desc complete */ + dma_rmb(); + rx_desc = (struct hbg_rx_desc *)buffer->page_addr; return FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2) != 0; } diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 417dfa18daae..4e503b3d0d2d 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3144,7 +3144,7 @@ static int emac_probe(struct platform_device *ofdev) netif_carrier_off(ndev); - err = devm_register_netdev(&ofdev->dev, ndev); + err = register_netdev(ndev); if (err) { printk(KERN_ERR "%pOF: failed to register net device (%d)!\n", np, err); @@ -3197,6 +3197,13 @@ static void emac_remove(struct platform_device *ofdev) DBG(dev, "remove" NL); + /* Unregister network device before tearing down hardware + * to prevent use-after-free during deferred cleanup. This ensures + * the network stack stops all operations before hardware resources + * are released. + */ + unregister_netdev(dev->ndev); + cancel_work_sync(&dev->reset_work); if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 892bc7c2e28b..0704e92ab043 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -2633,6 +2633,8 @@ static const struct dpll_pin_ops ice_dpll_pin_ufl_ops = { .state_on_dpll_set = ice_dpll_ufl_pin_state_set, .state_on_dpll_get = ice_dpll_sw_pin_state_get, .direction_get = ice_dpll_pin_sw_direction_get, + .prio_get = ice_dpll_sw_input_prio_get, + .prio_set = ice_dpll_sw_input_prio_set, .frequency_get = ice_dpll_sw_pin_frequency_get, .frequency_set = ice_dpll_sw_pin_frequency_set, .esync_set = ice_dpll_sw_esync_set, diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 36df742c326c..f9e4ec6f7ebb 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -2065,11 +2065,13 @@ static const struct ice_crosststamp_cfg ice_crosststamp_cfg_e830 = { /** * struct ice_crosststamp_ctx - Device cross timestamp context * @snapshot: snapshot of system clocks for historic interpolation + * @snapshot_clock_id: System clock ID for @snapshot * @pf: pointer to the PF private structure * @cfg: pointer to hardware configuration for cross timestamp */ struct ice_crosststamp_ctx { struct system_time_snapshot snapshot; + clockid_t snapshot_clock_id; struct ice_pf *pf; const struct ice_crosststamp_cfg *cfg; }; @@ -2115,7 +2117,7 @@ static int ice_capture_crosststamp(ktime_t *device, } /* Snapshot system time for historic interpolation */ - ktime_get_snapshot(&ctx->snapshot); + ktime_get_snapshot_id(ctx->snapshot_clock_id, &ctx->snapshot); /* Program cmd to master timer */ ice_ptp_src_cmd(hw, ICE_PTP_READ_TIME); @@ -2176,6 +2178,7 @@ static int ice_ptp_getcrosststamp(struct ptp_clock_info *info, { struct ice_pf *pf = ptp_info_to_pf(info); struct ice_crosststamp_ctx ctx = { + .snapshot_clock_id = cts->clock_id, .pf = pf, }; diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c index 4a51d2727547..71fe8b2a8b4e 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c @@ -51,7 +51,7 @@ void idpf_ptp_get_features_access(const struct idpf_adapter *adapter) /* Set the device clock time */ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME; - mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME; + mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB; ptp->set_dev_clk_time_access = idpf_ptp_get_access(adapter, direct, mailbox); diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 17236813965d..46d625b15f44 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -326,6 +326,7 @@ struct igc_adapter { struct timespec64 prev_ptp_time; /* Pre-reset PTP clock */ ktime_t ptp_reset_start; /* Reset time in clock mono */ struct system_time_snapshot snapshot; + clockid_t snapshot_clock_id; struct mutex ptm_lock; /* Only allow one PTM transaction at a time */ char fw_version[32]; diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 3d6b2264164a..b40aba9ab685 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -1049,7 +1049,7 @@ static int igc_phc_get_syncdevicetime(ktime_t *device, */ do { /* Get a snapshot of system clocks to use as historic value. */ - ktime_get_snapshot(&adapter->snapshot); + ktime_get_snapshot_id(adapter->snapshot_clock_id, &adapter->snapshot); igc_ptm_trigger(hw); @@ -1103,6 +1103,8 @@ static int igc_ptp_getcrosststamp(struct ptp_clock_info *ptp, /* This blocks until any in progress PTM transactions complete */ mutex_lock(&adapter->ptm_lock); + adapter->snapshot_clock_id = cts->clock_id; + ret = get_device_system_crosststamp(igc_phc_get_syncdevicetime, adapter, &adapter->snapshot, cts); mutex_unlock(&adapter->ptm_lock); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index f9055b3d6fb1..1881583be5ce 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, goto put_err; } ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - ppdev->dev.of_node = pnp; + ppdev->dev.of_node = of_node_get(pnp); ret = platform_device_add_resources(ppdev, &res, 1); if (ret) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index f442b874bb59..ccc24a1301f2 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3917,10 +3917,10 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, struct mvpp2_bm_pool *bm_pool; struct page_pool *pp = NULL; struct sk_buff *skb; - unsigned int frag_size; + unsigned int frag_size, rx_sync_size; dma_addr_t dma_addr; phys_addr_t phys_addr; - int pool, rx_bytes, err, ret; + int pool, rx_bytes, rx_offset, err, ret; struct page *page; void *data; @@ -3933,6 +3933,8 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, rx_status = mvpp2_rxdesc_status_get(port, rx_desc); rx_bytes = mvpp2_rxdesc_size_get(port, rx_desc); rx_bytes -= MVPP2_MH_SIZE; + rx_sync_size = rx_bytes + MVPP2_MH_SIZE; + rx_offset = MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM; dma_addr = mvpp2_rxdesc_dma_addr_get(port, rx_desc); pool = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >> @@ -3946,9 +3948,10 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, dma_dir = DMA_FROM_DEVICE; } - dma_sync_single_for_cpu(dev->dev.parent, dma_addr, - rx_bytes + MVPP2_MH_SIZE, - dma_dir); + dma_sync_single_range_for_cpu(dev->dev.parent, dma_addr, + MVPP2_SKB_HEADROOM, + rx_sync_size, + dma_dir); /* Buffer header not supported */ if (rx_status & MVPP2_RXD_BUF_HDR) @@ -3970,6 +3973,12 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, else frag_size = bm_pool->frag_size; + err = mvpp2_rx_refill(port, bm_pool, pp, pool); + if (err) { + netdev_err(port->dev, "failed to refill BM pools\n"); + goto err_drop_frame; + } + if (xdp_prog) { struct xdp_rxq_info *xdp_rxq; @@ -3978,7 +3987,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, else xdp_rxq = &rxq->xdp_rxq_long; - xdp_init_buff(&xdp, PAGE_SIZE, xdp_rxq); + xdp_init_buff(&xdp, bm_pool->frag_size, xdp_rxq); xdp_prepare_buff(&xdp, data, MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM, rx_bytes, true); @@ -3987,17 +3996,19 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, if (ret) { xdp_ret |= ret; - err = mvpp2_rx_refill(port, bm_pool, pp, pool); - if (err) { - netdev_err(port->dev, "failed to refill BM pools\n"); - goto err_drop_frame; - } - ps.rx_packets++; ps.rx_bytes += rx_bytes; continue; } + rx_sync_size = max_t(unsigned int, rx_sync_size, + xdp.data_end - xdp.data_hard_start - + MVPP2_SKB_HEADROOM); + + /* Update offset and length to reflect any XDP adjustments. */ + rx_offset = xdp.data - data; + rx_bytes = xdp.data_end - xdp.data; + metasize = xdp.data - xdp.data_meta; } @@ -4007,8 +4018,20 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, skb = slab_build_skb(data); if (!skb) { netdev_warn(port->dev, "skb build failed\n"); - goto err_drop_frame; + if (pp) { + page_pool_put_page(pp, virt_to_head_page(data), + rx_sync_size, true); + } else { + dma_unmap_single_attrs(dev->dev.parent, dma_addr, + bm_pool->buf_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + mvpp2_frag_free(bm_pool, pp, data); + } + goto err_drop_frame_retired; } + if (pp) + skb_mark_for_recycle(skb); /* If we have RX hardware timestamping enabled, grab the * timestamp from the queue and convert. @@ -4019,16 +4042,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, skb_hwtstamps(skb)); } - err = mvpp2_rx_refill(port, bm_pool, pp, pool); - if (err) { - netdev_err(port->dev, "failed to refill BM pools\n"); - dev_kfree_skb_any(skb); - goto err_drop_frame; - } - - if (pp) - skb_mark_for_recycle(skb); - else + if (!pp) dma_unmap_single_attrs(dev->dev.parent, dma_addr, bm_pool->buf_size, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); @@ -4036,7 +4050,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, ps.rx_packets++; ps.rx_bytes += rx_bytes; - skb_reserve(skb, MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM); + skb_reserve(skb, rx_offset); skb_put(skb, rx_bytes); if (metasize) skb_metadata_set(skb, metasize); @@ -4047,13 +4061,14 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, continue; err_drop_frame: - dev->stats.rx_errors++; - mvpp2_rx_error(port, rx_desc); /* Return the buffer to the pool */ if (rx_status & MVPP2_RXD_BUF_HDR) mvpp2_buff_hdr_pool_put(port, rx_desc, pool, rx_status); else mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr); +err_drop_frame_retired: + dev->stats.rx_errors++; + mvpp2_rx_error(port, rx_desc); } if (xdp_ret & MVPP2_XDP_REDIR) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index 6b3f453fd500..fe8c4ffcd8f7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -1571,53 +1571,49 @@ static u8 npc_map2cn20k_flag(u8 flag) return 0xff; } +static void npc_cn20k_translate_action_flags(struct npc_kpu_profile_action *act) +{ + u8 ltype, val; + + if (act->lid != NPC_LID_LC) + return; + + ltype = act->ltype; + if (ltype != NPC_LT_LC_IP && + ltype != NPC_LT_LC_IP6 && + ltype != NPC_LT_LC_IP_OPT && + ltype != NPC_LT_LC_IP6_EXT) + return; + + switch (act->flags) { + case NPC_F_LC_U_IP_FRAG: + case NPC_F_LC_U_IP6_FRAG: + case NPC_F_LC_L_6TO4: + case NPC_F_LC_L_MPLS_IN_IP: + case NPC_F_LC_L_IP6_TUN_IP6: + case NPC_F_LC_L_IP6_MPLS_IN_IP: + val = npc_map2cn20k_flag(act->flags); + if (val != 0xFF) + act->flags = val; + break; + default: + break; + } +} + void npc_cn20k_update_action_entries_n_flags(struct rvu *rvu, struct npc_kpu_profile_adapter *pfl) { struct npc_kpu_profile_action *action; - int entries, ltype; - u8 flags, val; + int entries; for (int i = 0; i < pfl->kpus; i++) { action = pfl->kpu[i].action; entries = pfl->kpu[i].action_entries; - for (int j = 0; j < entries; j++) { - if (action[j].lid != NPC_LID_LC) - continue; - - ltype = action[j].ltype; - - if (ltype != NPC_LT_LC_IP && - ltype != NPC_LT_LC_IP6 && - ltype != NPC_LT_LC_IP_OPT && - ltype != NPC_LT_LC_IP6_EXT) - continue; - - flags = action[j].flags; - - switch (flags) { - case NPC_F_LC_U_IP_FRAG: - case NPC_F_LC_U_IP6_FRAG: - case NPC_F_LC_L_6TO4: - case NPC_F_LC_L_MPLS_IN_IP: - case NPC_F_LC_L_IP6_TUN_IP6: - case NPC_F_LC_L_IP6_MPLS_IN_IP: - val = npc_map2cn20k_flag(flags); - if (val == 0xFF) { - dev_err(rvu->dev, - "%s: Error to get flag value\n", - __func__); - return; - } - - action[j].flags = val; - break; - default: - break; - } - } + for (int j = 0; j < entries; j++) + npc_cn20k_translate_action_flags(&action[j]); } } @@ -1709,9 +1705,9 @@ int npc_cn20k_apply_custom_kpu(struct rvu *rvu, for (entry = 0; entry < entries; entry++) { profile->kpu[kpu].cam[entry] = cam[entry]; profile->kpu[kpu].action[entry] = action[entry]; + npc_cn20k_translate_action_flags(&profile->kpu[kpu].action[entry]); } } - npc_cn20k_update_action_entries_n_flags(rvu, profile); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index e40b79076358..6e907ee19164 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -436,7 +436,7 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc) return &rvu->pf[rvu_get_pf(rvu->pdev, pcifunc)]; } -static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc) +bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc) { int pf, vf, nvfs; u64 cfg; @@ -1160,7 +1160,7 @@ cpt: err = rvu_npc_exact_init(rvu); if (err) { dev_err(rvu->dev, "failed to initialize exact match table\n"); - return err; + goto cgx_err; } /* Assign MACs for CGX mapped functions */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index a466181cf908..65397daae4c2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -917,6 +917,7 @@ u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blkaddr); struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc); void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf); bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr); +bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc); bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype); int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot); int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf); @@ -1144,6 +1145,7 @@ int rvu_cpt_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int lf, int slot); int rvu_cpt_ctx_flush(struct rvu *rvu, u16 pcifunc); int rvu_cpt_init(struct rvu *rvu); +u32 rvu_get_cpt_chan_mask(struct rvu *rvu); #define NDC_AF_BANK_MASK GENMASK_ULL(7, 0) #define NDC_AF_BANK_LINE_MASK GENMASK_ULL(31, 16) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 607d0cf1a778..d301a3f0f87a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -701,6 +701,19 @@ void npc_set_mcam_action(struct rvu *rvu, struct npc_mcam *mcam, return rvu_write64(rvu, blkaddr, reg, cfg); } +u32 rvu_get_cpt_chan_mask(struct rvu *rvu) +{ + /* For cn10k the upper two bits of the channel number are + * cpt channel number. with masking out these bits in the + * mcam entry, same entry used for NIX will allow packets + * received from cpt for parsing. + */ + if (!is_rvu_otx2(rvu)) + return NIX_CHAN_CPT_X2P_MASK; + else + return 0xFFFu; +} + void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u64 chan, u8 *mac_addr) { @@ -750,7 +763,7 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, eth_broadcast_addr((u8 *)&req.mask.dmac); req.features = BIT_ULL(NPC_DMAC); req.channel = chan; - req.chan_mask = 0xFFFU; + req.chan_mask = rvu_get_cpt_chan_mask(rvu); req.intf = pfvf->nix_rx_intf; req.op = action.op; req.hdr.pcifunc = 0; /* AF is requester */ @@ -845,11 +858,7 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc, * mcam entry, same entry used for NIX will allow packets * received from cpt for parsing. */ - if (!is_rvu_otx2(rvu)) { - req.chan_mask = NIX_CHAN_CPT_X2P_MASK; - } else { - req.chan_mask = 0xFFFU; - } + req.chan_mask = rvu_get_cpt_chan_mask(rvu); if (chan_cnt > 1) { if (!is_power_of_2(chan_cnt)) { @@ -1053,16 +1062,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, ether_addr_copy(req.mask.dmac, mac_addr); req.features = BIT_ULL(NPC_DMAC); - /* For cn10k the upper two bits of the channel number are - * cpt channel number. with masking out these bits in the - * mcam entry, same entry used for NIX will allow packets - * received from cpt for parsing. - */ - if (!is_rvu_otx2(rvu)) - req.chan_mask = NIX_CHAN_CPT_X2P_MASK; - else - req.chan_mask = 0xFFFU; - + req.chan_mask = rvu_get_cpt_chan_mask(rvu); req.channel = chan; req.intf = pfvf->nix_rx_intf; req.entry = index; @@ -2192,8 +2192,8 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) goto free_entry_cntr_map; /* Alloc memory for saving target device of mcam rule */ - mcam->entry2target_pffunc = kmalloc_array(mcam->total_entries, - sizeof(u16), GFP_KERNEL); + mcam->entry2target_pffunc = kcalloc(mcam->total_entries, + sizeof(u16), GFP_KERNEL); if (!mcam->entry2target_pffunc) goto free_cntr_refcnt; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 6ae9cdcb608b..34f1e066707b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -1820,7 +1820,7 @@ process_flow: /* ignore chan_mask in case pf func is not AF, revisit later */ if (!is_pffunc_af(req->hdr.pcifunc)) - req->chan_mask = 0xFFF; + req->chan_mask = rvu_get_cpt_chan_mask(rvu); err = npc_check_unsupported_flows(rvu, req->features, req->intf); if (err) { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c index 901f6fd40fd4..a2781e0f504e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c @@ -97,6 +97,14 @@ int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req, { struct rep_evtq_ent *qentry; + /* The mailbox dispatcher normalises only the header pcifunc; the + * nested struct rep_event::pcifunc body field is sender-controlled + * and is later used by rvu_rep_up_notify() to index rvu->pf[] / + * rvu->hwvf[]. Reject out-of-range body selectors before queueing. + */ + if (!is_pf_func_valid(rvu, req->pcifunc)) + return -EINVAL; + qentry = kmalloc_obj(*qentry, GFP_ATOMIC); if (!qentry) return -ENOMEM; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index ee623476e5ff..f9fbf0c17648 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -3473,7 +3473,7 @@ static void otx2_ndc_sync(struct otx2_nic *pf) req->nix_lf_rx_sync = 1; req->npa_lf_sync = 1; - if (!otx2_sync_mbox_msg(mbox)) + if (otx2_sync_mbox_msg(mbox)) dev_err(pf->dev, "NDC sync operation failed\n"); mutex_unlock(&mbox->lock); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 8d225bc9f063..7d771168b990 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4491,7 +4491,7 @@ static int mtk_free_dev(struct mtk_eth *eth) for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { if (!eth->dsa_meta[i]) break; - metadata_dst_free(eth->dsa_meta[i]); + dst_release(ð->dsa_meta[i]->dst); } return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index e130e7259275..5c55971abbf0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -290,6 +290,7 @@ static void mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn) static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size) { int entries_per_copy = PAGE_SIZE / cqe_size; + size_t copy_bytes; void *init_ents; int err = 0; int i; @@ -314,8 +315,14 @@ static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size) buf += PAGE_SIZE; } } else { + copy_bytes = array_size(entries, cqe_size); + if (WARN_ON_ONCE(copy_bytes > PAGE_SIZE)) { + err = -EINVAL; + goto out; + } + err = copy_to_user((void __user *)buf, init_ents, - array_size(entries, cqe_size)) ? + copy_bytes) ? -EFAULT : 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index c89417c1a1f9..e2895972cc82 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1002,12 +1002,13 @@ static void cmd_work_handler(struct work_struct *work) ent->callback(-EBUSY, ent->context); mlx5_free_cmd_msg(dev, ent->out); free_msg(dev, ent->in); + complete(&ent->slotted); cmd_ent_put(ent); } else { ent->ret = -EBUSY; complete(&ent->done); + complete(&ent->slotted); } - complete(&ent->slotted); return; } alloc_ret = cmd_alloc_index(cmd, ent); @@ -1017,13 +1018,14 @@ static void cmd_work_handler(struct work_struct *work) ent->callback(-EAGAIN, ent->context); mlx5_free_cmd_msg(dev, ent->out); free_msg(dev, ent->in); + complete(&ent->slotted); cmd_ent_put(ent); } else { ent->ret = -EAGAIN; complete(&ent->done); + complete(&ent->slotted); } up(&cmd->vars.sem); - complete(&ent->slotted); return; } } else { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index 190b8b66b3ce..d8c7cb8837d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -103,9 +103,15 @@ mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_rq *rq, xdptxd->dma_addr = dma_addr; - if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, - mlx5e_xmit_xdp_frame, sq, xdptxd, 0, NULL))) + if (unlikely(!INDIRECT_CALL_2(sq->xmit_xdp_frame, + mlx5e_xmit_xdp_frame_mpwqe, + mlx5e_xmit_xdp_frame, + sq, xdptxd, 0, NULL))) { + dma_unmap_single(sq->pdev, dma_addr, xdptxd->len, + DMA_TO_DEVICE); + xdp_return_frame(xdpf); return false; + } /* xmit_mode == MLX5E_XDP_XMIT_MODE_FRAME */ mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo, @@ -708,7 +714,7 @@ static void mlx5e_free_xdpsq_desc(struct mlx5e_xdpsq *sq, xdpi = mlx5e_xdpi_fifo_pop(xdpi_fifo); page = xdpi.page.page; - /* No need to check PageNetpp() as we + /* No need to check page_pool_page_is_pp() as we * know this is a page_pool page. */ page_pool_recycle_direct(pp_page_to_nmdesc(page)->pp, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 7c8311f41232..236f89a6483a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -533,23 +533,16 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, struct mlx5_vport *vport, int list_type) { bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC; - u8 (*mac_list)[ETH_ALEN]; + u8 (*mac_list)[ETH_ALEN] = NULL; struct l2addr_node *node; struct vport_addr *addr; struct hlist_head *hash; struct hlist_node *tmp; - int size; + int size = 0; int err; int hi; int i; - size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) : - MLX5_MAX_MC_PER_VPORT(esw->dev); - - mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL); - if (!mac_list) - return; - hash = is_uc ? vport->uc_list : vport->mc_list; for_each_l2hash_node(node, tmp, hash, hi) { @@ -561,7 +554,7 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, goto out; err = mlx5_query_nic_vport_mac_list(esw->dev, vport->vport, list_type, - mac_list, &size); + &mac_list, &size); if (err) goto out; esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c index 994fe83da4be..a0bb8ee44e35 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c @@ -105,9 +105,12 @@ irq_pool_find_least_loaded(struct mlx5_irq_pool *pool, const struct cpumask *req lockdep_assert_held(&pool->lock); xa_for_each_range(&pool->irqs, index, iter, start, end) { - struct cpumask *iter_mask = mlx5_irq_get_affinity_mask(iter); int iter_refcount = mlx5_irq_read_locked(iter); + const struct cpumask *iter_mask; + iter_mask = irq_get_effective_affinity_mask(mlx5_irq_get_irq(iter)); + if (!iter_mask) + continue; if (!cpumask_subset(iter_mask, req_mask)) /* skip IRQs with a mask which is not subset of req_mask */ continue; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c index d785f1b4f2e1..5df786133e4b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c @@ -340,7 +340,7 @@ static int mlx5_ptp_getcrosststamp(struct ptp_clock_info *ptp, goto unlock; } - ktime_get_snapshot(&history_begin); + ktime_get_snapshot_id(cts->clock_id, &history_begin); err = get_device_system_crosststamp(mlx5_mtctr_syncdevicetime, mdev, &history_begin, cts); @@ -366,7 +366,7 @@ static int mlx5_ptp_getcrosscycles(struct ptp_clock_info *ptp, goto unlock; } - ktime_get_snapshot(&history_begin); + ktime_get_snapshot_id(cts->clock_id, &history_begin); err = get_device_system_crosststamp(mlx5_mtctr_syncdevicecyclestime, mdev, &history_begin, cts); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c index aca77853abb8..5a172c572a68 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c @@ -1320,8 +1320,10 @@ mlx5_cmd_hws_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns, break; case MLX5_REFORMAT_TYPE_REMOVE_HDR: hws_action = mlx5_fs_get_action_remove_header_vlan(fs_ctx, params); - if (!hws_action) + if (!hws_action) { mlx5_core_err(dev, "Only vlan remove header supported\n"); + return -EOPNOTSUPP; + } break; default: mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 4effe37fd455..d63b0e8806b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -324,35 +324,63 @@ int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu) } EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mtu); +static int mlx5_vport_max_mac_list_size(struct mlx5_core_dev *dev, u16 vport, + enum mlx5_list_type list_type) +{ + void *query_ctx, *hca_caps; + int ret = 0; + + if (!vport && !mlx5_core_is_ecpf(dev)) + return list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : + 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + + query_ctx = kzalloc(MLX5_ST_SZ_BYTES(query_hca_cap_out), GFP_KERNEL); + if (!query_ctx) + return -ENOMEM; + + ret = mlx5_vport_get_other_func_general_cap(dev, vport, query_ctx); + if (ret) + goto out; + + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); + ret = list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_uc_list) : + 1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_mc_list); + +out: + kfree(query_ctx); + + return ret; +} + int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, u16 vport, enum mlx5_list_type list_type, - u8 addr_list[][ETH_ALEN], - int *list_size) + u8 (**addr_list)[ETH_ALEN], + int *addr_list_size) { u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {0}; + int allowed_list_size; void *nic_vport_ctx; int max_list_size; - int req_list_size; int out_sz; void *out; int err; int i; - req_list_size = *list_size; + if (!addr_list || !addr_list_size) + return -EINVAL; - max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ? - 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : - 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + *addr_list = NULL; + *addr_list_size = 0; - if (req_list_size > max_list_size) { - mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n", - req_list_size, max_list_size); - req_list_size = max_list_size; - } + max_list_size = mlx5_vport_max_mac_list_size(dev, vport, list_type); + if (max_list_size < 0) + return max_list_size; out_sz = MLX5_ST_SZ_BYTES(query_nic_vport_context_out) + - req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); + max_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); out = kvzalloc(out_sz, GFP_KERNEL); if (!out) @@ -371,16 +399,24 @@ int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out, nic_vport_context); - req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, - allowed_list_size); + allowed_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, + allowed_list_size); + if (!allowed_list_size) + goto out; + + *addr_list = kcalloc(allowed_list_size, ETH_ALEN, GFP_KERNEL); + if (!*addr_list) { + err = -ENOMEM; + goto out; + } - *list_size = req_list_size; - for (i = 0; i < req_list_size; i++) { + for (i = 0; i < allowed_list_size; i++) { u8 *mac_addr = MLX5_ADDR_OF(nic_vport_context, nic_vport_ctx, current_uc_mac_address[i]) + 2; - ether_addr_copy(addr_list[i], mac_addr); + ether_addr_copy((*addr_list)[i], mac_addr); } + *addr_list_size = allowed_list_size; out: kvfree(out); return err; diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index f3332417162e..ffac22883e49 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -1219,6 +1219,36 @@ static void lan743x_mac_set_address(struct lan743x_adapter *adapter, "MAC address set to %pM\n", addr); } +static void lan743x_mac_rx_enable_fse(struct lan743x_adapter *adapter) +{ + u32 mac_rx; + bool rxen; + + mac_rx = lan743x_csr_read(adapter, MAC_RX); + if (mac_rx & MAC_RX_FSE_) + return; + + rxen = mac_rx & MAC_RX_RXEN_; + if (rxen) { + mac_rx &= ~MAC_RX_RXEN_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + lan743x_csr_wait_for_bit(adapter, MAC_RX, MAC_RX_RXD_, + 1, 1000, 20000, 100); + } + + /* Per AN2948, hardware prevents modification of the FSE bit while the + * MAC receiver is enabled (RXEN bit set). Use separate register write + * to assert the FSE bit before enabling the RXEN bit in MAC_RX + */ + mac_rx |= MAC_RX_FSE_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + + if (rxen) { + mac_rx |= MAC_RX_RXEN_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + } +} + static int lan743x_mac_init(struct lan743x_adapter *adapter) { bool mac_address_valid = true; @@ -1258,6 +1288,8 @@ static int lan743x_mac_init(struct lan743x_adapter *adapter) lan743x_mac_set_address(adapter, adapter->mac_address); eth_hw_addr_set(netdev, adapter->mac_address); + lan743x_mac_rx_enable_fse(adapter); + return 0; } diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index 160d94a7cee6..1573c8f9c993 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -182,6 +182,7 @@ #define MAC_RX (0x104) #define MAC_RX_MAX_SIZE_SHIFT_ (16) #define MAC_RX_MAX_SIZE_MASK_ (0x3FFF0000) +#define MAC_RX_FSE_ BIT(2) #define MAC_RX_RXD_ BIT(1) #define MAC_RX_RXEN_ BIT(0) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 9afc786b297a..c9b1df1ed109 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -1727,6 +1727,9 @@ static void mana_fence_rqs(struct mana_port_context *apc) struct mana_rxq *rxq; int err; + if (!apc->rxqs) + return; + for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) { rxq = apc->rxqs[rxq_idx]; err = mana_fence_rq(apc, rxq); @@ -2858,13 +2861,16 @@ static void mana_destroy_vport(struct mana_port_context *apc) struct mana_rxq *rxq; u32 rxq_idx; - for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) { - rxq = apc->rxqs[rxq_idx]; - if (!rxq) - continue; + if (apc->rxqs) { + + for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) { + rxq = apc->rxqs[rxq_idx]; + if (!rxq) + continue; - mana_destroy_rxq(apc, rxq, true); - apc->rxqs[rxq_idx] = NULL; + mana_destroy_rxq(apc, rxq, true); + apc->rxqs[rxq_idx] = NULL; + } } mana_destroy_txq(apc); @@ -3269,7 +3275,8 @@ static int mana_dealloc_queues(struct net_device *ndev) if (apc->port_is_up) return -EINVAL; - mana_chn_setxdp(apc, NULL); + if (apc->rxqs) + mana_chn_setxdp(apc, NULL); if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) mana_pf_deregister_filter(apc); @@ -3287,33 +3294,38 @@ static int mana_dealloc_queues(struct net_device *ndev) * number of queues. */ - for (i = 0; i < apc->num_queues; i++) { - txq = &apc->tx_qp[i].txq; - tsleep = 1000; - while (atomic_read(&txq->pending_sends) > 0 && - time_before(jiffies, timeout)) { - usleep_range(tsleep, tsleep + 1000); - tsleep <<= 1; - } - if (atomic_read(&txq->pending_sends)) { - err = pcie_flr(to_pci_dev(gd->gdma_context->dev)); - if (err) { - netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n", - err, atomic_read(&txq->pending_sends), - txq->gdma_txq_id); + if (apc->tx_qp) { + for (i = 0; i < apc->num_queues; i++) { + txq = &apc->tx_qp[i].txq; + tsleep = 1000; + while (atomic_read(&txq->pending_sends) > 0 && + time_before(jiffies, timeout)) { + usleep_range(tsleep, tsleep + 1000); + tsleep <<= 1; + } + if (atomic_read(&txq->pending_sends)) { + err = + pcie_flr(to_pci_dev(gd->gdma_context->dev)); + if (err) { + netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n", + err, + atomic_read(&txq->pending_sends), + txq->gdma_txq_id); + } + break; } - break; } - } - for (i = 0; i < apc->num_queues; i++) { - txq = &apc->tx_qp[i].txq; - while ((skb = skb_dequeue(&txq->pending_skbs))) { - mana_unmap_skb(skb, apc); - dev_kfree_skb_any(skb); + for (i = 0; i < apc->num_queues; i++) { + txq = &apc->tx_qp[i].txq; + while ((skb = skb_dequeue(&txq->pending_skbs))) { + mana_unmap_skb(skb, apc); + dev_kfree_skb_any(skb); + } + atomic_set(&txq->pending_sends, 0); } - atomic_set(&txq->pending_sends, 0); } + /* We're 100% sure the queues can no longer be woken up, because * we're sure now mana_poll_tx_cq() can't be running. */ @@ -3338,6 +3350,12 @@ int mana_detach(struct net_device *ndev, bool from_close) ASSERT_RTNL(); + /* If already detached (indicates detach succeeded but attach failed + * previously). Now skip mana detach and just retry mana_attach. + */ + if (!from_close && !netif_device_present(ndev)) + return 0; + apc->port_st_save = apc->port_is_up; apc->port_is_up = false; diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index ef13109c49cf..55105d34bc79 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -239,6 +239,8 @@ static void rtase_tx_clear(struct rtase_private *tp) rtase_tx_clear_range(ring, ring->dirty_idx, RTASE_NUM_DESC); ring->cur_idx = 0; ring->dirty_idx = 0; + + netdev_tx_reset_subqueue(tp->dev, i); } } @@ -1563,8 +1565,9 @@ static void rtase_dump_tally_counter(const struct rtase_private *tp) rtase_w32(tp, RTASE_DTCCR0, cmd); rtase_w32(tp, RTASE_DTCCR0, cmd | RTASE_COUNTER_DUMP); - err = read_poll_timeout(rtase_r32, val, !(val & RTASE_COUNTER_DUMP), - 10, 250, false, tp, RTASE_DTCCR0); + err = read_poll_timeout_atomic(rtase_r32, val, + !(val & RTASE_COUNTER_DUMP), + 10, 250, false, tp, RTASE_DTCCR0); if (err == -ETIMEDOUT) netdev_err(tp->dev, "error occurred in dump tally counter\n"); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 7898b5075a8b..b8d467ba6d72 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -6,6 +6,7 @@ #include <linux/pci.h> #include <linux/dmi.h> #include <linux/platform_data/x86/intel_pmc_ipc.h> +#include <asm/cpuid/api.h> #include "dwmac-intel.h" #include "dwmac4.h" #include "stmmac.h" diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index f0514251d4f3..8fc32df8e49a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -204,7 +204,7 @@ int txgbe_set_phy_link(struct wx *wx) static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) { __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - DECLARE_PHY_INTERFACE_MASK(interfaces); + DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces); struct txgbe *txgbe = wx->priv; if (id->cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE) { @@ -271,7 +271,7 @@ static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) { __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - DECLARE_PHY_INTERFACE_MASK(interfaces); + DECLARE_PHY_INTERFACE_MASK_ZERO(interfaces); struct txgbe *txgbe = wx->priv; if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_CR4) { @@ -335,7 +335,7 @@ static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) int txgbe_identify_module(struct wx *wx) { - struct txgbe_hic_get_module_info buffer; + struct txgbe_hic_get_module_info buffer = { 0 }; struct txgbe_sff_id *id; int err = 0; u32 mod_abs; @@ -357,18 +357,16 @@ int txgbe_identify_module(struct wx *wx) } id = &buffer.id; - if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP_PLUS && - id->identifier != TXGBE_SFF_IDENTIFIER_QSFP28) { - wx_err(wx, "Invalid module\n"); - return -ENODEV; - } - - if (id->transceiver_type == 0xFF) + if (id->identifier == TXGBE_SFF_IDENTIFIER_SFP) return txgbe_sfp_to_linkmodes(wx, id); - return txgbe_qsfp_to_linkmodes(wx, id); + if (id->identifier == TXGBE_SFF_IDENTIFIER_QSFP || + id->identifier == TXGBE_SFF_IDENTIFIER_QSFP_PLUS || + id->identifier == TXGBE_SFF_IDENTIFIER_QSFP28) + return txgbe_qsfp_to_linkmodes(wx, id); + + wx_err(wx, "Invalid module\n"); + return -EINVAL; } void txgbe_setup_link(struct wx *wx) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 6b05f32b4a01..877234e3fdc2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -315,6 +315,9 @@ void txgbe_up(struct wx *wx); int txgbe_setup_tc(struct net_device *dev, u8 tc); void txgbe_do_reset(struct net_device *netdev); +#define DECLARE_PHY_INTERFACE_MASK_ZERO(name) \ + unsigned long name[PHY_INTERFACE_MODE_MAX] = { 0, } + #define TXGBE_LINK_SPEED_UNKNOWN 0 #define TXGBE_LINK_SPEED_10GB_FULL 4 #define TXGBE_LINK_SPEED_25GB_FULL 0x10 diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index c6563367d382..715180c3a1b3 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -632,7 +632,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, uh = udp_hdr(skb); uh->len = htons(skb->len - gro_hint->nested_tp_offset); if (uh->check) { - len = skb->len - gro_hint->nested_nh_offset; + len = skb->len - gro_hint->nested_tp_offset; skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM; if (gro_hint->nested_is_v6) uh->check = ~udp_v6_check(len, &ipv6h->saddr, diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 59e95341f9b1..4d319c50955e 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -12,6 +12,7 @@ #include <linux/sched.h> #include <linux/wait.h> #include <linux/mm.h> +#include <linux/highmem.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/slab.h> @@ -965,12 +966,22 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device, } for (i = 0; i < page_count; i++) { - char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT); - u32 offset = pb[i].offset; + phys_addr_t paddr = (pb[i].pfn << HV_HYP_PAGE_SHIFT) + + pb[i].offset; u32 len = pb[i].len; - memcpy(dest, (src + offset), len); - dest += len; + while (len) { + struct page *page = phys_to_page(paddr); + u32 off = offset_in_page(paddr); + u32 chunk = min_t(u32, len, PAGE_SIZE - off); + char *src = kmap_local_page(page); + + memcpy(dest, src + off, chunk); + kunmap_local(src); + dest += chunk; + paddr += chunk; + len -= chunk; + } } if (padding) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index f904f4d16b45..fb009120a924 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -808,7 +808,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u if (pn + 1 > rx_sa->next_pn_halves.lower) { rx_sa->next_pn_halves.lower = pn + 1; } else if (secy->xpn && - !pn_same_half(pn, rx_sa->next_pn_halves.lower)) { + (pn + 1 == 0 || + !pn_same_half(pn, rx_sa->next_pn_halves.lower))) { rx_sa->next_pn_halves.upper++; rx_sa->next_pn_halves.lower = pn + 1; } diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c index 3b5dff144177..fade65f2f269 100644 --- a/drivers/net/mctp/mctp-usb.c +++ b/drivers/net/mctp/mctp-usb.c @@ -22,7 +22,6 @@ struct mctp_usb { struct usb_device *usbdev; struct usb_interface *intf; - bool stopped; struct net_device *netdev; @@ -32,6 +31,9 @@ struct mctp_usb { struct urb *tx_urb; struct urb *rx_urb; + /* enforces atomic access to rx_stopped and requeuing the retry work */ + spinlock_t rx_lock; + bool rx_stopped; struct delayed_work rx_retry_work; }; @@ -122,6 +124,7 @@ static const unsigned long RX_RETRY_DELAY = HZ / 4; static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) { + unsigned long flags; struct sk_buff *skb; int rc; @@ -147,8 +150,11 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) return rc; err_retry: - schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); - return rc; + spin_lock_irqsave(&mctp_usb->rx_lock, flags); + if (!mctp_usb->rx_stopped) + schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); + spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); + return 0; } static void mctp_usb_in_complete(struct urb *urb) @@ -248,9 +254,6 @@ static void mctp_usb_rx_retry_work(struct work_struct *work) struct mctp_usb *mctp_usb = container_of(work, struct mctp_usb, rx_retry_work.work); - if (READ_ONCE(mctp_usb->stopped)) - return; - mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); } @@ -258,7 +261,7 @@ static int mctp_usb_open(struct net_device *dev) { struct mctp_usb *mctp_usb = netdev_priv(dev); - WRITE_ONCE(mctp_usb->stopped, false); + WRITE_ONCE(mctp_usb->rx_stopped, false); netif_start_queue(dev); @@ -268,17 +271,21 @@ static int mctp_usb_open(struct net_device *dev) static int mctp_usb_stop(struct net_device *dev) { struct mctp_usb *mctp_usb = netdev_priv(dev); + unsigned long flags; netif_stop_queue(dev); /* prevent RX submission retry */ - WRITE_ONCE(mctp_usb->stopped, true); + spin_lock_irqsave(&mctp_usb->rx_lock, flags); + mctp_usb->rx_stopped = true; + cancel_delayed_work(&mctp_usb->rx_retry_work); + spin_unlock_irqrestore(&mctp_usb->rx_lock, flags); + + flush_delayed_work(&mctp_usb->rx_retry_work); usb_kill_urb(mctp_usb->rx_urb); usb_kill_urb(mctp_usb->tx_urb); - cancel_delayed_work_sync(&mctp_usb->rx_retry_work); - return 0; } @@ -331,6 +338,7 @@ static int mctp_usb_probe(struct usb_interface *intf, dev->netdev = netdev; dev->usbdev = interface_to_usbdev(intf); dev->intf = intf; + spin_lock_init(&dev->rx_lock); usb_set_intfdata(intf, dev); dev->ep_in = ep_in->bEndpointAddress; diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c index c12f8087af9b..a753bd88cbc2 100644 --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c @@ -129,6 +129,9 @@ static int mtk_pcs_config_polarity(struct mtk_pcs_lynxi *mpcs, unsigned int val = 0; int ret; + if (!fwnode) + return 0; + if (fwnode_property_read_bool(fwnode, "mediatek,pnswap")) default_pol = PHY_POL_INVERT; diff --git a/drivers/net/pcs/pcs-xpcs-plat.c b/drivers/net/pcs/pcs-xpcs-plat.c index b8c48f9effbf..f4b1b8246ce9 100644 --- a/drivers/net/pcs/pcs-xpcs-plat.c +++ b/drivers/net/pcs/pcs-xpcs-plat.c @@ -349,7 +349,7 @@ static int xpcs_plat_init_dev(struct dw_xpcs_plat *pxpcs) * up later. Make sure DD-core is aware of the OF-node being re-used. */ device_set_node(&mdiodev->dev, fwnode_handle_get(dev_fwnode(dev))); - mdiodev->dev.of_node_reused = true; + dev_set_of_node_reused(&mdiodev->dev); /* Pass the data further so the DW XPCS driver core could use it */ mdiodev->dev.platform_data = (void *)device_get_match_data(dev); diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index 29ae73e65caa..a86129ce693c 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -17,6 +17,7 @@ #include <linux/phy.h> #include <linux/phy/phy-common-props.h> #include <linux/firmware.h> +#include <linux/bitfield.h> #include <linux/property.h> #include <linux/wordpart.h> #include <linux/unaligned.h> @@ -170,9 +171,23 @@ #define AN8811HB_CLK_DRV_CKO_LDPWD BIT(13) #define AN8811HB_CLK_DRV_CKO_LPPWD BIT(14) +#define AN8811HB_MCU_SW_RST 0x5cf9f8 +#define AN8811HB_MCU_SW_RST_HOLD BIT(16) +#define AN8811HB_MCU_SW_RST_RUN (BIT(16) | BIT(0)) +#define AN8811HB_MCU_SW_START 0x5cf9fc +#define AN8811HB_MCU_SW_START_EN BIT(16) + +/* MII register constants for PBUS access (PHY addr + 8) */ +#define AIR_PBUS_ADDR_HIGH 0x1c +#define AIR_PBUS_DATA_HIGH 0x10 +#define AIR_PBUS_REG_ADDR_HIGH_MASK GENMASK(15, 6) +#define AIR_PBUS_REG_ADDR_LOW_MASK GENMASK(5, 2) + /* Led definitions */ #define EN8811H_LED_COUNT 3 +#define EN8811H_PBUS_ADDR_OFFS 8 + /* Default LED setup: * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx * GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps @@ -201,6 +216,7 @@ struct en8811h_priv { struct clk_hw hw; struct phy_device *phydev; unsigned int cko_is_enabled; + struct mdio_device *pbusdev; }; enum { @@ -254,6 +270,31 @@ static int air_phy_write_page(struct phy_device *phydev, int page) return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); } +static int __air_pbus_reg_write(struct mdio_device *mdiodev, + u32 pbus_reg, u32 pbus_data) +{ + int ret; + + ret = __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_EXT_PAGE_ACCESS, + upper_16_bits(pbus_reg)); + if (ret < 0) + return ret; + + ret = __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_PBUS_ADDR_HIGH, + FIELD_GET(AIR_PBUS_REG_ADDR_HIGH_MASK, pbus_reg)); + if (ret < 0) + return ret; + + ret = __mdiobus_write(mdiodev->bus, mdiodev->addr, + FIELD_GET(AIR_PBUS_REG_ADDR_LOW_MASK, pbus_reg), + lower_16_bits(pbus_data)); + if (ret < 0) + return ret; + + return __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_PBUS_DATA_HIGH, + upper_16_bits(pbus_data)); +} + static int __air_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, u32 pbus_data) { @@ -570,10 +611,67 @@ static int an8811hb_load_file(struct phy_device *phydev, const char *name, return ret; } +static int an8811hb_mcu_assert(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + int ret; + + phy_lock_mdio_bus(phydev); + + ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_RST, + AN8811HB_MCU_SW_RST_HOLD); + if (ret < 0) + goto unlock; + + ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_START, 0); + if (ret < 0) + goto unlock; + + msleep(50); + phydev_dbg(phydev, "MCU asserted\n"); + +unlock: + phy_unlock_mdio_bus(phydev); + return ret; +} + +static int an8811hb_mcu_deassert(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + int ret; + + phy_lock_mdio_bus(phydev); + + ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_START, + AN8811HB_MCU_SW_START_EN); + if (ret < 0) + goto unlock; + + ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_RST, + AN8811HB_MCU_SW_RST_RUN); + if (ret < 0) + goto unlock; + + msleep(50); + phydev_dbg(phydev, "MCU deasserted\n"); + +unlock: + phy_unlock_mdio_bus(phydev); + return ret; +} + static int an8811hb_load_firmware(struct phy_device *phydev) { int ret; + ret = an8811hb_mcu_assert(phydev); + if (ret < 0) + return ret; + + ret = an8811hb_mcu_deassert(phydev); + if (ret < 0) + return ret; + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, EN8811H_FW_CTRL_1_START); if (ret < 0) @@ -662,6 +760,16 @@ static int en8811h_restart_mcu(struct phy_device *phydev) { int ret; + if (phy_id_compare_model(phydev->phy_id, AN8811HB_PHY_ID)) { + ret = an8811hb_mcu_assert(phydev); + if (ret < 0) + return ret; + + ret = an8811hb_mcu_deassert(phydev); + if (ret < 0) + return ret; + } + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, EN8811H_FW_CTRL_1_START); if (ret < 0) @@ -1166,6 +1274,7 @@ static int en8811h_leds_setup(struct phy_device *phydev) static int an8811hb_probe(struct phy_device *phydev) { + struct mdio_device *mdiodev; struct en8811h_priv *priv; int ret; @@ -1175,10 +1284,28 @@ static int an8811hb_probe(struct phy_device *phydev) return -ENOMEM; phydev->priv = priv; + /* + * The AN8811HB PHY address is restricted to 8-15 (decimal), + * depending on the board hardware strapping. + * This means the PBUS address is only in the range 16-21 (decimal), + * so we do not need to handle the case + * where the PBUS address exceeds 31 (decimal). + */ + mdiodev = mdio_device_create(phydev->mdio.bus, + phydev->mdio.addr + EN8811H_PBUS_ADDR_OFFS); + if (IS_ERR(mdiodev)) + return PTR_ERR(mdiodev); + + ret = mdio_device_register(mdiodev); + if (ret) + goto err_dev_free; + + priv->pbusdev = mdiodev; + ret = an8811hb_load_firmware(phydev); if (ret < 0) { phydev_err(phydev, "Load firmware failed: %d\n", ret); - return ret; + goto err_dev_create; } en8811h_print_fw_version(phydev); @@ -1191,22 +1318,29 @@ static int an8811hb_probe(struct phy_device *phydev) ret = en8811h_leds_setup(phydev); if (ret < 0) - return ret; + goto err_dev_create; priv->phydev = phydev; /* Co-Clock Output */ ret = an8811hb_clk_provider_setup(&phydev->mdio.dev, &priv->hw); if (ret) - return ret; + goto err_dev_create; /* Configure led gpio pins as output */ ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, AN8811HB_GPIO_OUTPUT_345, AN8811HB_GPIO_OUTPUT_345); if (ret < 0) - return ret; + goto err_dev_create; return 0; + +err_dev_create: + mdio_device_remove(mdiodev); + +err_dev_free: + mdio_device_free(mdiodev); + return ret; } static int en8811h_probe(struct phy_device *phydev) @@ -1561,6 +1695,16 @@ static int en8811h_suspend(struct phy_device *phydev) return genphy_suspend(phydev); } +static void an8811hb_remove(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + + if (priv->pbusdev) { + mdio_device_remove(priv->pbusdev); + mdio_device_free(priv->pbusdev); + } +} + static struct phy_driver en8811h_driver[] = { { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID), @@ -1587,6 +1731,7 @@ static struct phy_driver en8811h_driver[] = { PHY_ID_MATCH_MODEL(AN8811HB_PHY_ID), .name = "Airoha AN8811HB", .probe = an8811hb_probe, + .remove = an8811hb_remove, .get_features = en8811h_get_features, .config_init = an8811hb_config_init, .get_rate_matching = en8811h_get_rate_matching, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3370eb822017..1511385b9b36 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1718,6 +1718,9 @@ static int phy_sfp_probe(struct phy_device *phydev) ret = sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); sfp_bus_put(bus); + + if (ret) + phydev->sfp_bus = NULL; } if (!ret && phydev->sfp_bus) @@ -3509,9 +3512,15 @@ static int phy_setup_ports(struct phy_device *phydev) if (ret) return ret; - ret = phy_sfp_probe(phydev); - if (ret) - goto out; + /* We don't support SFP with genphy drivers. Also, genphy driver + * binding occurs with RTNL help, which will deadlock the call to + * sfp_bus_add_upstream(). + */ + if (!phydev->is_genphy_driven) { + ret = phy_sfp_probe(phydev); + if (ret) + goto out; + } if (phydev->n_ports < phydev->max_n_ports) { ret = phy_default_setup_single_port(phydev); @@ -3775,6 +3784,11 @@ static int phy_probe(struct device *dev) return 0; out: + sfp_bus_del_upstream(phydev->sfp_bus); + phydev->sfp_bus = NULL; + + phy_cleanup_ports(phydev); + if (!phydev->is_on_sfp_module) phy_led_triggers_unregister(phydev); @@ -3798,11 +3812,11 @@ static int phy_remove(struct device *dev) phydev->state = PHY_DOWN; - phy_cleanup_ports(phydev); - sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL; + phy_cleanup_ports(phydev); + if (phydev->drv && phydev->drv->remove) phydev->drv->remove(phydev); diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index bd970f753beb..b94b9c433a21 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -822,6 +822,7 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) return -EINVAL; } + sfp->i2c_block_size = sfp->i2c_max_block_size; return 0; } diff --git a/drivers/net/tap.c b/drivers/net/tap.c index a590e07ce0a9..fae115915c8e 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1052,6 +1052,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) int err, depth; if (unlikely(xdp->data_end - xdp->data < ETH_HLEN)) { + put_page(virt_to_head_page(xdp->data)); err = -EINVAL; goto err; } @@ -1061,6 +1062,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) skb = build_skb(xdp->data_hard_start, buflen); if (!skb) { + put_page(virt_to_head_page(xdp->data)); err = -ENOMEM; goto err; } diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index 0c87f9972457..f51388d50307 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -534,21 +534,23 @@ static void team_adjust_ops(struct team *team) if (!team->tx_en_port_count || !team_is_mode_set(team) || !team->mode->ops->transmit) - team->ops.transmit = team_dummy_transmit; + WRITE_ONCE(team->ops.transmit, team_dummy_transmit); else - team->ops.transmit = team->mode->ops->transmit; + WRITE_ONCE(team->ops.transmit, team->mode->ops->transmit); if (!team->rx_en_port_count || !team_is_mode_set(team) || !team->mode->ops->receive) - team->ops.receive = team_dummy_receive; + WRITE_ONCE(team->ops.receive, team_dummy_receive); else - team->ops.receive = team->mode->ops->receive; + WRITE_ONCE(team->ops.receive, team->mode->ops->receive); } /* - * We can benefit from the fact that it's ensured no port is present - * at the time of mode change. Therefore no packets are in fly so there's no - * need to set mode operations in any special way. + * team_change_mode() ensures no ports are present during mode change, + * but lockless readers can still reach team_xmit(). Avoid touching + * transmit/receive -- they are already set to dummies by + * team_adjust_ops() since no ports are enabled. synchronize_net() + * drains in-flight readers before destroying old mode state. */ static int __team_change_mode(struct team *team, const struct team_mode *new_mode) @@ -557,9 +559,21 @@ static int __team_change_mode(struct team *team, if (team_is_mode_set(team)) { void (*exit_op)(struct team *team) = team->ops.exit; - /* Clear ops area so no callback is called any longer */ - memset(&team->ops, 0, sizeof(struct team_mode_ops)); - team_adjust_ops(team); + /* Clear cold-path ops used only under RTNL. transmit and + * receive are already dummies (no ports) so leave them + * alone -- overwriting them is the source of the race. + */ + team->ops.init = NULL; + team->ops.exit = NULL; + team->ops.port_enter = NULL; + team->ops.port_leave = NULL; + team->ops.port_change_dev_addr = NULL; + team->ops.port_tx_disabled = NULL; + + /* Wait for in-flight readers before tearing down mode + * state they may reference. + */ + synchronize_net(); if (exit_op) exit_op(team); @@ -582,7 +596,12 @@ static int __team_change_mode(struct team *team, } team->mode = new_mode; - memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops)); + team->ops.init = new_mode->ops->init; + team->ops.exit = new_mode->ops->exit; + team->ops.port_enter = new_mode->ops->port_enter; + team->ops.port_leave = new_mode->ops->port_leave; + team->ops.port_change_dev_addr = new_mode->ops->port_change_dev_addr; + team->ops.port_tx_disabled = new_mode->ops->port_tx_disabled; team_adjust_ops(team); return 0; @@ -743,7 +762,7 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) /* allow exact match delivery for disabled ports */ res = RX_HANDLER_EXACT; } else { - res = team->ops.receive(team, port, skb); + res = READ_ONCE(team->ops.receive)(team, port, skb); } if (res == RX_HANDLER_ANOTHER) { struct team_pcpu_stats *pcpu_stats; @@ -1845,7 +1864,7 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev) tx_success = team_queue_override_transmit(team, skb); if (!tx_success) - tx_success = team->ops.transmit(team, skb); + tx_success = READ_ONCE(team->ops.transmit)(team, skb); if (tx_success) { struct team_pcpu_stats *pcpu_stats; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b183189f1853..fed9dfdfcc3b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2070,6 +2070,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, struct virtio_net_hdr_v1_hash_tunnel hdr; struct virtio_net_hdr *gso; + memset(&hdr, 0, sizeof(hdr)); ret = tun_vnet_hdr_tnl_from_skb(tun->flags, tun->dev, skb, &hdr); if (ret) @@ -2394,8 +2395,10 @@ static int tun_xdp_one(struct tun_struct *tun, bool skb_xdp = false; struct page *page; - if (unlikely(datasize < ETH_HLEN)) + if (unlikely(datasize < ETH_HLEN)) { + put_page(virt_to_head_page(xdp->data)); return -EINVAL; + } xdp_prog = rcu_dereference(tun->xdp_prog); if (xdp_prog) { @@ -2437,6 +2440,7 @@ static int tun_xdp_one(struct tun_struct *tun, build: skb = build_skb(xdp->data_hard_start, buflen); if (!skb) { + put_page(virt_to_head_page(xdp->data)); ret = -ENOMEM; goto out; } diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 1ace1d2398c9..b1268553cd70 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -9851,7 +9851,12 @@ static int rtl8152_probe_once(struct usb_interface *intf, struct net_device *netdev; int ret; - usb_reset_device(udev); + ret = usb_reset_device(udev); + if (ret < 0) { + dev_err(&intf->dev, "USB reset failed, errno=%d\n", ret); + return ret; + } + netdev = alloc_etherdev(sizeof(struct r8152)); if (!netdev) { dev_err(&intf->dev, "Out of memory\n"); diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index e88798497503..b5b1253ac08b 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2531,7 +2531,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto out_unlock; } - tos = ip_tunnel_ecn_encap(tos, old_iph, skb); + tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb); ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr), vni, md, flags, udp_sum); @@ -2605,7 +2605,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto out_unlock; } - tos = ip_tunnel_ecn_encap(tos, old_iph, skb); + tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb); ttl = ttl ? : ip6_dst_hoplimit(ndst); skb_scrub_packet(skb, xnet); err = vxlan_build_skb(skb, ndst, sizeof(struct ipv6hdr), diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index 2042369379ff..3e76f4e21094 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -661,7 +661,7 @@ static int vxlan_vni_update(struct vxlan_dev *vxlan, if (ret) return ret; - if (changed) + if (*changed) vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); return 0; @@ -759,8 +759,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan, err = vxlan_vni_update_group(vxlan, vninode, group, true, &changed, extack); - if (changed) - vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); + vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); return err; } diff --git a/drivers/net/wireguard/send.c b/drivers/net/wireguard/send.c index 26e09c30d596..67d01478eb76 100644 --- a/drivers/net/wireguard/send.c +++ b/drivers/net/wireguard/send.c @@ -177,16 +177,6 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair) trailer_len = padding_len + noise_encrypted_len(0); plaintext_len = skb->len + padding_len; - /* Expand data section to have room for padding and auth tag. */ - num_frags = skb_cow_data(skb, trailer_len, &trailer); - if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg))) - return false; - - /* Set the padding to zeros, and make sure it and the auth tag are part - * of the skb. - */ - memset(skb_tail_pointer(trailer), 0, padding_len); - /* Expand head section to have room for our header and the network * stack's headers. */ @@ -198,6 +188,16 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair) skb_checksum_help(skb))) return false; + /* Expand data section to have room for padding and auth tag. */ + num_frags = skb_cow_data(skb, trailer_len, &trailer); + if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg))) + return false; + + /* Set the padding to zeros, and make sure it and the auth tag are part + * of the skb. + */ + memset(skb_tail_pointer(trailer), 0, padding_len); + /* Only after checksumming can we safely add on the padding at the end * and the header. */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c index 5c59acc8c4c5..6598d9333333 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ap.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -9,7 +9,6 @@ #include "ap.h" #include "hcmd.h" #include "tx.h" -#include "power.h" #include "key.h" #include "phy.h" #include "iwl-utils.h" @@ -273,9 +272,6 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx; int ret; - if (vif->type == NL80211_IFTYPE_AP) - iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); - ret = iwl_mld_update_beacon_template(mld, vif, link); if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index da6fd7471568..3c8daddc0bcb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1150,6 +1150,13 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, if (iwl_mld_can_activate_link(mld, vif, link)) { iwl_mld_tlc_update_phy(mld, vif, link); + /* FW requires AP_TX_POWER_CONSTRAINTS_CMD before link + * activation for AP and after link activation for STA, + * for an unknown reason. + */ + if (vif->type == NL80211_IFTYPE_AP) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); + ret = iwl_mld_activate_link(mld, link); if (ret) goto err; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index 49b0d9f8f865..266fe16bb95d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -366,7 +366,7 @@ iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld, lockdep_assert_wiphy(mld->wiphy); - if (!mld_link->active) + if (!mld_link->active && vif->type != NL80211_IFTYPE_AP) return; if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index c65f4b56a327..f829156d42b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -250,7 +250,8 @@ iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp, /* System (wall) time */ ktime_t sys_time; - memset(xtstamp, 0, sizeof(struct system_device_crosststamp)); + if (xtstamp->clock_id != CLOCK_REALTIME) + return -ENOTSUPP; ret = iwl_mld_get_crosstimestamp_fw(mld, &gp2, &sys_time); if (ret) { @@ -270,7 +271,7 @@ iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp, /* System monotonic raw time is not used */ xtstamp->device = ns_to_ktime(gp2_ns); - xtstamp->sys_realtime = sys_time; + xtstamp->sys_systime = sys_time; return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index f05df3a3300e..6e507d6dcdd2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -459,9 +459,14 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, static void iwl_mvm_uats_init(struct iwl_mvm *mvm) { + struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd __free(kfree) = NULL; int cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, MCC_ALLOWED_AP_TYPE_CMD); - struct iwl_mcc_allowed_ap_type_cmd_v1 cmd = {}; + struct iwl_host_cmd hcmd = { + .id = cmd_id, + .len[0] = sizeof(*cmd), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; u8 cmd_ver; int ret; @@ -485,14 +490,25 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) if (!mvm->fwrt.ap_type_cmd_valid) return; + /* Since we free the command immediately after iwl_mvm_send_cmd, we + * must send this command in SYNC mode. + */ + lockdep_assert_held(&mvm->mutex); + + cmd = kzalloc_obj(*cmd); + if (!cmd) + return; + BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) != - sizeof(cmd.mcc_to_ap_type_map)); + sizeof(cmd->mcc_to_ap_type_map)); - memcpy(cmd.mcc_to_ap_type_map, + memcpy(cmd->mcc_to_ap_type_map, mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map, sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map)); - ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + hcmd.data[0] = cmd; + + ret = iwl_mvm_send_cmd(mvm, &hcmd); if (ret < 0) IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", ret); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index ae177477b201..384bed95835d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1416,6 +1416,12 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE); + /* Those firmware versions claim to support the fw_reset_handshake + * but they are buggy. + */ + if (IWL_UCODE_MAJOR(mvm->fw->ucode_ver) <= 77) + trans->conf.fw_reset_handshake = false; + trans->conf.queue_alloc_cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index f7b620136c85..bcd6f7cead2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -160,13 +160,14 @@ iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp, /* System (wall) time */ ktime_t sys_time; - memset(xtstamp, 0, sizeof(struct system_device_crosststamp)); - if (!mvm->ptp_data.ptp_clock) { IWL_ERR(mvm, "No PHC clock registered\n"); return -ENODEV; } + if (xtstamp->clock_id != CLOCK_REALTIME) + return -ENOTSUPP; + mutex_lock(&mvm->mutex); if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SYNCED_TIME)) { ret = iwl_mvm_get_crosstimestamp_fw(mvm, &gp2, &sys_time); @@ -184,7 +185,7 @@ iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp, /* System monotonic raw time is not used */ xtstamp->device = (ktime_t)gp2_ns; - xtstamp->sys_realtime = sys_time; + xtstamp->sys_systime = sys_time; out: mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index dc99e7ac4726..eb3c5a6dd088 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1225,33 +1225,41 @@ static int _iwl_pci_resume(struct device *device, bool restore) if (!trans->op_mode) return 0; - /* - * Scratch value was altered, this means the device was powered off, we - * need to reset it completely. - * Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan, - * but not bits [15:8]. So if we have bits set in lower word, assume - * the device is alive. - * Alternatively, if the scratch value is 0xFFFFFFFF, then we no longer - * have access to the device and consider it powered off. - * For older devices, just try silently to grab the NIC. - */ - if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { - u32 scratch = iwl_read32(trans, CSR_FUNC_SCRATCH); - - if (!(scratch & CSR_FUNC_SCRATCH_POWER_OFF_MASK) || - scratch == ~0U) - device_was_powered_off = true; - } else { + if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) { /* - * bh are re-enabled by iwl_trans_pcie_release_nic_access, - * so re-enable them if _iwl_trans_pcie_grab_nic_access fails. + * Scratch value was altered, this means the device was powered + * off, we need to reset it completely. + * Note: MAC (bits 0:7) will be cleared upon suspend even with + * wowlan, but not bits [15:8]. So if we have bits set in lower + * word, assume the device is alive. + * Alternatively, if the scratch value is 0xFFFFFFFF, then we + * no longer have access to the device and consider it powered + * off. + * For older devices, just try silently to grab the NIC. */ - local_bh_disable(); - if (_iwl_trans_pcie_grab_nic_access(trans, true)) { - iwl_trans_pcie_release_nic_access(trans); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + u32 scratch = iwl_read32(trans, CSR_FUNC_SCRATCH); + + if (!(scratch & CSR_FUNC_SCRATCH_POWER_OFF_MASK) || + scratch == ~0U) { + IWL_DEBUG_WOWLAN(trans, + "Scratch 0x%08x indicates device was powered off\n", + scratch); + device_was_powered_off = true; + } } else { - device_was_powered_off = true; - local_bh_enable(); + /* + * bh are re-enabled by iwl_trans_pcie_release_nic_access, + * so re-enable them if _iwl_trans_pcie_grab_nic_access + * fails. + */ + local_bh_disable(); + if (_iwl_trans_pcie_grab_nic_access(trans, true)) { + iwl_trans_pcie_release_nic_access(trans); + } else { + device_was_powered_off = true; + local_bh_enable(); + } } } diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index b3d34433bd14..a6c08175d9dd 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/module.h> #include <linux/nfc.h> #include <linux/gpio/consumer.h> @@ -267,6 +268,7 @@ static int nxp_nci_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct nxp_nci_i2c_phy *phy; + unsigned long irqflags; int r; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -303,9 +305,26 @@ static int nxp_nci_i2c_probe(struct i2c_client *client) if (r < 0) return r; + /* + * ACPI platforms may report incorrect IRQ trigger types + * (e.g. level-high), which can lead to interrupt storms. + * + * Use the historically stable rising-edge trigger for ACPI devices. + * + * On non-ACPI systems (e.g. Device Tree), prefer the firmware- + * provided trigger type, falling back to rising-edge if not set. + */ + if (ACPI_COMPANION(dev)) { + irqflags = IRQF_TRIGGER_RISING; + } else { + irqflags = irq_get_trigger_type(client->irq); + if (!irqflags) + irqflags = IRQF_TRIGGER_RISING; + } + r = request_threaded_irq(client->irq, NULL, nxp_nci_i2c_irq_thread_fn, - IRQF_ONESHOT, + irqflags | IRQF_ONESHOT, NXP_NCI_I2C_DRIVER_NAME, phy); if (r < 0) nfc_err(&client->dev, "Unable to register IRQ handler\n"); diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c index 559dce302d06..40ce4991c356 100644 --- a/drivers/nubus/nubus.c +++ b/drivers/nubus/nubus.c @@ -41,9 +41,7 @@ module_param_named(populate_procfs, nubus_populate_procfs, bool, 0); LIST_HEAD(nubus_func_rsrcs); -static struct device nubus_parent = { - .init_name = "nubus", -}; +static struct device *nubus_parent; /* Meaning of "bytelanes": @@ -833,7 +831,7 @@ static void __init nubus_add_board(int slot, int bytelanes) list_add_tail(&fres->list, &nubus_func_rsrcs); } - if (nubus_device_register(&nubus_parent, board)) + if (nubus_device_register(nubus_parent, board)) put_device(&board->dev); } @@ -880,18 +878,17 @@ static void __init nubus_scan_bus(void) static int __init nubus_init(void) { - int err; - if (!MACH_IS_MAC) return 0; nubus_proc_init(); - err = device_register(&nubus_parent); - if (err) { - put_device(&nubus_parent); - return err; - } + + nubus_parent = root_device_register("nubus"); + if (IS_ERR(nubus_parent)) + return PTR_ERR(nubus_parent); + nubus_scan_bus(); + return 0; } diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index b199eea3260e..18b64559664b 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -632,8 +632,11 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region, u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region); void nvdimm_bus_lock(struct device *dev); void nvdimm_bus_unlock(struct device *dev); -DEFINE_GUARD(nvdimm_bus, struct device *, - if (_T) nvdimm_bus_lock(_T), if (_T) nvdimm_bus_unlock(_T)); +DEFINE_CLASS(nvdimm_bus, struct device *, + if (_T) nvdimm_bus_unlock(_T), + ({ if (_T) nvdimm_bus_lock(_T); _T; }), + struct device *_T); +DEFINE_CLASS_IS_GUARD(nvdimm_bus); bool is_nvdimm_bus_locked(struct device *dev); void nvdimm_check_and_set_ro(struct gendisk *disk); diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index c692fc73babf..f9327feb87d0 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -819,7 +819,7 @@ static int apple_nvme_init_hctx(struct blk_mq_hw_ctx *hctx, void *data, static int apple_nvme_init_request(struct blk_mq_tag_set *set, struct request *req, unsigned int hctx_idx, - unsigned int numa_node) + int numa_node) { struct apple_nvme_queue *q = set->driver_data; struct apple_nvme *anv = queue_to_apple_nvme(q); @@ -858,7 +858,7 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown) * doing a safe shutdown. */ if (!dead && shutdown && freeze) - nvme_wait_freeze_timeout(&anv->ctrl, NVME_IO_TIMEOUT); + nvme_wait_freeze_timeout(&anv->ctrl); nvme_quiesce_io_queues(&anv->ctrl); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index c3032d6ad6b1..3b7a8f7a3542 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -323,6 +323,7 @@ static void nvme_retry_req(struct request *req) { unsigned long delay = 0; u16 crd; + struct nvme_ns *ns = req->q->queuedata; /* The mask and shift result must be <= 3 */ crd = (nvme_req(req)->status & NVME_STATUS_CRD) >> 11; @@ -330,6 +331,9 @@ static void nvme_retry_req(struct request *req) delay = nvme_req(req)->ctrl->crdt[crd - 1] * 100; nvme_req(req)->retries++; + if (ns) + atomic_long_inc(&ns->retries); + blk_mq_requeue_request(req, false); blk_mq_delay_kick_requeue_list(req->q, delay); } @@ -434,11 +438,19 @@ static inline void nvme_end_req_zoned(struct request *req) static inline void __nvme_end_req(struct request *req) { - if (unlikely(nvme_req(req)->status && !(req->rq_flags & RQF_QUIET))) { + struct nvme_ns *ns = req->q->queuedata; + struct nvme_request *nr = nvme_req(req); + + if (unlikely(nr->status && !(req->rq_flags & RQF_QUIET))) { if (blk_rq_is_passthrough(req)) nvme_log_err_passthru(req); else nvme_log_error(req); + + if (ns) + atomic_long_inc(&ns->errors); + else + atomic_long_inc(&nr->ctrl->errors); } nvme_end_req_zoned(req); nvme_trace_bio_complete(req); @@ -584,6 +596,7 @@ bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl, case NVME_CTRL_NEW: case NVME_CTRL_LIVE: changed = true; + atomic_long_inc(&ctrl->nr_reset); fallthrough; default: break; @@ -729,10 +742,8 @@ void nvme_init_request(struct request *req, struct nvme_command *cmd) struct nvme_ns *ns = req->q->disk->private_data; logging_enabled = ns->head->passthru_err_log_enabled; - req->timeout = NVME_IO_TIMEOUT; } else { /* no queuedata implies admin queue */ logging_enabled = nr->ctrl->passthru_err_log_enabled; - req->timeout = NVME_ADMIN_TIMEOUT; } if (!logging_enabled) @@ -2263,7 +2274,7 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl, } n = le16_to_cpu(h->numfdpc) + 1; - if (fdp_idx > n) { + if (fdp_idx >= n) { dev_warn(ctrl->device, "FDP index:%d out of range:%d\n", fdp_idx, n); /* Proceed without registering FDP streams */ @@ -2275,14 +2286,16 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl, desc = log; end = log + size - sizeof(*h); for (i = 0; i < fdp_idx; i++) { - log += le16_to_cpu(desc->dsze); - desc = log; - if (log >= end) { + u16 dsze = le16_to_cpu(desc->dsze); + + if (!dsze || log + dsze > end) { dev_warn(ctrl->device, - "FDP invalid config descriptor list\n"); + "FDP invalid config descriptor at index %d\n", i); ret = 0; goto out; } + log += dsze; + desc = log; } if (le32_to_cpu(desc->nrg) > 1) { @@ -2409,12 +2422,22 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, goto out; } + if (id->lbaf[lbaf].ds < SECTOR_SHIFT || + check_shl_overflow(le64_to_cpu(id->nsze), + id->lbaf[lbaf].ds - SECTOR_SHIFT, + &capacity)) { + dev_warn_once(ns->ctrl->device, + "invalid LBA data size %u, skipping namespace\n", + id->lbaf[lbaf].ds); + ret = -ENODEV; + goto out; + } + lim = queue_limits_start_update(ns->disk->queue); memflags = blk_mq_freeze_queue(ns->disk->queue); ns->head->lba_shift = id->lbaf[lbaf].ds; ns->head->nuse = le64_to_cpu(id->nuse); - capacity = nvme_lba_to_sect(ns->head, le64_to_cpu(id->nsze)); nvme_set_ctrl_limits(ns->ctrl, &lim, false); nvme_configure_metadata(ns->ctrl, ns->head, id, nvm, info); nvme_set_chunk_sectors(ns, id, &lim); @@ -2483,6 +2506,14 @@ out: return ret; } +static void nvme_stack_zone_resources(struct queue_limits *t, + const struct queue_limits *b) +{ + t->max_open_zones = min_not_zero(t->max_open_zones, b->max_open_zones); + t->max_active_zones = + min_not_zero(t->max_active_zones, b->max_active_zones); +} + static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info) { bool unsupported = false; @@ -2549,6 +2580,8 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info) lim.io_opt = ns_lim->io_opt; queue_limits_stack_bdev(&lim, ns->disk->part0, 0, ns->head->disk->disk_name); + if (lim.features & BLK_FEAT_ZONED) + nvme_stack_zone_resources(&lim, ns_lim); if (unsupported) ns->head->disk->flags |= GENHD_FL_HIDDEN; else @@ -2559,7 +2592,7 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info) set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk)); set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info)); - nvme_mpath_revalidate_paths(ns); + nvme_mpath_revalidate_paths(ns->head); blk_mq_unfreeze_queue(ns->head->disk->queue, memflags); } @@ -3926,7 +3959,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, int ret = -ENOMEM; #ifdef CONFIG_NVME_MULTIPATH - size += num_possible_nodes() * sizeof(struct nvme_ns *); + size += nr_node_ids * sizeof(struct nvme_ns *); #endif head = kzalloc(size, GFP_KERNEL); @@ -4209,6 +4242,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info) mutex_unlock(&ctrl->namespaces_lock); goto out_unlink_ns; } + blk_queue_rq_timeout(ns->queue, ctrl->io_timeout); nvme_ns_add_to_ctrl_list(ns); mutex_unlock(&ctrl->namespaces_lock); synchronize_srcu(&ctrl->srcu); @@ -4894,12 +4928,7 @@ int nvme_alloc_admin_tag_set(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set, if (ret) return ret; - /* - * If a previous admin queue exists (e.g., from before a reset), - * put it now before allocating a new one to avoid orphaning it. - */ - if (ctrl->admin_q) - blk_put_queue(ctrl->admin_q); + WARN_ON_ONCE(ctrl->admin_q); ctrl->admin_q = blk_mq_alloc_queue(set, NULL, NULL); if (IS_ERR(ctrl->admin_q)) { @@ -4937,10 +4966,8 @@ void nvme_remove_admin_tag_set(struct nvme_ctrl *ctrl) */ nvme_stop_keep_alive(ctrl); blk_mq_destroy_queue(ctrl->admin_q); - if (ctrl->ops->flags & NVME_F_FABRICS) { + if (ctrl->fabrics_q) blk_mq_destroy_queue(ctrl->fabrics_q); - blk_put_queue(ctrl->fabrics_q); - } blk_mq_free_tag_set(ctrl->admin_tagset); } EXPORT_SYMBOL_GPL(nvme_remove_admin_tag_set); @@ -5082,6 +5109,8 @@ static void nvme_free_ctrl(struct device *dev) if (ctrl->admin_q) blk_put_queue(ctrl->admin_q); + if (ctrl->fabrics_q) + blk_put_queue(ctrl->fabrics_q); if (!subsys || ctrl->instance != subsys->instance) ida_free(&nvme_instance_ida, ctrl->instance); nvme_free_cels(ctrl); @@ -5146,6 +5175,8 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, memset(&ctrl->ka_cmd, 0, sizeof(ctrl->ka_cmd)); ctrl->ka_cmd.common.opcode = nvme_admin_keep_alive; ctrl->ka_last_check_time = jiffies; + ctrl->admin_timeout = NVME_ADMIN_TIMEOUT; + ctrl->io_timeout = NVME_IO_TIMEOUT; BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) > PAGE_SIZE); @@ -5252,8 +5283,9 @@ void nvme_unfreeze(struct nvme_ctrl *ctrl) } EXPORT_SYMBOL_GPL(nvme_unfreeze); -int nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl, long timeout) +int nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl) { + long timeout = ctrl->io_timeout; struct nvme_ns *ns; int srcu_idx; diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index e4f4528fe2a2..2c9a6d3c9797 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2109,7 +2109,7 @@ out_on_error: static int nvme_fc_init_request(struct blk_mq_tag_set *set, struct request *rq, - unsigned int hctx_idx, unsigned int numa_node) + unsigned int hctx_idx, int numa_node) { struct nvme_fc_ctrl *ctrl = to_fc_ctrl(set->driver_data); struct nvme_fcp_op_w_sgl *op = blk_mq_rq_to_pdu(rq); @@ -3148,6 +3148,8 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) goto out_term_aen_ops; } + /* accumulate reconnect attempts before resetting it to zero */ + atomic_long_add(ctrl->ctrl.nr_reconnects, &ctrl->ctrl.acc_reconnects); ctrl->ctrl.nr_reconnects = 0; nvme_start_ctrl(&ctrl->ctrl); @@ -3470,6 +3472,7 @@ nvme_fc_alloc_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, ctrl->ctrl.opts = opts; ctrl->ctrl.nr_reconnects = 0; + atomic_long_set(&ctrl->ctrl.acc_reconnects, 0); INIT_LIST_HEAD(&ctrl->ctrl_list); ctrl->lport = lport; ctrl->rport = rport; diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index 08889b20e5d8..664216eece4a 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -102,8 +102,17 @@ static struct request *nvme_alloc_user_request(struct request_queue *q, struct nvme_command *cmd, blk_opf_t rq_flags, blk_mq_req_flags_t blk_flags) { + struct nvme_ns *ns = q->queuedata; struct request *req; + /* + * The NVME_MPATH flag is set only for IO commands sent to a namespace + * with a multipath enabled head. The request is not eligible for + * failover as passthrough requests also append REQ_FAILFAST_DRIVER. + */ + if (ns && nvme_ns_head_multipath(ns->head)) + rq_flags |= REQ_NVME_MPATH; + req = blk_mq_alloc_request(q, nvme_req_op(cmd) | rq_flags, blk_flags); if (IS_ERR(req)) return req; diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 263161cb8ac0..e033ede953cc 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -73,19 +73,29 @@ static const char *nvme_iopolicy_names[] = { static int iopolicy = NVME_IOPOLICY_NUMA; +static int nvme_iopolicy_parse(const char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(nvme_iopolicy_names); i++) { + if (sysfs_streq(str, nvme_iopolicy_names[i])) + return i; + } + return -EINVAL; +} + static int nvme_set_iopolicy(const char *val, const struct kernel_param *kp) { + int policy; + if (!val) return -EINVAL; - if (!strncmp(val, "numa", 4)) - iopolicy = NVME_IOPOLICY_NUMA; - else if (!strncmp(val, "round-robin", 11)) - iopolicy = NVME_IOPOLICY_RR; - else if (!strncmp(val, "queue-depth", 11)) - iopolicy = NVME_IOPOLICY_QD; - else - return -EINVAL; + policy = nvme_iopolicy_parse(val); + if (policy < 0) + return policy; + + iopolicy = policy; return 0; } @@ -142,6 +152,7 @@ void nvme_failover_req(struct request *req) struct bio *bio; nvme_mpath_clear_current_path(ns); + atomic_long_inc(&ns->failover); /* * If we got back an ANA error, we know the controller is alive but not @@ -175,9 +186,12 @@ void nvme_mpath_start_request(struct request *rq) nvme_req(rq)->flags |= NVME_MPATH_CNT_ACTIVE; } - if (!blk_queue_io_stat(disk->queue) || blk_rq_is_passthrough(rq) || + if (!blk_queue_io_stat(disk->queue) || (nvme_req(rq)->flags & NVME_MPATH_IO_STATS)) return; + if (blk_rq_is_passthrough(rq) && + !blk_rq_passthrough_stats(rq, disk->queue)) + return; nvme_req(rq)->flags |= NVME_MPATH_IO_STATS; nvme_req(rq)->start_time = bdev_start_io_acct(disk->part0, req_op(rq), @@ -254,10 +268,10 @@ void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl) srcu_read_unlock(&ctrl->srcu, srcu_idx); } -void nvme_mpath_revalidate_paths(struct nvme_ns *ns) +void nvme_mpath_revalidate_paths(struct nvme_ns_head *head) { - struct nvme_ns_head *head = ns->head; sector_t capacity = get_capacity(head->disk); + struct nvme_ns *ns; int node; int srcu_idx; @@ -511,6 +525,12 @@ static void nvme_ns_head_submit_bio(struct bio *bio) ns = nvme_find_path(head); if (likely(ns)) { bio_set_dev(bio, ns->disk->part0); + /* + * Use BIO_REMAPPED to skip bio_check_eod() when this bio + * enters submit_bio_noacct() for the per-path device. The EOD + * check already passed on the multipath head. + */ + bio_set_flag(bio, BIO_REMAPPED); bio->bi_opf |= REQ_NVME_MPATH; trace_block_bio_remap(bio, disk_devt(ns->head->disk), bio->bi_iter.bi_sector); @@ -521,10 +541,12 @@ static void nvme_ns_head_submit_bio(struct bio *bio) spin_lock_irq(&head->requeue_lock); bio_list_add(&head->requeue_list, bio); spin_unlock_irq(&head->requeue_lock); + atomic_long_inc(&head->io_requeue_no_usable_path_count); } else { dev_warn_ratelimited(dev, "no available path - failing I/O\n"); bio_io_error(bio); + atomic_long_inc(&head->io_fail_no_available_path_count); } srcu_read_unlock(&head->srcu, srcu_idx); @@ -730,7 +752,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head) blk_set_stacking_limits(&lim); lim.dma_alignment = 3; lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT | - BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES; + BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA; if (head->ids.csi == NVME_CSI_ZNS) lim.features |= BLK_FEAT_ZONED; @@ -1039,16 +1061,14 @@ static ssize_t nvme_subsys_iopolicy_store(struct device *dev, { struct nvme_subsystem *subsys = container_of(dev, struct nvme_subsystem, dev); - int i; + int policy; - for (i = 0; i < ARRAY_SIZE(nvme_iopolicy_names); i++) { - if (sysfs_streq(buf, nvme_iopolicy_names[i])) { - nvme_subsys_iopolicy_update(subsys, i); - return count; - } - } + policy = nvme_iopolicy_parse(buf); + if (policy < 0) + return policy; - return -EINVAL; + nvme_subsys_iopolicy_update(subsys, policy); + return count; } SUBSYS_ATTR_RW(iopolicy, S_IRUGO | S_IWUSR, nvme_subsys_iopolicy_show, nvme_subsys_iopolicy_store); @@ -1151,6 +1171,90 @@ static ssize_t delayed_removal_secs_store(struct device *dev, DEVICE_ATTR_RW(delayed_removal_secs); +static ssize_t multipath_failover_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + + return sysfs_emit(buf, "%lu\n", atomic_long_read(&ns->failover)); +} + +static ssize_t multipath_failover_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long failover; + int ret; + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + + ret = kstrtoul(buf, 0, &failover); + if (ret) + return -EINVAL; + + atomic_long_set(&ns->failover, failover); + + return count; +} + +DEVICE_ATTR_RW(multipath_failover_count); + +static ssize_t io_requeue_no_usable_path_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + struct nvme_ns_head *head = disk->private_data; + + return sysfs_emit(buf, "%lu\n", + atomic_long_read(&head->io_requeue_no_usable_path_count)); +} + +static ssize_t io_requeue_no_usable_path_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long requeue_cnt; + struct gendisk *disk = dev_to_disk(dev); + struct nvme_ns_head *head = disk->private_data; + + err = kstrtoul(buf, 0, &requeue_cnt); + if (err) + return -EINVAL; + + atomic_long_set(&head->io_requeue_no_usable_path_count, requeue_cnt); + + return count; +} + +DEVICE_ATTR_RW(io_requeue_no_usable_path_count); + +static ssize_t io_fail_no_available_path_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + struct nvme_ns_head *head = disk->private_data; + + return sysfs_emit(buf, "%lu\n", + atomic_long_read(&head->io_fail_no_available_path_count)); +} + +static ssize_t io_fail_no_available_path_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long fail_cnt; + struct gendisk *disk = dev_to_disk(dev); + struct nvme_ns_head *head = disk->private_data; + + err = kstrtoul(buf, 0, &fail_cnt); + if (err) + return -EINVAL; + + atomic_long_set(&head->io_fail_no_available_path_count, fail_cnt); + + return count; +} + +DEVICE_ATTR_RW(io_fail_no_available_path_count); + static int nvme_lookup_ana_group_desc(struct nvme_ctrl *ctrl, struct nvme_ana_group_desc *desc, void *data) { diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index ccd5e05dac98..b367c67dcb37 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -370,6 +370,8 @@ struct nvme_ctrl { u16 mtfa; u32 ctrl_config; u32 queue_count; + u32 admin_timeout; + u32 io_timeout; u64 cap; u32 max_hw_sectors; @@ -413,6 +415,8 @@ struct nvme_ctrl { unsigned long ka_last_check_time; struct work_struct fw_act_work; unsigned long events; + atomic_long_t errors; + atomic_long_t nr_reset; #ifdef CONFIG_NVME_MULTIPATH /* asymmetric namespace access: */ @@ -454,6 +458,8 @@ struct nvme_ctrl { u16 icdoff; u16 maxcmd; int nr_reconnects; + /* accumulate reconenct attempts, as nr_reconnects can reset to zero */ + atomic_long_t acc_reconnects; unsigned long flags; struct nvmf_ctrl_options *opts; @@ -563,6 +569,8 @@ struct nvme_ns_head { unsigned long flags; struct delayed_work remove_work; unsigned int delayed_removal_secs; + atomic_long_t io_requeue_no_usable_path_count; + atomic_long_t io_fail_no_available_path_count; #define NVME_NSHEAD_DISK_LIVE 0 #define NVME_NSHEAD_QUEUE_IF_NO_PATH 1 struct nvme_ns __rcu *current_path[]; @@ -589,7 +597,10 @@ struct nvme_ns { #ifdef CONFIG_NVME_MULTIPATH enum nvme_ana_state ana_state; u32 ana_grpid; + atomic_long_t failover; #endif + atomic_long_t retries; + atomic_long_t errors; struct list_head siblings; struct kref kref; struct nvme_ns_head *head; @@ -900,7 +911,7 @@ void nvme_sync_queues(struct nvme_ctrl *ctrl); void nvme_sync_io_queues(struct nvme_ctrl *ctrl); void nvme_unfreeze(struct nvme_ctrl *ctrl); void nvme_wait_freeze(struct nvme_ctrl *ctrl); -int nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl, long timeout); +int nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl); void nvme_start_freeze(struct nvme_ctrl *ctrl); static inline enum req_op nvme_req_op(struct nvme_command *cmd) @@ -1012,6 +1023,7 @@ extern const struct attribute_group nvme_ns_mpath_attr_group; extern const struct pr_ops nvme_pr_ops; extern const struct block_device_operations nvme_ns_head_ops; extern const struct attribute_group nvme_dev_attrs_group; +extern const struct attribute_group nvme_dev_diag_attrs_group; extern const struct attribute_group *nvme_subsys_attrs_groups[]; extern const struct attribute_group *nvme_dev_attr_groups[]; extern const struct block_device_operations nvme_bdev_ops; @@ -1041,7 +1053,7 @@ void nvme_mpath_update(struct nvme_ctrl *ctrl); void nvme_mpath_uninit(struct nvme_ctrl *ctrl); void nvme_mpath_stop(struct nvme_ctrl *ctrl); bool nvme_mpath_clear_current_path(struct nvme_ns *ns); -void nvme_mpath_revalidate_paths(struct nvme_ns *ns); +void nvme_mpath_revalidate_paths(struct nvme_ns_head *head); void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl); void nvme_mpath_remove_disk(struct nvme_ns_head *head); void nvme_mpath_start_request(struct request *rq); @@ -1061,6 +1073,9 @@ extern struct device_attribute dev_attr_ana_state; extern struct device_attribute dev_attr_queue_depth; extern struct device_attribute dev_attr_numa_nodes; extern struct device_attribute dev_attr_delayed_removal_secs; +extern struct device_attribute dev_attr_multipath_failover_count; +extern struct device_attribute dev_attr_io_requeue_no_usable_path_count; +extern struct device_attribute dev_attr_io_fail_no_available_path_count; extern struct device_attribute subsys_attr_iopolicy; static inline bool nvme_disk_is_ns_head(struct gendisk *disk) @@ -1106,7 +1121,7 @@ static inline bool nvme_mpath_clear_current_path(struct nvme_ns *ns) { return false; } -static inline void nvme_mpath_revalidate_paths(struct nvme_ns *ns) +static inline void nvme_mpath_revalidate_paths(struct nvme_ns_head *head) { } static inline void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index b5f846200678..8438c904ec49 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -587,11 +587,16 @@ static bool nvme_dbbuf_update_and_check_event(u16 value, __le32 *dbbuf_db, } static struct nvme_descriptor_pools * -nvme_setup_descriptor_pools(struct nvme_dev *dev, unsigned numa_node) +nvme_setup_descriptor_pools(struct nvme_dev *dev, int numa_node) { - struct nvme_descriptor_pools *pools = &dev->descriptor_pools[numa_node]; + struct nvme_descriptor_pools *pools; size_t small_align = NVME_SMALL_POOL_SIZE; + if (numa_node == NUMA_NO_NODE) + numa_node = 0; + + pools = &dev->descriptor_pools[numa_node]; + if (pools->small) return pools; /* already initialized */ @@ -660,7 +665,7 @@ static int nvme_init_hctx(struct blk_mq_hw_ctx *hctx, void *data, static int nvme_pci_init_request(struct blk_mq_tag_set *set, struct request *req, unsigned int hctx_idx, - unsigned int numa_node) + int numa_node) { struct nvme_iod *iod = blk_mq_rq_to_pdu(req); @@ -2838,6 +2843,7 @@ static const struct attribute_group nvme_pci_dev_attrs_group = { static const struct attribute_group *nvme_pci_dev_attr_groups[] = { &nvme_dev_attrs_group, &nvme_pci_dev_attrs_group, + &nvme_dev_diag_attrs_group, NULL, }; @@ -3122,7 +3128,7 @@ static bool __nvme_delete_io_queues(struct nvme_dev *dev, u8 opcode) unsigned long timeout; retry: - timeout = NVME_ADMIN_TIMEOUT; + timeout = dev->ctrl.admin_timeout; while (nr_queues > 0) { if (nvme_delete_queue(&dev->queues[nr_queues], opcode)) break; @@ -3304,7 +3310,7 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown) * if doing a safe shutdown. */ if (!dead && shutdown) - nvme_wait_freeze_timeout(&dev->ctrl, NVME_IO_TIMEOUT); + nvme_wait_freeze_timeout(&dev->ctrl); } nvme_quiesce_io_queues(&dev->ctrl); diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index f77c960f7632..6909e3542794 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -292,7 +292,7 @@ static void nvme_rdma_exit_request(struct blk_mq_tag_set *set, static int nvme_rdma_init_request(struct blk_mq_tag_set *set, struct request *rq, unsigned int hctx_idx, - unsigned int numa_node) + int numa_node) { struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(set->driver_data); struct nvme_rdma_request *req = blk_mq_rq_to_pdu(rq); @@ -888,7 +888,7 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new) if (!new) { nvme_start_freeze(&ctrl->ctrl); nvme_unquiesce_io_queues(&ctrl->ctrl); - if (!nvme_wait_freeze_timeout(&ctrl->ctrl, NVME_IO_TIMEOUT)) { + if (!nvme_wait_freeze_timeout(&ctrl->ctrl)) { /* * If we timed out waiting for freeze we are likely to * be stuck. Fail the controller initialization just @@ -1110,6 +1110,8 @@ static void nvme_rdma_reconnect_ctrl_work(struct work_struct *work) dev_info(ctrl->ctrl.device, "Successfully reconnected (%d attempts)\n", ctrl->ctrl.nr_reconnects); + /* accumulate reconnect attempts before resetting it to zero */ + atomic_long_add(ctrl->ctrl.nr_reconnects, &ctrl->ctrl.acc_reconnects); ctrl->ctrl.nr_reconnects = 0; return; diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index e59758616f27..933a5adfb7af 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -6,6 +6,7 @@ */ #include <linux/nvme-auth.h> +#include <linux/blkdev.h> #include "nvme.h" #include "fabrics.h" @@ -335,14 +336,7 @@ static bool multipath_sysfs_group_visible(struct kobject *kobj) return nvme_disk_is_ns_head(dev_to_disk(dev)); } - -static bool multipath_sysfs_attr_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - return false; -} - -DEFINE_SYSFS_GROUP_VISIBLE(multipath_sysfs) +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(multipath_sysfs) const struct attribute_group nvme_ns_mpath_attr_group = { .name = "multipath", @@ -351,11 +345,114 @@ const struct attribute_group nvme_ns_mpath_attr_group = { }; #endif +static ssize_t command_retries_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + + return sysfs_emit(buf, "%lu\n", atomic_long_read(&ns->retries)); +} + +static ssize_t command_retries_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long retries; + int err; + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + + err = kstrtoul(buf, 0, &retries); + if (err) + return -EINVAL; + + atomic_long_set(&ns->retries, retries); + + return count; +} +static DEVICE_ATTR_RW(command_retries_count); + +static ssize_t nvme_io_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + + return sysfs_emit(buf, "%lu\n", atomic_long_read(&ns->errors)); +} + +static ssize_t nvme_io_errors_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long errors; + int err; + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + + err = kstrtoul(buf, 0, &errors); + if (err) + return -EINVAL; + + atomic_long_set(&ns->errors, errors); + + return count; +} + +struct device_attribute dev_attr_io_errors = + __ATTR(command_error_count, 0644, + nvme_io_errors_show, nvme_io_errors_store); + +static struct attribute *nvme_ns_diag_attrs[] = { + &dev_attr_command_retries_count.attr, + &dev_attr_io_errors.attr, +#ifdef CONFIG_NVME_MULTIPATH + &dev_attr_multipath_failover_count.attr, + &dev_attr_io_requeue_no_usable_path_count.attr, + &dev_attr_io_fail_no_available_path_count.attr, +#endif + NULL, +}; + +static umode_t nvme_ns_diag_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (a == &dev_attr_command_retries_count.attr) { + if (nvme_disk_is_ns_head(dev_to_disk(dev))) + return 0; + } + if (a == &dev_attr_io_errors.attr) { + struct gendisk *disk = dev_to_disk(dev); + + if (nvme_disk_is_ns_head(disk)) + return 0; + } +#ifdef CONFIG_NVME_MULTIPATH + if (a == &dev_attr_multipath_failover_count.attr) { + if (nvme_disk_is_ns_head(dev_to_disk(dev))) + return 0; + } + if (a == &dev_attr_io_requeue_no_usable_path_count.attr) { + if (!nvme_disk_is_ns_head(dev_to_disk(dev))) + return 0; + } + if (a == &dev_attr_io_fail_no_available_path_count.attr) { + if (!nvme_disk_is_ns_head(dev_to_disk(dev))) + return 0; + } +#endif + return a->mode; +} + +const struct attribute_group nvme_ns_diag_attr_group = { + .name = "diag", + .attrs = nvme_ns_diag_attrs, + .is_visible = nvme_ns_diag_attrs_are_visible, +}; + const struct attribute_group *nvme_ns_attr_groups[] = { &nvme_ns_attr_group, #ifdef CONFIG_NVME_MULTIPATH &nvme_ns_mpath_attr_group, #endif + &nvme_ns_diag_attr_group, NULL, }; @@ -623,6 +720,92 @@ static ssize_t quirks_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(quirks); +static ssize_t nvme_admin_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", + jiffies_to_msecs(ctrl->admin_timeout)); +} + +static ssize_t nvme_admin_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + u32 timeout; + int err; + + /* + * Wait until the controller reaches the LIVE state to be sure that + * admin_q and fabrics_q are properly initialized. + */ + if (!test_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags)) + return -EBUSY; + + err = kstrtou32(buf, 10, &timeout); + if (err || !timeout) + return -EINVAL; + + ctrl->admin_timeout = msecs_to_jiffies(timeout); + + blk_queue_rq_timeout(ctrl->admin_q, ctrl->admin_timeout); + if (ctrl->fabrics_q) + blk_queue_rq_timeout(ctrl->fabrics_q, ctrl->admin_timeout); + + return count; +} + +static DEVICE_ATTR(admin_timeout, S_IRUGO | S_IWUSR, + nvme_admin_timeout_show, nvme_admin_timeout_store); + +static ssize_t nvme_io_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", jiffies_to_msecs(ctrl->io_timeout)); +} + +static ssize_t nvme_io_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + struct nvme_ns *ns; + u32 timeout; + int err; + + /* + * Wait until the controller reaches the LIVE state to be sure that + * connect_q is properly initialized. + */ + if (!test_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags)) + return -EBUSY; + + err = kstrtou32(buf, 10, &timeout); + if (err || !timeout) + return -EINVAL; + + /* Take the namespaces_lock to avoid racing against nvme_alloc_ns() */ + mutex_lock(&ctrl->namespaces_lock); + + ctrl->io_timeout = msecs_to_jiffies(timeout); + list_for_each_entry(ns, &ctrl->namespaces, list) + blk_queue_rq_timeout(ns->queue, ctrl->io_timeout); + + mutex_unlock(&ctrl->namespaces_lock); + + if (ctrl->connect_q) + blk_queue_rq_timeout(ctrl->connect_q, ctrl->io_timeout); + + return count; +} + +static DEVICE_ATTR(io_timeout, S_IRUGO | S_IWUSR, + nvme_io_timeout_show, nvme_io_timeout_store); + #ifdef CONFIG_NVME_HOST_AUTH static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -765,6 +948,8 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_cntrltype.attr, &dev_attr_dctype.attr, &dev_attr_quirks.attr, + &dev_attr_admin_timeout.attr, + &dev_attr_io_timeout.attr, #ifdef CONFIG_NVME_HOST_AUTH &dev_attr_dhchap_secret.attr, &dev_attr_dhchap_ctrl_secret.attr, @@ -937,11 +1122,121 @@ static const struct attribute_group nvme_tls_attrs_group = { }; #endif +static ssize_t nvme_adm_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%lu\n", + (unsigned long)atomic_long_read(&ctrl->errors)); +} + +static ssize_t nvme_adm_errors_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long errors; + int err; + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + err = kstrtoul(buf, 0, &errors); + if (err) + return -EINVAL; + + atomic_long_set(&ctrl->errors, errors); + + return count; +} + +struct device_attribute dev_attr_adm_errors = + __ATTR(command_error_count, 0644, + nvme_adm_errors_show, nvme_adm_errors_store); + +static ssize_t reset_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%lu\n", atomic_long_read(&ctrl->nr_reset)); +} + +static ssize_t reset_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long reset_cnt; + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + err = kstrtoul(buf, 0, &reset_cnt); + if (err) + return -EINVAL; + + atomic_long_set(&ctrl->nr_reset, reset_cnt); + + return count; +} + +static ssize_t reconnect_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%lu\n", + atomic_long_read(&ctrl->acc_reconnects) + + ctrl->nr_reconnects); +} + +static ssize_t reconnect_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long reconnect_cnt; + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + err = kstrtoul(buf, 0, &reconnect_cnt); + if (err) + return -EINVAL; + + atomic_long_set(&ctrl->acc_reconnects, reconnect_cnt); + + return count; +} + +static DEVICE_ATTR_RW(reconnect_count); + +static DEVICE_ATTR_RW(reset_count); + +static struct attribute *nvme_dev_diag_attrs[] = { + &dev_attr_adm_errors.attr, + &dev_attr_reset_count.attr, + &dev_attr_reconnect_count.attr, + NULL, +}; + +static umode_t nvme_dev_diag_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + if (a == &dev_attr_reconnect_count.attr && !ctrl->opts) + return 0; + + return a->mode; +} + +const struct attribute_group nvme_dev_diag_attrs_group = { + .name = "diag", + .attrs = nvme_dev_diag_attrs, + .is_visible = nvme_dev_diag_attrs_are_visible, +}; +EXPORT_SYMBOL_GPL(nvme_dev_diag_attrs_group); + const struct attribute_group *nvme_dev_attr_groups[] = { &nvme_dev_attrs_group, #ifdef CONFIG_NVME_TCP_TLS &nvme_tls_attrs_group, #endif + &nvme_dev_diag_attrs_group, NULL, }; diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 15d36d6a728e..ba5c7b3e2a7c 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -56,44 +56,6 @@ MODULE_PARM_DESC(tls_handshake_timeout, static atomic_t nvme_tcp_cpu_queues[NR_CPUS]; -#ifdef CONFIG_DEBUG_LOCK_ALLOC -/* lockdep can detect a circular dependency of the form - * sk_lock -> mmap_lock (page fault) -> fs locks -> sk_lock - * because dependencies are tracked for both nvme-tcp and user contexts. Using - * a separate class prevents lockdep from conflating nvme-tcp socket use with - * user-space socket API use. - */ -static struct lock_class_key nvme_tcp_sk_key[2]; -static struct lock_class_key nvme_tcp_slock_key[2]; - -static void nvme_tcp_reclassify_socket(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (WARN_ON_ONCE(!sock_allow_reclassification(sk))) - return; - - switch (sk->sk_family) { - case AF_INET: - sock_lock_init_class_and_name(sk, "slock-AF_INET-NVME", - &nvme_tcp_slock_key[0], - "sk_lock-AF_INET-NVME", - &nvme_tcp_sk_key[0]); - break; - case AF_INET6: - sock_lock_init_class_and_name(sk, "slock-AF_INET6-NVME", - &nvme_tcp_slock_key[1], - "sk_lock-AF_INET6-NVME", - &nvme_tcp_sk_key[1]); - break; - default: - WARN_ON_ONCE(1); - } -} -#else -static void nvme_tcp_reclassify_socket(struct socket *sock) { } -#endif - enum nvme_tcp_send_state { NVME_TCP_SEND_CMD_PDU = 0, NVME_TCP_SEND_H2C_PDU, @@ -180,6 +142,11 @@ struct nvme_tcp_queue { void (*state_change)(struct sock *); void (*data_ready)(struct sock *); void (*write_space)(struct sock *); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lock_class_key nvme_tcp_sk_key; + struct lock_class_key nvme_tcp_slock_key; +#endif }; struct nvme_tcp_ctrl { @@ -207,6 +174,39 @@ static const struct blk_mq_ops nvme_tcp_mq_ops; static const struct blk_mq_ops nvme_tcp_admin_mq_ops; static int nvme_tcp_try_send(struct nvme_tcp_queue *queue); +#ifdef CONFIG_DEBUG_LOCK_ALLOC +/* lockdep can detect a circular dependency of the form + * sk_lock -> mmap_lock (page fault) -> fs locks -> sk_lock + * because dependencies are tracked for both nvme-tcp and user contexts. Using + * a separate class prevents lockdep from conflating nvme-tcp socket use with + * user-space socket API use. + */ +static void nvme_tcp_reclassify_socket(struct nvme_tcp_queue *queue) +{ + struct sock *sk = queue->sock->sk; + + if (WARN_ON_ONCE(!sock_allow_reclassification(sk))) + return; + + switch (sk->sk_family) { + case AF_INET: + sock_lock_init_class_and_name(sk, "slock-AF_INET-NVME", + &queue->nvme_tcp_slock_key, + "sk_lock-AF_INET-NVME", + &queue->nvme_tcp_sk_key); + break; + case AF_INET6: + sock_lock_init_class_and_name(sk, "slock-AF_INET6-NVME", + &queue->nvme_tcp_slock_key, + "sk_lock-AF_INET6-NVME", + &queue->nvme_tcp_sk_key); + break; + default: + WARN_ON_ONCE(1); + } +} +#endif + static inline struct nvme_tcp_ctrl *to_tcp_ctrl(struct nvme_ctrl *ctrl) { return container_of(ctrl, struct nvme_tcp_ctrl, ctrl); @@ -340,32 +340,25 @@ static void nvme_tcp_init_iter(struct nvme_tcp_request *req, unsigned int dir) { struct request *rq = blk_mq_rq_from_pdu(req); - struct bio_vec *vec; - unsigned int size; - int nr_bvec; - size_t offset; if (rq->rq_flags & RQF_SPECIAL_PAYLOAD) { - vec = &rq->special_vec; - nr_bvec = 1; - size = blk_rq_payload_bytes(rq); - offset = 0; + iov_iter_bvec(&req->iter, dir, &rq->special_vec, 1, + blk_rq_payload_bytes(rq)); + req->iter.iov_offset = 0; } else { struct bio *bio = req->curr_bio; struct bvec_iter bi; struct bio_vec bv; + int nr_bvec = 0; - vec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter); - nr_bvec = 0; - bio_for_each_bvec(bv, bio, bi) { + bio_for_each_bvec(bv, bio, bi) nr_bvec++; - } - size = bio->bi_iter.bi_size; - offset = bio->bi_iter.bi_bvec_done; - } - iov_iter_bvec(&req->iter, dir, vec, nr_bvec, size); - req->iter.iov_offset = offset; + iov_iter_bvec(&req->iter, dir, + __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter), nr_bvec, + bio->bi_iter.bi_size); + req->iter.iov_offset = bio->bi_iter.bi_bvec_done; + } } static inline void nvme_tcp_advance_req(struct nvme_tcp_request *req, @@ -548,7 +541,7 @@ static void nvme_tcp_exit_request(struct blk_mq_tag_set *set, static int nvme_tcp_init_request(struct blk_mq_tag_set *set, struct request *rq, unsigned int hctx_idx, - unsigned int numa_node) + int numa_node) { struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(set->driver_data); struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq); @@ -1468,6 +1461,11 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, int qid) kfree(queue->pdu); mutex_destroy(&queue->send_mutex); mutex_destroy(&queue->queue_lock); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + lockdep_unregister_key(&queue->nvme_tcp_sk_key); + lockdep_unregister_key(&queue->nvme_tcp_slock_key); +#endif } static int nvme_tcp_init_connection(struct nvme_tcp_queue *queue) @@ -1702,7 +1700,7 @@ static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid) qid, pskid, status); if (status) { - queue->tls_err = -status; + queue->tls_err = status; goto out_complete; } @@ -1813,7 +1811,12 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid, } sk_net_refcnt_upgrade(queue->sock->sk); - nvme_tcp_reclassify_socket(queue->sock); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + lockdep_register_key(&queue->nvme_tcp_sk_key); + lockdep_register_key(&queue->nvme_tcp_slock_key); + nvme_tcp_reclassify_socket(queue); +#endif /* Single syn retry */ tcp_sock_set_syncnt(queue->sock->sk, 1); @@ -1918,6 +1921,10 @@ err_sock: /* Use sync variant - see nvme_tcp_free_queue() for explanation */ __fput_sync(queue->sock->file); queue->sock = NULL; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + lockdep_unregister_key(&queue->nvme_tcp_sk_key); + lockdep_unregister_key(&queue->nvme_tcp_slock_key); +#endif err_destroy_mutex: mutex_destroy(&queue->send_mutex); mutex_destroy(&queue->queue_lock); @@ -2208,7 +2215,7 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new) if (!new) { nvme_start_freeze(ctrl); nvme_unquiesce_io_queues(ctrl); - if (!nvme_wait_freeze_timeout(ctrl, NVME_IO_TIMEOUT)) { + if (!nvme_wait_freeze_timeout(ctrl)) { /* * If we timed out waiting for freeze we are likely to * be stuck. Fail the controller initialization just @@ -2475,6 +2482,8 @@ static void nvme_tcp_reconnect_ctrl_work(struct work_struct *work) dev_info(ctrl->device, "Successfully reconnected (attempt %d/%d)\n", ctrl->nr_reconnects, ctrl->opts->max_reconnects); + /* accumulate reconnect attempts before resetting it to zero */ + atomic_long_add(ctrl->nr_reconnects, &ctrl->acc_reconnects); ctrl->nr_reconnects = 0; return; @@ -3053,6 +3062,8 @@ static int __init nvme_tcp_init_module(void) if (wq_unbound) wq_flags |= WQ_UNBOUND; + else + wq_flags |= WQ_PERCPU; nvme_tcp_wq = alloc_workqueue("nvme_tcp_wq", wq_flags, 0); if (!nvme_tcp_wq) diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c index e9b35549e254..114869d16a1f 100644 --- a/drivers/nvme/target/discovery.c +++ b/drivers/nvme/target/discovery.c @@ -166,6 +166,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) u64 offset = nvmet_get_log_page_offset(req->cmd); size_t data_len = nvmet_get_log_page_len(req->cmd); size_t alloc_len; + size_t copy_len; struct nvmet_subsys_link *p; struct nvmet_port *r; u32 numrec = 0; @@ -242,7 +243,27 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) up_read(&nvmet_config_sem); - status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len); + /* + * Validate the host-supplied log page offset before copying out. + * Without this check, the host controls a 64-bit byte offset into + * a small kzalloc'd buffer: a value past the log page lets the + * subsequent memcpy read adjacent kernel heap, and a value aimed + * at unmapped kernel memory faults the in-kernel copy and crashes + * the target host. The Discovery controller is unauthenticated, + * so the bug is reachable from any reachable fabric peer. + */ + if (offset > alloc_len) { + req->error_loc = + offsetof(struct nvme_get_log_page_command, lpo); + status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR; + goto out_free_buffer; + } + + copy_len = min_t(size_t, data_len, alloc_len - offset); + status = nvmet_copy_to_sgl(req, 0, buffer + offset, copy_len); + if (!status && copy_len < data_len) + status = nvmet_zero_sgl(req, copy_len, data_len - copy_len); +out_free_buffer: kfree(buffer); out: nvmet_req_complete(req, status); diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index f1e613e7c63e..0a85acf1e5c7 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -132,13 +132,22 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d) return 0; } -static u8 nvmet_auth_reply(struct nvmet_req *req, void *d) +static u8 nvmet_auth_reply(struct nvmet_req *req, void *d, u32 tl) { struct nvmet_ctrl *ctrl = req->sq->ctrl; struct nvmf_auth_dhchap_reply_data *data = d; - u16 dhvlen = le16_to_cpu(data->dhvlen); + u16 dhvlen; u8 *response; + if (tl < sizeof(*data)) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + + dhvlen = le16_to_cpu(data->dhvlen); + + /* Validate that hl and dhvlen fit within the transfer length */ + if (sizeof(*data) + 2 * (size_t)data->hl + dhvlen > tl) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n", __func__, ctrl->cntlid, req->sq->qid, data->hl, data->cvalid, dhvlen); @@ -338,7 +347,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req) switch (data->auth_id) { case NVME_AUTH_DHCHAP_MESSAGE_REPLY: - dhchap_status = nvmet_auth_reply(req, d); + dhchap_status = nvmet_auth_reply(req, d, tl); if (dhchap_status == 0) req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1; diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index d98d0cdc5d6f..fcb1f8186fdd 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -202,7 +202,7 @@ static int nvme_loop_init_iod(struct nvme_loop_ctrl *ctrl, static int nvme_loop_init_request(struct blk_mq_tag_set *set, struct request *req, unsigned int hctx_idx, - unsigned int numa_node) + int numa_node) { struct nvme_loop_ctrl *ctrl = to_loop_ctrl(set->driver_data); struct nvme_loop_iod *iod = blk_mq_rq_to_pdu(req); @@ -274,7 +274,6 @@ static void nvme_loop_destroy_admin_queue(struct nvme_loop_ctrl *ctrl) nvmet_sq_destroy(&ctrl->queues[0].nvme_sq); nvmet_cq_put(&ctrl->queues[0].nvme_cq); - nvme_remove_admin_tag_set(&ctrl->ctrl); } static void nvme_loop_free_ctrl(struct nvme_ctrl *nctrl) @@ -375,25 +374,18 @@ static int nvme_loop_configure_admin_queue(struct nvme_loop_ctrl *ctrl) } ctrl->ctrl.queue_count = 1; - error = nvme_alloc_admin_tag_set(&ctrl->ctrl, &ctrl->admin_tag_set, - &nvme_loop_admin_mq_ops, - sizeof(struct nvme_loop_iod) + - NVME_INLINE_SG_CNT * sizeof(struct scatterlist)); - if (error) - goto out_free_sq; - /* reset stopped state for the fresh admin queue */ clear_bit(NVME_CTRL_ADMIN_Q_STOPPED, &ctrl->ctrl.flags); error = nvmf_connect_admin_queue(&ctrl->ctrl); if (error) - goto out_cleanup_tagset; + goto out_free_sq; set_bit(NVME_LOOP_Q_LIVE, &ctrl->queues[0].flags); error = nvme_enable_ctrl(&ctrl->ctrl); if (error) - goto out_cleanup_tagset; + goto out_free_sq; ctrl->ctrl.max_hw_sectors = (NVME_LOOP_MAX_SEGMENTS - 1) << PAGE_SECTORS_SHIFT; @@ -402,14 +394,12 @@ static int nvme_loop_configure_admin_queue(struct nvme_loop_ctrl *ctrl) error = nvme_init_ctrl_finish(&ctrl->ctrl, false); if (error) - goto out_cleanup_tagset; + goto out_free_sq; return 0; -out_cleanup_tagset: - clear_bit(NVME_LOOP_Q_LIVE, &ctrl->queues[0].flags); - nvme_remove_admin_tag_set(&ctrl->ctrl); out_free_sq: + clear_bit(NVME_LOOP_Q_LIVE, &ctrl->queues[0].flags); nvmet_sq_destroy(&ctrl->queues[0].nvme_sq); nvmet_cq_put(&ctrl->queues[0].nvme_cq); return error; @@ -432,6 +422,7 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl) static void nvme_loop_delete_ctrl_host(struct nvme_ctrl *ctrl) { nvme_loop_shutdown_ctrl(to_loop_ctrl(ctrl)); + nvme_remove_admin_tag_set(ctrl); } static void nvme_loop_delete_ctrl(struct nvmet_ctrl *nctrl) @@ -494,6 +485,7 @@ out_destroy_admin: nvme_cancel_admin_tagset(&ctrl->ctrl); nvme_loop_destroy_admin_queue(ctrl); out_disable: + nvme_remove_admin_tag_set(&ctrl->ctrl); dev_warn(ctrl->ctrl.device, "Removing after reset failure\n"); nvme_uninit_ctrl(&ctrl->ctrl); } @@ -594,10 +586,17 @@ static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev, if (!ctrl->queues) goto out_uninit_ctrl; - ret = nvme_loop_configure_admin_queue(ctrl); + ret = nvme_alloc_admin_tag_set(&ctrl->ctrl, &ctrl->admin_tag_set, + &nvme_loop_admin_mq_ops, + sizeof(struct nvme_loop_iod) + + NVME_INLINE_SG_CNT * sizeof(struct scatterlist)); if (ret) goto out_free_queues; + ret = nvme_loop_configure_admin_queue(ctrl); + if (ret) + goto out_remove_admin_tagset; + if (opts->queue_size > ctrl->ctrl.maxcmd) { /* warn if maxcmd is lower than queue_size */ dev_warn(ctrl->ctrl.device, @@ -633,6 +632,8 @@ out_remove_admin_queue: nvme_quiesce_admin_queue(&ctrl->ctrl); nvme_cancel_admin_tagset(&ctrl->ctrl); nvme_loop_destroy_admin_queue(ctrl); +out_remove_admin_tagset: + nvme_remove_admin_tag_set(&ctrl->ctrl); out_free_queues: kfree(ctrl->queues); out_uninit_ctrl: diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index e6e2c3f9afdf..ac26f4f774c4 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -1598,8 +1598,10 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, pending++; } mutex_unlock(&nvmet_rdma_queue_mutex); - if (pending > NVMET_RDMA_BACKLOG) - return NVME_SC_CONNECT_CTRL_BUSY; + if (pending > NVMET_RDMA_BACKLOG) { + ret = NVME_SC_CONNECT_CTRL_BUSY; + goto put_device; + } } ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn); diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 20f150d17a96..15c52f1f95f1 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1844,10 +1844,11 @@ static void nvmet_tcp_tls_handshake_done(void *data, int status, if (!status) status = nvmet_tcp_tls_key_lookup(queue, peerid); + if (!status) + status = nvmet_tcp_set_queue_sock(queue); + if (status) nvmet_tcp_schedule_release_queue(queue); - else - nvmet_tcp_set_queue_sock(queue); kref_put(&queue->kref, nvmet_tcp_release_queue); } @@ -1999,6 +2000,12 @@ out_free_connect: nvmet_tcp_free_cmd(&queue->connect); out_ida_remove: ida_free(&nvmet_tcp_queue_ida, queue->idx); + /* + * Drain the page fragment cache if any allocations were done. + * The first allocation using pf_cache is nvmet_tcp_alloc_cmd() + * for queue->connect after ida_alloc(). + */ + page_frag_cache_drain(&queue->pf_cache); out_sock: fput(queue->sock->file); out_free_queue: diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 311cb2e5a5c0..e871181751f3 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1468,18 +1468,16 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np); of_node_put(cell_np); if (!cell_entry) { - __nvmem_device_put(nvmem); nvmem_layout_module_put(nvmem); - if (nvmem->layout) - return ERR_PTR(-EPROBE_DEFER); - else - return ERR_PTR(-ENOENT); + ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT; + __nvmem_device_put(nvmem); + return ERR_PTR(ret); } cell = nvmem_create_cell(cell_entry, id, cell_index); if (IS_ERR(cell)) { - __nvmem_device_put(nvmem); nvmem_layout_module_put(nvmem); + __nvmem_device_put(nvmem); } return cell; @@ -1593,8 +1591,8 @@ void nvmem_cell_put(struct nvmem_cell *cell) kfree_const(cell->id); kfree(cell); - __nvmem_device_put(nvmem); nvmem_layout_module_put(nvmem); + __nvmem_device_put(nvmem); } EXPORT_SYMBOL_GPL(nvmem_cell_put); diff --git a/drivers/nvmem/layouts/onie-tlv.c b/drivers/nvmem/layouts/onie-tlv.c index 0967a32319a2..8b0f3c1b8a0e 100644 --- a/drivers/nvmem/layouts/onie-tlv.c +++ b/drivers/nvmem/layouts/onie-tlv.c @@ -119,7 +119,7 @@ static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem, cell.name = onie_tlv_cell_name(tlv.type); if (!cell.name) - continue; + goto next; cell.offset = hdr_len + offset + sizeof(tlv.type) + sizeof(tlv.len); cell.bytes = tlv.len; @@ -132,6 +132,7 @@ static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem, return ret; } +next: offset += sizeof(tlv) + tlv.len; } diff --git a/drivers/of/device.c b/drivers/of/device.c index f7e75e527667..be4e1584e0af 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -26,7 +26,7 @@ const struct of_device_id *of_match_device(const struct of_device_id *matches, const struct device *dev) { - if (!matches || !dev->of_node || dev->of_node_reused) + if (!matches || !dev->of_node || dev_of_node_reused(dev)) return NULL; return of_match_node(matches, dev->of_node); } @@ -192,7 +192,7 @@ ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len) { ssize_t sl; - if (!dev || !dev->of_node || dev->of_node_reused) + if (!dev || !dev->of_node || dev_of_node_reused(dev)) return -ENODEV; sl = of_modalias(dev->of_node, str, len - 2); @@ -254,7 +254,7 @@ int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env * { int sl; - if ((!dev) || (!dev->of_node) || dev->of_node_reused) + if ((!dev) || (!dev->of_node) || dev_of_node_reused(dev)) return -ENODEV; /* Devicetree modalias is tricky, we add it in 2 steps */ diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index ade288372101..aa450425ec1e 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -225,7 +225,6 @@ static void __of_attach_node(struct device_node *np) np->sibling = np->parent->child; np->parent->child = np; of_node_clear_flag(np, OF_DETACHED); - fwnode_set_flag(&np->fwnode, FWNODE_FLAG_NOT_DEVICE); raw_spin_unlock_irqrestore(&devtree_lock, flags); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index c1c5686fc7b1..4e45f3414c2c 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -185,6 +185,15 @@ static int overlay_notify(struct overlay_changeset *ovcs, return 0; } +static void overlay_fw_devlink_refresh(struct overlay_changeset *ovcs) +{ + for (int i = 0; i < ovcs->count; i++) { + struct device_node *np = ovcs->fragments[i].target; + + fw_devlink_refresh_fwnode(of_fwnode_handle(np)); + } +} + /* * The values of properties in the "/__symbols__" node are paths in * the ovcs->overlay_root. When duplicating the properties, the paths @@ -951,6 +960,12 @@ static int of_overlay_apply(struct overlay_changeset *ovcs, pr_err("overlay apply changeset entry notify error %d\n", ret); /* notify failure is not fatal, continue */ + /* + * Needs to happen after changeset notify to give the listeners a chance + * to finish creating all the devices they need to create. + */ + overlay_fw_devlink_refresh(ovcs); + ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY); if (ret_tmp) if (!ret) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index a42224f9d1a8..53bca8c6f781 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -744,11 +744,6 @@ static int of_platform_notify(struct notifier_block *nb, if (of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); /* pdev_parent may be NULL when no bus platform device */ pdev_parent = of_find_device_by_node(parent); pdev = of_platform_device_create(rd->dn, NULL, diff --git a/drivers/opp/core.c b/drivers/opp/core.c index da3f5eba4341..ab0b0a2f85a1 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2088,11 +2088,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, return ret; list_add(&new_opp->node, head); + new_opp->opp_table = opp_table; + kref_init(&new_opp->kref); } - new_opp->opp_table = opp_table; - kref_init(&new_opp->kref); - opp_debug_create_one(new_opp, opp_table); if (!_opp_supported_by_regulators(new_opp, opp_table)) { diff --git a/drivers/opp/of.c b/drivers/opp/of.c index f96adfd5b219..c02e20632fa6 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -673,7 +673,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, */ if (unlikely(opp_table->regulator_count == -1)) { opp_table->regulator_count = 0; - return 0; + goto free_microwatt; } for (i = 0, j = 0; i < opp_table->regulator_count; i++) { @@ -696,6 +696,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, opp->supplies[i].u_watt = microwatt[i]; } +free_microwatt: kfree(microwatt); free_microamp: kfree(microamp); diff --git a/drivers/parport/share.c b/drivers/parport/share.c index ba5292828703..eb0977ca1605 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -214,10 +214,14 @@ static void get_lowlevel_driver(void) static int port_check(struct device *dev, void *dev_drv) { struct parport_driver *drv = dev_drv; + struct parport *port; /* only send ports, do not send other devices connected to bus */ - if (is_parport(dev)) - drv->match_port(to_parport_dev(dev)); + if (is_parport(dev)) { + port = to_parport_dev(dev); + if (test_bit(PARPORT_ANNOUNCED, &port->devflags)) + drv->match_port(port); + } return 0; } @@ -532,6 +536,7 @@ void parport_announce_port(struct parport *port) if (slave) attach_driver_chain(slave); } + set_bit(PARPORT_ANNOUNCED, &port->devflags); mutex_unlock(®istration_lock); } EXPORT_SYMBOL(parport_announce_port); @@ -561,6 +566,8 @@ void parport_remove_port(struct parport *port) mutex_lock(®istration_lock); + clear_bit(PARPORT_ANNOUNCED, &port->devflags); + /* Spread the word. */ detach_driver_chain(port); diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 6da569fd3b8f..8b18c4ba845c 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -38,7 +38,7 @@ int pci_set_of_node(struct pci_dev *dev) struct device *pdev __free(put_device) = bus_find_device_by_of_node(&platform_bus_type, node); if (pdev) - dev->bus->dev.of_node_reused = true; + dev_set_of_node_reused(&dev->bus->dev); device_set_node(&dev->dev, of_fwnode_handle(no_free_ptr(node))); return 0; diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 97cff5b8ca88..31246bac84f1 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -39,7 +39,7 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, * If we got here then the PCI device is the second after the * power control platform device. Mark its OF node as reused. */ - dev->of_node_reused = true; + dev_set_of_node_reused(dev); break; } diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index ab90932fc2d0..245e7bb763b9 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -188,7 +188,7 @@ config FUJITSU_UNCORE_PMU monitoring Uncore events. config QCOM_L2_PMU - bool "Qualcomm Technologies L2-cache PMU" + bool "Qualcomm L2-cache PMU" depends on ARCH_QCOM && ARM64 && ACPI select QCOM_KRYO_L2_ACCESSORS help @@ -198,7 +198,7 @@ config QCOM_L2_PMU monitoring L2 cache events. config QCOM_L3_PMU - bool "Qualcomm Technologies L3-cache PMU" + bool "Qualcomm L3-cache PMU" depends on ARCH_QCOM && ARM64 && ACPI select QCOM_IRQ_COMBINER help diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index f5305c8fdca4..6e5cc4086a9e 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -197,13 +197,14 @@ enum cmn_model { CMN600 = 1, CMN650 = 2, - CMN700 = 4, - CI700 = 8, + CI700 = 4, + CMN700 = 8, CMNS3 = 16, /* ...and then we can use bitmap tricks for commonality */ CMN_ANY = -1, NOT_CMN600 = -2, - CMN_650ON = CMN650 | CMN700 | CMNS3, + CMN_700ON = ~(CMN700 - 1), + CMN_650ON = CMN_700ON | CMN650, }; /* Actual part numbers and revision IDs defined by the hardware */ @@ -919,14 +920,14 @@ static struct attribute *arm_cmn_event_attrs[] = { CMN_EVENT_DVM(NOT_CMN600, txsnp_stall, 0x0a), CMN_EVENT_DVM(NOT_CMN600, trkfull, 0x0b), CMN_EVENT_DVM_OCC(NOT_CMN600, trk_occupancy, 0x0c), - CMN_EVENT_DVM_OCC(CMN700, trk_occupancy_cxha, 0x0d), - CMN_EVENT_DVM_OCC(CMN700, trk_occupancy_pdn, 0x0e), - CMN_EVENT_DVM(CMN700, trk_alloc, 0x0f), - CMN_EVENT_DVM(CMN700, trk_cxha_alloc, 0x10), - CMN_EVENT_DVM(CMN700, trk_pdn_alloc, 0x11), - CMN_EVENT_DVM(CMN700, txsnp_stall_limit, 0x12), - CMN_EVENT_DVM(CMN700, rxsnp_stall_starv, 0x13), - CMN_EVENT_DVM(CMN700, txsnp_sync_stall_op, 0x14), + CMN_EVENT_DVM_OCC(CMN_700ON, trk_occupancy_cxha, 0x0d), + CMN_EVENT_DVM_OCC(CMN_700ON, trk_occupancy_pdn, 0x0e), + CMN_EVENT_DVM(CMN_700ON, trk_alloc, 0x0f), + CMN_EVENT_DVM(CMN_700ON, trk_cxha_alloc, 0x10), + CMN_EVENT_DVM(CMN_700ON, trk_pdn_alloc, 0x11), + CMN_EVENT_DVM(CMN_700ON, txsnp_stall_limit, 0x12), + CMN_EVENT_DVM(CMN_700ON, rxsnp_stall_starv, 0x13), + CMN_EVENT_DVM(CMN_700ON, txsnp_sync_stall_op, 0x14), CMN_EVENT_HNF(CMN_ANY, cache_miss, 0x01), CMN_EVENT_HNF(CMN_ANY, slc_sf_cache_access, 0x02), diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 64315b0edf2a..e3128b0045d2 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -26,7 +26,6 @@ #include <linux/interrupt.h> #include <linux/bitops.h> #include <linux/pinctrl/pinconf.h> -#include <linux/dmi.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinmux.h> #include <linux/string_choices.h> @@ -40,39 +39,6 @@ static struct amd_gpio *pinctrl_dev; #endif -static const struct dmi_system_id amd_gpio_quirk_yoga7_14agp11[] = { - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "83TD"), - DMI_MATCH(DMI_BOARD_NAME, "LNVNB161216"), - }, - }, - { } -}; - -static void amd_gpio_apply_quirks(struct amd_gpio *gpio_dev) -{ - const unsigned int pin = 157; /* WACF2200 GpioInt per ACPI _CRS */ - unsigned long flags; - u32 reg; - - if (!dmi_check_system(amd_gpio_quirk_yoga7_14agp11)) - return; - if (pin >= gpio_dev->gc.ngpio) - return; - - raw_spin_lock_irqsave(&gpio_dev->lock, flags); - reg = readl(gpio_dev->base + pin * 4); - reg |= BIT(INTERRUPT_ENABLE_OFF) | BIT(INTERRUPT_MASK_OFF); - writel(reg, gpio_dev->base + pin * 4); - raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); - - dev_info(&gpio_dev->pdev->dev, - "Enabled IRQ for GPIO %u (Yoga 7 14AGP11 touchscreen)\n", - pin); -} - static int amd_gpio_get_direction(struct gpio_chip *gc, unsigned offset) { unsigned long flags; @@ -1253,7 +1219,6 @@ static int amd_gpio_probe(struct platform_device *pdev) /* Disable and mask interrupts */ amd_gpio_irq_init(gpio_dev); - amd_gpio_apply_quirks(gpio_dev); girq = &gpio_dev->gc.irq; gpio_irq_chip_set_chip(girq, &amd_gpio_irqchip); diff --git a/drivers/pinctrl/pinctrl-mcp23s08_spi.c b/drivers/pinctrl/pinctrl-mcp23s08_spi.c index 54f61c8cb1c0..30775d31bd69 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08_spi.c +++ b/drivers/pinctrl/pinctrl-mcp23s08_spi.c @@ -10,6 +10,7 @@ #include "pinctrl-mcp23s08.h" #define MCP_MAX_DEV_PER_CS 8 +#define MCP23S08_SPI_BASE 0x40 /* * A given spi_device can represent up to eight mcp23sxx chips @@ -143,13 +144,13 @@ static int mcp23s08_probe(struct spi_device *spi) unsigned int addr; int chips; int ret; - u32 v; + u8 v; info = spi_get_device_match_data(spi); - ret = device_property_read_u32(dev, "microchip,spi-present-mask", &v); + ret = device_property_read_u8(dev, "microchip,spi-present-mask", &v); if (ret) { - ret = device_property_read_u32(dev, "mcp,spi-present-mask", &v); + ret = device_property_read_u8(dev, "mcp,spi-present-mask", &v); if (ret) { dev_err(dev, "missing spi-present-mask"); return ret; @@ -173,6 +174,8 @@ static int mcp23s08_probe(struct spi_device *spi) for_each_set_bit(addr, &spi_present_mask, MCP_MAX_DEV_PER_CS) { data->mcp[addr] = &data->chip[--chips]; data->mcp[addr]->irq = spi->irq; + data->mcp[addr]->dev = dev; + data->mcp[addr]->addr = MCP23S08_SPI_BASE | (addr << 1); ret = mcp23s08_spi_regmap_init(data->mcp[addr], dev, addr, info); if (ret) @@ -184,7 +187,7 @@ static int mcp23s08_probe(struct spi_device *spi) if (!data->mcp[addr]->pinctrl_desc.name) return -ENOMEM; - ret = mcp23s08_probe_one(data->mcp[addr], dev, 0x40 | (addr << 1), + ret = mcp23s08_probe_one(data->mcp[addr], dev, MCP23S08_SPI_BASE | (addr << 1), info->type, -1); if (ret < 0) return ret; diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 2281d6dacc9b..ca2e8442026e 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -6,6 +6,7 @@ menuconfig CHROME_PLATFORMS bool "Platform support for Chrome hardware" depends on X86 || ARM || ARM64 || COMPILE_TEST + depends on !CPU_BIG_ENDIAN || COMPILE_TEST help Say Y here to get to see options for platform support for various Chromebooks and Chromeboxes. This option alone does @@ -75,7 +76,6 @@ config CHROMEOS_OF_HW_PROBER config CROS_EC tristate "ChromeOS Embedded Controller" select CROS_EC_PROTO - depends on X86 || ARM || ARM64 || COMPILE_TEST help If you say Y here you get support for the ChromeOS Embedded Controller (EC) providing keyboard, battery and power services. diff --git a/drivers/platform/chrome/chromeos_privacy_screen.c b/drivers/platform/chrome/chromeos_privacy_screen.c index abc5d189a389..407b04207de2 100644 --- a/drivers/platform/chrome/chromeos_privacy_screen.c +++ b/drivers/platform/chrome/chromeos_privacy_screen.c @@ -104,6 +104,9 @@ static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = { static int chromeos_privacy_screen_probe(struct platform_device *pdev) { + if (!ACPI_COMPANION(&pdev->dev)) + return -ENODEV; + struct drm_privacy_screen *drm_privacy_screen = drm_privacy_screen_register(&pdev->dev, &chromeos_privacy_screen_ops, diff --git a/drivers/platform/chrome/chromeos_tbmc.c b/drivers/platform/chrome/chromeos_tbmc.c index 5133806b2d95..fd756761a481 100644 --- a/drivers/platform/chrome/chromeos_tbmc.c +++ b/drivers/platform/chrome/chromeos_tbmc.c @@ -69,9 +69,13 @@ static int chromeos_tbmc_probe(struct platform_device *pdev) { struct input_dev *idev; struct device *dev = &pdev->dev; - struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *adev; int ret; + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + idev = devm_input_allocate_device(dev); if (!idev) return -ENOMEM; diff --git a/drivers/platform/chrome/cros_ec_chardev.c b/drivers/platform/chrome/cros_ec_chardev.c index 002be3352100..47e03014dcbe 100644 --- a/drivers/platform/chrome/cros_ec_chardev.c +++ b/drivers/platform/chrome/cros_ec_chardev.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/fs.h> +#include <linux/kref.h> #include <linux/miscdevice.h> #include <linux/mod_devicetable.h> #include <linux/module.h> @@ -22,6 +23,7 @@ #include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/poll.h> +#include <linux/rwsem.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/uaccess.h> @@ -31,14 +33,44 @@ /* Arbitrary bounded size for the event queue */ #define CROS_MAX_EVENT_LEN PAGE_SIZE -struct chardev_priv { +/* + * Platform device driver data. + */ +struct chardev_pdata { + struct miscdevice misc; + struct kref kref; + struct rw_semaphore ec_dev_sem; struct cros_ec_device *ec_dev; + u16 cmd_offset; + struct blocking_notifier_head subscribers; + struct notifier_block relay; +}; + +static void chardev_pdata_release(struct kref *kref) +{ + struct chardev_pdata *pdata = container_of(kref, typeof(*pdata), kref); + + kfree(pdata); +} + +static int cros_ec_chardev_relay_event(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct chardev_pdata *pdata = container_of(nb, typeof(*pdata), relay); + + blocking_notifier_call_chain(&pdata->subscribers, queued_during_suspend, + _notify); + return NOTIFY_OK; +} + +struct chardev_priv { struct notifier_block notifier; wait_queue_head_t wait_event; unsigned long event_mask; struct list_head events; size_t event_len; - u16 cmd_offset; + struct chardev_pdata *pdata; }; struct ec_event { @@ -61,10 +93,10 @@ static int ec_get_version(struct chardev_priv *priv, char *str, int maxlen) if (!msg) return -ENOMEM; - msg->command = EC_CMD_GET_VERSION + priv->cmd_offset; + msg->command = EC_CMD_GET_VERSION + priv->pdata->cmd_offset; msg->insize = sizeof(*resp); - ret = cros_ec_cmd_xfer_status(priv->ec_dev, msg); + ret = cros_ec_cmd_xfer_status(priv->pdata->ec_dev, msg); if (ret < 0) { snprintf(str, maxlen, "Unknown EC version, returned error: %d\n", @@ -92,10 +124,18 @@ static int cros_ec_chardev_mkbp_event(struct notifier_block *nb, { struct chardev_priv *priv = container_of(nb, struct chardev_priv, notifier); - struct cros_ec_device *ec_dev = priv->ec_dev; + struct cros_ec_device *ec_dev; struct ec_event *event; - unsigned long event_bit = 1 << ec_dev->event_data.event_type; - int total_size = sizeof(*event) + ec_dev->event_size; + unsigned long event_bit; + int total_size; + + guard(rwsem_read)(&priv->pdata->ec_dev_sem); + if (!priv->pdata->ec_dev) + return NOTIFY_DONE; + ec_dev = priv->pdata->ec_dev; + + event_bit = 1 << ec_dev->event_data.event_type; + total_size = sizeof(*event) + ec_dev->event_size; if (!(event_bit & priv->event_mask) || (priv->event_len + total_size) > CROS_MAX_EVENT_LEN) @@ -157,8 +197,7 @@ out: static int cros_ec_chardev_open(struct inode *inode, struct file *filp) { struct miscdevice *mdev = filp->private_data; - struct cros_ec_dev *ec = dev_get_drvdata(mdev->parent); - struct cros_ec_device *ec_dev = ec->ec_dev; + struct chardev_pdata *pdata = container_of(mdev, typeof(*pdata), misc); struct chardev_priv *priv; int ret; @@ -166,18 +205,20 @@ static int cros_ec_chardev_open(struct inode *inode, struct file *filp) if (!priv) return -ENOMEM; - priv->ec_dev = ec_dev; - priv->cmd_offset = ec->cmd_offset; + priv->pdata = pdata; + kref_get(&pdata->kref); filp->private_data = priv; INIT_LIST_HEAD(&priv->events); init_waitqueue_head(&priv->wait_event); nonseekable_open(inode, filp); priv->notifier.notifier_call = cros_ec_chardev_mkbp_event; - ret = blocking_notifier_chain_register(&ec_dev->event_notifier, + ret = blocking_notifier_chain_register(&pdata->subscribers, &priv->notifier); if (ret) { - dev_err(ec_dev->dev, "failed to register event notifier\n"); + dev_err(pdata->ec_dev->dev, + "failed to register event notifier\n"); + kref_put(&priv->pdata->kref, chardev_pdata_release); kfree(priv); } @@ -188,6 +229,10 @@ static __poll_t cros_ec_chardev_poll(struct file *filp, poll_table *wait) { struct chardev_priv *priv = filp->private_data; + guard(rwsem_read)(&priv->pdata->ec_dev_sem); + if (!priv->pdata->ec_dev) + return EPOLLHUP; + poll_wait(filp, &priv->wait_event, wait); if (list_empty(&priv->events)) @@ -205,6 +250,10 @@ static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer, size_t count; int ret; + guard(rwsem_read)(&priv->pdata->ec_dev_sem); + if (!priv->pdata->ec_dev) + return -ENODEV; + if (priv->event_mask) { /* queued MKBP event */ struct ec_event *event; @@ -251,11 +300,11 @@ static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer, static int cros_ec_chardev_release(struct inode *inode, struct file *filp) { struct chardev_priv *priv = filp->private_data; - struct cros_ec_device *ec_dev = priv->ec_dev; struct ec_event *event, *e; - blocking_notifier_chain_unregister(&ec_dev->event_notifier, + blocking_notifier_chain_unregister(&priv->pdata->subscribers, &priv->notifier); + kref_put(&priv->pdata->kref, chardev_pdata_release); list_for_each_entry_safe(event, e, &priv->events, node) { list_del(&event->node); @@ -298,8 +347,8 @@ static long cros_ec_chardev_ioctl_xcmd(struct chardev_priv *priv, void __user *a goto exit; } - s_cmd->command += priv->cmd_offset; - ret = cros_ec_cmd_xfer(priv->ec_dev, s_cmd); + s_cmd->command += priv->pdata->cmd_offset; + ret = cros_ec_cmd_xfer(priv->pdata->ec_dev, s_cmd); /* Only copy data to userland if data was received. */ if (ret < 0) goto exit; @@ -313,7 +362,7 @@ exit: static long cros_ec_chardev_ioctl_readmem(struct chardev_priv *priv, void __user *arg) { - struct cros_ec_device *ec_dev = priv->ec_dev; + struct cros_ec_device *ec_dev = priv->pdata->ec_dev; struct cros_ec_readmem s_mem = { }; long num; @@ -343,6 +392,10 @@ static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd, { struct chardev_priv *priv = filp->private_data; + guard(rwsem_read)(&priv->pdata->ec_dev_sem); + if (!priv->pdata->ec_dev) + return -ENODEV; + if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) return -ENOTTY; @@ -374,28 +427,61 @@ static int cros_ec_chardev_probe(struct platform_device *pdev) { struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent); struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev); - struct miscdevice *misc; + struct chardev_pdata *pdata; + int ret; - /* Create a char device: we want to create it anew */ - misc = devm_kzalloc(&pdev->dev, sizeof(*misc), GFP_KERNEL); - if (!misc) + pdata = kzalloc_obj(*pdata); + if (!pdata) return -ENOMEM; - misc->minor = MISC_DYNAMIC_MINOR; - misc->fops = &chardev_fops; - misc->name = ec_platform->ec_name; - misc->parent = pdev->dev.parent; + platform_set_drvdata(pdev, pdata); + kref_init(&pdata->kref); + init_rwsem(&pdata->ec_dev_sem); + pdata->ec_dev = ec->ec_dev; + pdata->cmd_offset = ec->cmd_offset; + BLOCKING_INIT_NOTIFIER_HEAD(&pdata->subscribers); + pdata->relay.notifier_call = cros_ec_chardev_relay_event; + ret = blocking_notifier_chain_register(&pdata->ec_dev->event_notifier, + &pdata->relay); + if (ret) { + dev_err(&pdev->dev, "failed to register event notifier\n"); + goto err_put_pdata; + } + + pdata->misc.minor = MISC_DYNAMIC_MINOR; + pdata->misc.fops = &chardev_fops; + pdata->misc.name = ec_platform->ec_name; + pdata->misc.parent = pdev->dev.parent; - dev_set_drvdata(&pdev->dev, misc); + ret = misc_register(&pdata->misc); + if (ret) { + dev_err(&pdev->dev, "failed to register misc device\n"); + goto err_unregister_notifier; + } - return misc_register(misc); + return 0; +err_unregister_notifier: + blocking_notifier_chain_unregister(&pdata->ec_dev->event_notifier, + &pdata->relay); +err_put_pdata: + kref_put(&pdata->kref, chardev_pdata_release); + return ret; } static void cros_ec_chardev_remove(struct platform_device *pdev) { - struct miscdevice *misc = dev_get_drvdata(&pdev->dev); + struct chardev_pdata *pdata = platform_get_drvdata(pdev); + struct cros_ec_device *ec_dev = pdata->ec_dev; - misc_deregister(misc); + /* stop new fops from being created */ + misc_deregister(&pdata->misc); + /* stop existing fops from running */ + scoped_guard(rwsem_write, &pdata->ec_dev_sem) + pdata->ec_dev = NULL; + + blocking_notifier_chain_unregister(&ec_dev->event_notifier, + &pdata->relay); + kref_put(&pdata->kref, chardev_pdata_release); } static const struct platform_device_id cros_ec_chardev_id[] = { diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index def1144a077e..2f46be4a2756 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -348,7 +348,7 @@ MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match); #endif static const struct i2c_device_id cros_ec_i2c_id[] = { - { "cros-ec-i2c" }, + { .name = "cros-ec-i2c" }, { } }; MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id); diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c index 9bad8f72680e..f938c3fc84e4 100644 --- a/drivers/platform/chrome/cros_ec_sensorhub.c +++ b/drivers/platform/chrome/cros_ec_sensorhub.c @@ -122,8 +122,12 @@ static int cros_ec_sensorhub_register(struct device *dev, sensor_type[sensorhub->resp->info.type]++; } - if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) + if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) { ec->has_kb_wake_angle = true; + if (ec->group && sysfs_update_group(&ec->class_dev.kobj, + ec->group)) + dev_warn(dev, "Unable to update sysfs"); + } if (cros_ec_check_features(ec, EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) { diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index f22e9523da3e..9d3767ab1548 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -405,7 +405,8 @@ static int cros_ec_sysfs_probe(struct platform_device *pd) struct device *dev = &pd->dev; int ret; - ret = sysfs_create_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group); + ec_dev->group = &cros_ec_attr_group; + ret = sysfs_create_group(&ec_dev->class_dev.kobj, ec_dev->group); if (ret < 0) dev_err(dev, "failed to create attributes. err=%d\n", ret); @@ -416,7 +417,7 @@ static void cros_ec_sysfs_remove(struct platform_device *pd) { struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); - sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group); + sysfs_remove_group(&ec_dev->class_dev.kobj, ec_dev->group); } static const struct platform_device_id cros_ec_sysfs_id[] = { diff --git a/drivers/platform/chrome/cros_hps_i2c.c b/drivers/platform/chrome/cros_hps_i2c.c index ac6498c593e3..3b9485627831 100644 --- a/drivers/platform/chrome/cros_hps_i2c.c +++ b/drivers/platform/chrome/cros_hps_i2c.c @@ -131,7 +131,7 @@ static int hps_resume(struct device *dev) static DEFINE_RUNTIME_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL); static const struct i2c_device_id hps_i2c_id[] = { - { "cros-hps" }, + { .name = "cros-hps" }, { } }; MODULE_DEVICE_TABLE(i2c, hps_i2c_id); diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c index f4c2282129f5..80dc52833dc9 100644 --- a/drivers/platform/chrome/cros_kbd_led_backlight.c +++ b/drivers/platform/chrome/cros_kbd_led_backlight.c @@ -32,12 +32,11 @@ struct keyboard_led { * @brightness_set_blocking: Set LED brightness level. It can block the * caller for the time required for accessing a * LED device register - * @max_brightness: Maximum brightness. * * See struct led_classdev in include/linux/leds.h for more details. */ struct keyboard_led_drvdata { - int (*init)(struct platform_device *pdev); + int (*init)(struct platform_device *pdev, struct keyboard_led *keyboard_led); enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); @@ -45,12 +44,8 @@ struct keyboard_led_drvdata { enum led_brightness brightness); int (*brightness_set_blocking)(struct led_classdev *led_cdev, enum led_brightness brightness); - - enum led_brightness max_brightness; }; -#define KEYBOARD_BACKLIGHT_MAX 100 - #ifdef CONFIG_ACPI /* Keyboard LED ACPI Device must be defined in firmware */ @@ -94,7 +89,8 @@ keyboard_led_get_brightness_acpi(struct led_classdev *cdev) return brightness; } -static int keyboard_led_init_acpi(struct platform_device *pdev) +static int keyboard_led_init_acpi(struct platform_device *pdev, + struct keyboard_led *keyboard_led) { acpi_handle handle; acpi_status status; @@ -116,17 +112,15 @@ static const struct keyboard_led_drvdata keyboard_led_drvdata_acpi = { .init = keyboard_led_init_acpi, .brightness_set = keyboard_led_set_brightness_acpi, .brightness_get = keyboard_led_get_brightness_acpi, - .max_brightness = KEYBOARD_BACKLIGHT_MAX, }; #endif /* CONFIG_ACPI */ -#if IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) -static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev) +static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev, + struct keyboard_led *keyboard_led) { struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); struct cros_ec_device *cros_ec = ec_dev->ec_dev; - struct keyboard_led *keyboard_led = platform_get_drvdata(pdev); keyboard_led->ec = cros_ec; @@ -175,15 +169,8 @@ static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = { .init = keyboard_led_init_ec_pwm_mfd, .brightness_set_blocking = keyboard_led_set_brightness_ec_pwm, .brightness_get = keyboard_led_get_brightness_ec_pwm, - .max_brightness = KEYBOARD_BACKLIGHT_MAX, }; -#else /* IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) */ - -static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = {}; - -#endif /* IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) */ - static int keyboard_led_is_mfd_device(struct platform_device *pdev) { return IS_ENABLED(CONFIG_MFD_CROS_EC_DEV) && mfd_get_cell(pdev); @@ -205,17 +192,16 @@ static int keyboard_led_probe(struct platform_device *pdev) keyboard_led = devm_kzalloc(&pdev->dev, sizeof(*keyboard_led), GFP_KERNEL); if (!keyboard_led) return -ENOMEM; - platform_set_drvdata(pdev, keyboard_led); if (drvdata->init) { - err = drvdata->init(pdev); + err = drvdata->init(pdev, keyboard_led); if (err) return err; } keyboard_led->cdev.name = "chromeos::kbd_backlight"; keyboard_led->cdev.flags |= LED_CORE_SUSPENDRESUME | LED_REJECT_NAME_CONFLICT; - keyboard_led->cdev.max_brightness = drvdata->max_brightness; + keyboard_led->cdev.max_brightness = 100; keyboard_led->cdev.brightness_set = drvdata->brightness_set; keyboard_led->cdev.brightness_set_blocking = drvdata->brightness_set_blocking; keyboard_led->cdev.brightness_get = drvdata->brightness_get; diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c index b6e935badc0e..1b5cb89839e0 100644 --- a/drivers/platform/chrome/wilco_ec/event.c +++ b/drivers/platform/chrome/wilco_ec/event.c @@ -452,8 +452,13 @@ static void hangup_device(struct event_device_data *dev_data) static int event_device_probe(struct platform_device *pdev) { struct event_device_data *dev_data; + struct acpi_device *adev; int error, minor; + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return -ENODEV; + minor = ida_alloc_max(&event_ida, EVENT_MAX_DEV-1, GFP_KERNEL); if (minor < 0) { error = minor; @@ -494,8 +499,7 @@ static int event_device_probe(struct platform_device *pdev) goto free_dev_data; /* Install an ACPI notify handler. */ - error = acpi_dev_install_notify_handler(ACPI_COMPANION(&pdev->dev), - ACPI_DEVICE_NOTIFY, + error = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, event_device_notify, &pdev->dev); if (error) goto free_cdev; diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c index de695f1944ab..42e50c9b4fb9 100644 --- a/drivers/pmdomain/imx/gpc.c +++ b/drivers/pmdomain/imx/gpc.c @@ -487,7 +487,7 @@ static int imx_gpc_probe(struct platform_device *pdev) domain->ipg_rate_mhz = ipg_rate_mhz; pd_pdev->dev.parent = &pdev->dev; - pd_pdev->dev.of_node = np; + pd_pdev->dev.of_node = of_node_get(np); pd_pdev->dev.fwnode = of_fwnode_handle(np); ret = platform_device_add(pd_pdev); diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c index 18d33bc35dee..949e4115f930 100644 --- a/drivers/pmdomain/ti/ti_sci_pm_domains.c +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -86,7 +86,7 @@ static inline void ti_sci_pd_set_wkup_constraint(struct device *dev) const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; int ret; - if (device_may_wakeup(dev)) { + if (device_may_wakeup(dev) || device_wakeup_path(dev)) { /* * If device can wakeup using IO daisy chain wakeups, * we do not want to set a constraint. diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 4dff71be11b6..89694092981f 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -965,6 +965,29 @@ int pwrseq_power_off(struct pwrseq_desc *desc) } EXPORT_SYMBOL_GPL(pwrseq_power_off); +/** + * pwrseq_to_device() - Get the pwrseq device pointer from a descriptor. + * @desc: Descriptor referencing the power sequencer. + * + * Return the 'dev' pointer of the power sequencer device associated with @desc. + * Consumer drivers can use this to query the pwrseq provider's device tree + * node, for example to check for the existence of specific properties. + * + * Since pwrseq_get() already takes a reference to the pwrseq device, this + * function does not take an additional reference. + * + * Returns: + * Pointer to the pwrseq struct device, or NULL if @desc is NULL. + */ +struct device *pwrseq_to_device(struct pwrseq_desc *desc) +{ + if (!desc) + return NULL; + + return &desc->pwrseq->dev; +} +EXPORT_SYMBOL_GPL(pwrseq_to_device); + #if IS_ENABLED(CONFIG_DEBUG_FS) struct pwrseq_debugfs_count_ctx { @@ -1043,7 +1066,7 @@ static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data) struct pwrseq_target *target; struct pwrseq_unit *unit; - seq_printf(seq, "%s:\n", dev_name(dev)); + seq_printf(seq, "%s (%s):\n", dev_name(dev), dev_name(dev->parent)); seq_puts(seq, " targets:\n"); list_for_each_entry(target, &pwrseq->targets, list) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index ef69ae268059..94c3f4b7ee36 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -7,6 +7,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/list.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> @@ -19,6 +20,13 @@ #include <linux/serdev.h> #include <linux/slab.h> +struct pwrseq_pci_dev { + struct serdev_device *serdev; + struct of_changeset *ocs; + struct pci_dev *pdev; + struct list_head list; +}; + struct pwrseq_pcie_m2_pdata { const struct pwrseq_target_data **targets; }; @@ -32,9 +40,9 @@ struct pwrseq_pcie_m2_ctx { struct notifier_block nb; struct gpio_desc *w_disable1_gpio; struct gpio_desc *w_disable2_gpio; - struct serdev_device *serdev; - struct of_changeset *ocs; struct device *dev; + struct list_head pci_devices; + struct mutex list_lock; }; static int pwrseq_pcie_m2_vregs_enable(struct pwrseq_device *pwrseq) @@ -177,39 +185,57 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, return PWRSEQ_NO_MATCH; } -static int pwrseq_m2_pcie_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, - struct device_node *parent) +static const struct pci_device_id pwrseq_m2_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1107), + .driver_data = (kernel_ulong_t)"qcom,wcn7850-bt" }, + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1103), + .driver_data = (kernel_ulong_t)"qcom,wcn6855-bt" }, + { } /* Sentinel */ +}; + +static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, + struct pwrseq_pci_dev *pci_dev, + struct device_node *parent, + struct pci_dev *pdev) { + const struct pci_device_id *id; struct device *dev = ctx->dev; + const char *compatible; struct device_node *np; int ret; - ctx->ocs = kzalloc_obj(*ctx->ocs); - if (!ctx->ocs) + id = pci_match_id(pwrseq_m2_pci_ids, pdev); + if (WARN_ON_ONCE(!id)) /* Shouldn't happen */ + return -ENODEV; + + compatible = (const char *)id->driver_data; + + pci_dev->ocs = kzalloc_obj(*pci_dev->ocs); + if (!pci_dev->ocs) return -ENOMEM; - of_changeset_init(ctx->ocs); + of_changeset_init(pci_dev->ocs); - np = of_changeset_create_node(ctx->ocs, parent, "bluetooth"); + np = of_changeset_create_node(pci_dev->ocs, parent, "bluetooth"); if (!np) { dev_err(dev, "Failed to create bluetooth node\n"); ret = -ENODEV; goto err_destroy_changeset; } - ret = of_changeset_add_prop_string(ctx->ocs, np, "compatible", "qcom,wcn7850-bt"); + ret = of_changeset_add_prop_string(pci_dev->ocs, np, "compatible", compatible); if (ret) { dev_err(dev, "Failed to add bluetooth compatible: %d\n", ret); goto err_destroy_changeset; } - ret = of_changeset_apply(ctx->ocs); + ret = of_changeset_apply(pci_dev->ocs); if (ret) { dev_err(dev, "Failed to apply changeset: %d\n", ret); goto err_destroy_changeset; } - ret = device_add_of_node(&ctx->serdev->dev, np); + ret = device_add_of_node(&pci_dev->serdev->dev, np); if (ret) { dev_err(dev, "Failed to add OF node: %d\n", ret); goto err_revert_changeset; @@ -218,19 +244,21 @@ static int pwrseq_m2_pcie_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, return 0; err_revert_changeset: - of_changeset_revert(ctx->ocs); + of_changeset_revert(pci_dev->ocs); err_destroy_changeset: - of_changeset_destroy(ctx->ocs); - kfree(ctx->ocs); - ctx->ocs = NULL; + of_changeset_destroy(pci_dev->ocs); + kfree(pci_dev->ocs); + pci_dev->ocs = NULL; return ret; } -static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) +static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx, + struct pci_dev *pdev) { struct serdev_controller *serdev_ctrl; struct device *dev = ctx->dev; + struct pwrseq_pci_dev *pci_dev; int ret; struct device_node *serdev_parent __free(device_node) = @@ -248,58 +276,100 @@ static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) return 0; } - ctx->serdev = serdev_device_alloc(serdev_ctrl); - if (!ctx->serdev) { + /* Bail out if the serdev device was already created for the PCI dev */ + scoped_guard(mutex, &ctx->list_lock) { + list_for_each_entry(pci_dev, &ctx->pci_devices, list) { + if (pci_dev->pdev == pdev) + return 0; + } + } + + pci_dev = kzalloc(sizeof(*pci_dev), GFP_KERNEL); + if (!pci_dev) { ret = -ENOMEM; goto err_put_ctrl; } - ret = pwrseq_m2_pcie_create_bt_node(ctx, serdev_parent); + pci_dev->serdev = serdev_device_alloc(serdev_ctrl); + if (!pci_dev->serdev) { + ret = -ENOMEM; + goto err_free_pci_dev; + } + + ret = pwrseq_pcie_m2_create_bt_node(ctx, pci_dev, serdev_parent, pdev); if (ret) goto err_free_serdev; - ret = serdev_device_add(ctx->serdev); + ret = serdev_device_add(pci_dev->serdev); if (ret) { - dev_err(dev, "Failed to add serdev for WCN7850: %d\n", ret); + dev_err(dev, "Failed to add serdev for PCI device (%s): %d\n", + pci_name(pdev), ret); goto err_free_dt_node; } serdev_controller_put(serdev_ctrl); + pci_dev->pdev = pci_dev_get(pdev); + + mutex_lock(&ctx->list_lock); + list_add_tail(&pci_dev->list, &ctx->pci_devices); + mutex_unlock(&ctx->list_lock); + return 0; err_free_dt_node: - device_remove_of_node(&ctx->serdev->dev); - of_changeset_revert(ctx->ocs); - of_changeset_destroy(ctx->ocs); - kfree(ctx->ocs); - ctx->ocs = NULL; + device_remove_of_node(&pci_dev->serdev->dev); + of_changeset_revert(pci_dev->ocs); + of_changeset_destroy(pci_dev->ocs); + kfree(pci_dev->ocs); + pci_dev->ocs = NULL; err_free_serdev: - serdev_device_put(ctx->serdev); - ctx->serdev = NULL; + serdev_device_put(pci_dev->serdev); + pci_dev->serdev = NULL; +err_free_pci_dev: + kfree(pci_dev); err_put_ctrl: serdev_controller_put(serdev_ctrl); return ret; } -static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx) +static void __pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx, + struct pwrseq_pci_dev *pci_dev) { - if (ctx->serdev) { - device_remove_of_node(&ctx->serdev->dev); - serdev_device_remove(ctx->serdev); - ctx->serdev = NULL; + if (pci_dev->serdev) { + device_remove_of_node(&pci_dev->serdev->dev); + serdev_device_remove(pci_dev->serdev); + } + + if (pci_dev->ocs) { + of_changeset_revert(pci_dev->ocs); + of_changeset_destroy(pci_dev->ocs); + kfree(pci_dev->ocs); } - if (ctx->ocs) { - of_changeset_revert(ctx->ocs); - of_changeset_destroy(ctx->ocs); - kfree(ctx->ocs); - ctx->ocs = NULL; + pci_dev_put(pci_dev->pdev); + list_del(&pci_dev->list); + kfree(pci_dev); +} + +static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx, + struct pci_dev *pdev) +{ + struct pwrseq_pci_dev *pci_dev, *tmp; + + mutex_lock(&ctx->list_lock); + list_for_each_entry_safe(pci_dev, tmp, &ctx->pci_devices, list) { + if (!pdev || pci_dev->pdev == pdev) { + __pwrseq_pcie_m2_remove_serdev(ctx, pci_dev); + if (pdev) + break; + } } + mutex_unlock(&ctx->list_lock); } -static int pwrseq_m2_pcie_notify(struct notifier_block *nb, unsigned long action, +static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action, void *data) { struct pwrseq_pcie_m2_ctx *ctx = container_of(nb, struct pwrseq_pcie_m2_ctx, nb); @@ -318,17 +388,15 @@ static int pwrseq_m2_pcie_notify(struct notifier_block *nb, unsigned long action switch (action) { case BUS_NOTIFY_ADD_DEVICE: - /* Create serdev device for WCN7850 */ - if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) { - ret = pwrseq_pcie_m2_create_serdev(ctx); + if (pci_match_id(pwrseq_m2_pci_ids, pdev)) { + ret = pwrseq_pcie_m2_create_serdev_one(ctx, pdev); if (ret) return notifier_from_errno(ret); } break; case BUS_NOTIFY_REMOVED_DEVICE: - /* Destroy serdev device for WCN7850 */ - if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) - pwrseq_pcie_m2_remove_serdev(ctx); + if (pci_match_id(pwrseq_m2_pci_ids, pdev)) + pwrseq_pcie_m2_remove_serdev(ctx, pdev); break; } @@ -353,7 +421,7 @@ static bool pwrseq_pcie_m2_check_remote_node(struct device *dev, u8 port, u8 end * protocol device needs to be created manually with the help of the notifier * of the discoverable bus like PCIe. */ -static int pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx, struct device *dev) +static int pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx) { int ret; @@ -361,18 +429,56 @@ static int pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx, stru * Register a PCI notifier for Key E connector that has PCIe as Port * 0/Endpoint 0 interface and Serial as Port 3/Endpoint 0 interface. */ - if (pwrseq_pcie_m2_check_remote_node(dev, 3, 0, "serial")) { - if (pwrseq_pcie_m2_check_remote_node(dev, 0, 0, "pcie")) { - ctx->dev = dev; - ctx->nb.notifier_call = pwrseq_m2_pcie_notify; - ret = bus_register_notifier(&pci_bus_type, &ctx->nb); - if (ret) - return dev_err_probe(dev, ret, - "Failed to register notifier for serdev\n"); + if (!pwrseq_pcie_m2_check_remote_node(ctx->dev, 3, 0, "serial") || + !pwrseq_pcie_m2_check_remote_node(ctx->dev, 0, 0, "pcie")) + return 0; + + ctx->nb.notifier_call = pwrseq_pcie_m2_notify; + ret = bus_register_notifier(&pci_bus_type, &ctx->nb); + if (ret) + return dev_err_probe(ctx->dev, ret, + "Failed to register notifier for serdev\n"); + return 0; +} + +static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) +{ + struct pci_dev *pdev = NULL; + int ret; + + if (!pwrseq_pcie_m2_check_remote_node(ctx->dev, 3, 0, "serial") || + !pwrseq_pcie_m2_check_remote_node(ctx->dev, 0, 0, "pcie")) + return 0; + + struct device_node *pci_parent __free(device_node) = + of_graph_get_remote_node(dev_of_node(ctx->dev), 0, 0); + if (!pci_parent) + return 0; + + /* Create serdev for existing PCI devices if required */ + for_each_pci_dev(pdev) { + if (!pdev->dev.parent || pci_parent != pdev->dev.parent->of_node) + continue; + + if (!pci_match_id(pwrseq_m2_pci_ids, pdev)) + continue; + + ret = pwrseq_pcie_m2_create_serdev_one(ctx, pdev); + if (ret) { + dev_err_probe(ctx->dev, ret, + "Failed to create serdev for PCI device (%s)\n", + pci_name(pdev)); + pci_dev_put(pdev); + goto err_remove_serdev; } } return 0; + +err_remove_serdev: + pwrseq_pcie_m2_remove_serdev(ctx, NULL); + + return ret; } static int pwrseq_pcie_m2_probe(struct platform_device *pdev) @@ -432,16 +538,29 @@ static int pwrseq_pcie_m2_probe(struct platform_device *pdev) goto err_free_regulators; } + mutex_init(&ctx->list_lock); + INIT_LIST_HEAD(&ctx->pci_devices); + ctx->dev = dev; + + /* Create serdev for available PCI devices (if required) */ + ret = pwrseq_pcie_m2_create_serdev(ctx); + if (ret) + goto err_destroy_mutex; + /* * Register a notifier for creating protocol devices for * non-discoverable busses like UART. */ - ret = pwrseq_pcie_m2_register_notifier(ctx, dev); + ret = pwrseq_pcie_m2_register_notifier(ctx); if (ret) - goto err_free_regulators; + goto err_remove_serdev; return 0; +err_remove_serdev: + pwrseq_pcie_m2_remove_serdev(ctx, NULL); +err_destroy_mutex: + mutex_destroy(&ctx->list_lock); err_free_regulators: regulator_bulk_free(ctx->num_vregs, ctx->regs); @@ -453,7 +572,8 @@ static void pwrseq_pcie_m2_remove(struct platform_device *pdev) struct pwrseq_pcie_m2_ctx *ctx = platform_get_drvdata(pdev); bus_unregister_notifier(&pci_bus_type, &ctx->nb); - pwrseq_pcie_m2_remove_serdev(ctx); + pwrseq_pcie_m2_remove_serdev(ctx, NULL); + mutex_destroy(&ctx->list_lock); regulator_bulk_free(ctx->num_vregs, ctx->regs); } diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index c49e0e4d02f7..1b0239c59114 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -881,26 +881,22 @@ static bool cm_setup_timer(void) mutex_unlock(&cm_list_mtx); if (timer_req && cm_timer) { - ktime_t now, add; - /* * Set alarm with the polling interval (wakeup_ms) * The alarm time should be NOW + CM_RTC_SMALL or later. */ - if (wakeup_ms == UINT_MAX || - wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC) + if (wakeup_ms == UINT_MAX || wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC) wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC; pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); - now = ktime_get_boottime(); - add = ktime_set(wakeup_ms / MSEC_PER_SEC, - (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC); - alarm_start(cm_timer, ktime_add(now, add)); - cm_suspend_duration_ms = wakeup_ms; - return true; + /* + * The timer should always be queued as the timeout is at least + * two seconds out. Handle it correctly nevertheless. + */ + return alarm_start_timer(cm_timer, ktime_add_ms(0, wakeup_ms), true); } return false; } diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index a8dd02dff0a0..1006d183d508 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -1441,7 +1441,7 @@ static ssize_t cpumask_show(struct device *dev, } cpus_read_unlock(); - ret = cpumap_print_to_pagebuf(true, buf, cpu_mask); + ret = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_mask)); free_cpumask_var(cpu_mask); @@ -1770,7 +1770,8 @@ struct rapl_package *rapl_add_package_cpuslocked(int id, struct rapl_if_priv *pr topology_physical_package_id(id) : topology_logical_die_id(id); if ((int)(rp->id) < 0) { pr_err("topology_logical_(package/die)_id() returned a negative value"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto err_free_package; } rp->lead_cpu = id; if (!rapl_msrs_are_pkg_scope() && topology_max_dies_per_package() > 1) diff --git a/drivers/pps/generators/pps_gen-dummy.c b/drivers/pps/generators/pps_gen-dummy.c index 547fa7fe29f4..a4395543c4bb 100644 --- a/drivers/pps/generators/pps_gen-dummy.c +++ b/drivers/pps/generators/pps_gen-dummy.c @@ -39,11 +39,7 @@ static void pps_gen_ktimer_event(struct timer_list *unused) static int pps_gen_dummy_get_time(struct pps_gen_device *pps_gen, struct timespec64 *time) { - struct system_time_snapshot snap; - - ktime_get_snapshot(&snap); - *time = ktime_to_timespec64(snap.real); - + ktime_get_real_ts64(time); return 0; } diff --git a/drivers/pps/generators/pps_gen_tio.c b/drivers/pps/generators/pps_gen_tio.c index de00a85bfafa..9483d126ada0 100644 --- a/drivers/pps/generators/pps_gen_tio.c +++ b/drivers/pps/generators/pps_gen_tio.c @@ -189,11 +189,7 @@ static int pps_tio_gen_enable(struct pps_gen_device *pps_gen, bool enable) static int pps_tio_get_time(struct pps_gen_device *pps_gen, struct timespec64 *time) { - struct system_time_snapshot snap; - - ktime_get_snapshot(&snap); - *time = ktime_to_timespec64(snap.real); - + ktime_get_real_ts64(time); return 0; } diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index c61cf9edac48..dc23cd708cfe 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -317,8 +317,8 @@ typedef int (*ptp_crosststamp_fn)(struct ptp_clock_info *, static long ptp_sys_offset_precise(struct ptp_clock *ptp, void __user *arg, ptp_crosststamp_fn crosststamp_fn) { + struct system_device_crosststamp xtstamp = { .clock_id = CLOCK_REALTIME }; struct ptp_sys_offset_precise precise_offset; - struct system_device_crosststamp xtstamp; struct timespec64 ts; int err; @@ -333,7 +333,7 @@ static long ptp_sys_offset_precise(struct ptp_clock *ptp, void __user *arg, ts = ktime_to_timespec64(xtstamp.device); precise_offset.device.sec = ts.tv_sec; precise_offset.device.nsec = ts.tv_nsec; - ts = ktime_to_timespec64(xtstamp.sys_realtime); + ts = ktime_to_timespec64(xtstamp.sys_systime); precise_offset.sys_realtime.sec = ts.tv_sec; precise_offset.sys_realtime.nsec = ts.tv_nsec; ts = ktime_to_timespec64(xtstamp.sys_monoraw); @@ -386,15 +386,19 @@ static long ptp_sys_offset_extended(struct ptp_clock *ptp, void __user *arg, return err; /* Filter out disabled or unavailable clocks */ - if (sts.pre_ts.tv_sec < 0 || sts.post_ts.tv_sec < 0) + if (!sts.pre_sts.valid || !sts.post_sts.valid) return -EINVAL; - extoff->ts[i][0].sec = sts.pre_ts.tv_sec; - extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec; extoff->ts[i][1].sec = ts.tv_sec; extoff->ts[i][1].nsec = ts.tv_nsec; - extoff->ts[i][2].sec = sts.post_ts.tv_sec; - extoff->ts[i][2].nsec = sts.post_ts.tv_nsec; + + ts = ktime_to_timespec64(sts.pre_sts.systime); + extoff->ts[i][0].sec = ts.tv_sec; + extoff->ts[i][0].nsec = ts.tv_nsec; + + ts = ktime_to_timespec64(sts.post_sts.systime); + extoff->ts[i][2].sec = ts.tv_sec; + extoff->ts[i][2].nsec = ts.tv_nsec; } return copy_to_user(arg, extoff, sizeof(*extoff)) ? -EFAULT : 0; diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index beacc2ffb166..8231c2d7785e 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -1491,11 +1491,8 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, } ptp_read_system_postts(sts); - if (sts && bp->ts_window_adjust) { - s64 ns = timespec64_to_ns(&sts->post_ts); - - sts->post_ts = ns_to_timespec64(ns - bp->ts_window_adjust); - } + if (sts && bp->ts_window_adjust) + sts->post_sts.systime -= bp->ts_window_adjust; time_ns = ioread32(&bp->reg->time_ns); time_sec = ioread32(&bp->reg->time_sec); @@ -2479,8 +2476,13 @@ ptp_ocp_ts_enable(void *priv, u32 req, bool enable) iowrite32(1, ®->intr_mask); iowrite32(1, ®->intr); } else { + int irq_vec = pci_irq_vector(bp->pdev, ext->irq_vec); + iowrite32(0, ®->intr_mask); iowrite32(0, ®->enable); + ioread32(®->intr_mask); + if (irq_vec > 0) + synchronize_irq(irq_vec); } return 0; @@ -4595,8 +4597,8 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) struct timespec64 sys_ts; s64 pre_ns, post_ns, ns; - pre_ns = timespec64_to_ns(&sts.pre_ts); - post_ns = timespec64_to_ns(&sts.post_ts); + pre_ns = ktime_to_ns(sts.pre_sts.systime); + post_ns = ktime_to_ns(sts.post_sts.systime); ns = (pre_ns + post_ns) / 2; ns += (s64)bp->utc_tai_offset * NSEC_PER_SEC; sys_ts = ns_to_timespec64(ns); @@ -4867,6 +4869,22 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_detach_sysfs(bp); ptp_ocp_attr_group_del(bp); timer_delete_sync(&bp->watchdog); + /* Disable interrupts on all timestampers */ + if (bp->ts0) + ptp_ocp_ts_enable(bp->ts0, 0, false); + if (bp->ts1) + ptp_ocp_ts_enable(bp->ts1, 0, false); + if (bp->ts2) + ptp_ocp_ts_enable(bp->ts2, 0, false); + if (bp->ts3) + ptp_ocp_ts_enable(bp->ts3, 0, false); + if (bp->ts4) + ptp_ocp_ts_enable(bp->ts4, 0, false); + if (bp->pps) + ptp_ocp_ts_enable(bp->pps, ~0, false); + if (bp->ptp) + ptp_clock_unregister(bp->ptp); + kfree(bp->ptp_info.pin_config); ptp_ocp_unregister_ext(bp->ts0); ptp_ocp_unregister_ext(bp->ts1); ptp_ocp_unregister_ext(bp->ts2); @@ -4884,9 +4902,6 @@ ptp_ocp_detach(struct ptp_ocp *bp) clk_hw_unregister_fixed_rate(bp->i2c_clk); if (bp->n_irqs) pci_free_irq_vectors(bp->pdev); - if (bp->ptp) - ptp_clock_unregister(bp->ptp); - kfree(bp->ptp_info.pin_config); device_unregister(&bp->dev); } diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c index 915a4f6defc9..84cb527f59cc 100644 --- a/drivers/ptp/ptp_vclock.c +++ b/drivers/ptp/ptp_vclock.c @@ -19,6 +19,8 @@ static DEFINE_SPINLOCK(vclock_hash_lock); static DEFINE_READ_MOSTLY_HASHTABLE(vclock_hash, 8); +DEFINE_STATIC_SRCU(vclock_srcu); + static void ptp_vclock_hash_add(struct ptp_vclock *vclock) { spin_lock(&vclock_hash_lock); @@ -37,7 +39,7 @@ static void ptp_vclock_hash_del(struct ptp_vclock *vclock) spin_unlock(&vclock_hash_lock); - synchronize_rcu(); + synchronize_srcu(&vclock_srcu); } static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) @@ -276,14 +278,16 @@ ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index) { unsigned int hash = vclock_index % HASH_SIZE(vclock_hash); struct ptp_vclock *vclock; - u64 ns; u64 vclock_ns = 0; + int srcu_idx; + u64 ns; ns = ktime_to_ns(*hwtstamp); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&vclock_srcu); - hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) { + hlist_for_each_entry_srcu(vclock, &vclock_hash[hash], vclock_hash_node, + srcu_read_lock_held(&vclock_srcu)) { if (vclock->clock->index != vclock_index) continue; @@ -294,7 +298,7 @@ ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index) break; } - rcu_read_unlock(); + srcu_read_unlock(&vclock_srcu, srcu_idx); return ns_to_ktime(vclock_ns); } diff --git a/drivers/ptp/ptp_vmclock.c b/drivers/ptp/ptp_vmclock.c index 8b630eb916b5..eebdcd5ebc08 100644 --- a/drivers/ptp/ptp_vmclock.c +++ b/drivers/ptp/ptp_vmclock.c @@ -101,7 +101,6 @@ static int vmclock_get_crosststamp(struct vmclock_state *st, struct timespec64 *tspec) { ktime_t deadline = ktime_add(ktime_get(), VMCLOCK_MAX_WAIT); - struct system_time_snapshot systime_snapshot; uint64_t cycle, delta, seq, frac_sec; #ifdef CONFIG_X86 @@ -132,17 +131,19 @@ static int vmclock_get_crosststamp(struct vmclock_state *st, * will be derived from the *same* counter value. * * If the system isn't using the same counter, then the value - * from ktime_get_snapshot() will still be used as pre_ts, and - * ptp_read_system_postts() is called to populate postts after - * calling get_cycles(). - * - * The conversion to timespec64 happens further down, outside - * the seq_count loop. + * from ptp_read_system_prets() will still be used as pre_ts, + * and ptp_read_system_postts() is called to populate postts + * after calling get_cycles(). */ if (sts) { - ktime_get_snapshot(&systime_snapshot); - if (systime_snapshot.cs_id == st->cs_id) { - cycle = systime_snapshot.cycles; + ptp_read_system_prets(sts); + if (sts->pre_sts.cs_id == st->cs_id) { + cycle = sts->pre_sts.cycles; + sts->post_sts = sts->pre_sts; + } else if (sts->pre_sts.hw_csid == st->cs_id && + sts->pre_sts.hw_cycles) { + cycle = sts->pre_sts.hw_cycles; + sts->post_sts = sts->pre_sts; } else { cycle = get_cycles(); ptp_read_system_postts(sts); @@ -180,12 +181,6 @@ static int vmclock_get_crosststamp(struct vmclock_state *st, system_counter->cs_id = st->cs_id; } - if (sts) { - sts->pre_ts = ktime_to_timespec64(systime_snapshot.real); - if (systime_snapshot.cs_id == st->cs_id) - sts->post_ts = sts->pre_ts; - } - return 0; } @@ -272,7 +267,7 @@ static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp, if (ret == -ENODEV) { struct system_time_snapshot systime_snapshot; - ktime_get_snapshot(&systime_snapshot); + ktime_get_snapshot_id(CLOCK_REALTIME, &systime_snapshot); if (systime_snapshot.cs_id == CSID_X86_TSC || systime_snapshot.cs_id == CSID_X86_KVM_CLK) { diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6f3147518376..e8886a9b64d9 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -347,6 +347,18 @@ config PWM_INTEL_LGM To compile this driver as a module, choose M here: the module will be called pwm-intel-lgm. +config PWM_IPQ + tristate "IPQ PWM support" + depends on ARCH_QCOM || COMPILE_TEST + depends on HAVE_CLK && HAS_IOMEM + help + Generic PWM framework driver for IPQ PWM block which supports + 4 pwm channels. Each of the these channels can be configured + independent of each other. + + To compile this driver as a module, choose M here: the module + will be called pwm-ipq. + config PWM_IQS620A tristate "Azoteq IQS620A PWM support" depends on MFD_IQS62X || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 0dc0d2b69025..5630a521a7cf 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o +obj-$(CONFIG_PWM_IPQ) += pwm-ipq.o obj-$(CONFIG_PWM_IQS620A) += pwm-iqs620a.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 3d30aeab507e..a765ef279b51 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -443,7 +443,7 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) err = clk_prepare_enable(tcbpwmc->slow_clk); if (err) - goto err_disable_clk;; + goto err_disable_clk; err = clk_rate_exclusive_get(tcbpwmc->clk); if (err) diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c index 86b72db58741..8ba7ef2c27d0 100644 --- a/drivers/pwm/pwm-dwc.c +++ b/drivers/pwm/pwm-dwc.c @@ -147,8 +147,8 @@ static int dwc_pwm_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume); static const struct pci_device_id dwc_pwm_id_table[] = { - { PCI_VDEVICE(INTEL, 0x4bb7), (kernel_ulong_t)&ehl_pwm_info }, - { } /* Terminating Entry */ + { PCI_VDEVICE(INTEL, 0x4bb7), .driver_data = (kernel_ulong_t)&ehl_pwm_info }, + { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(pci, dwc_pwm_id_table); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 3d34cdc4a3a5..c8b801fcb525 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -200,7 +200,7 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - unsigned long period_cycles, duty_cycles, prescale, period_us, tmp; + unsigned long period_cycles, duty_cycles, prescale, period_us; struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); unsigned long long c; unsigned long long clkrate; @@ -208,6 +208,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, int val; int ret; u32 cr; + u64 tmp; clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk); c = clkrate * state->period; @@ -249,6 +250,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, val = readl(imx->mmio_base + MX3_PWMPR); val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val; cr = readl(imx->mmio_base + MX3_PWMCR); + + /* + * tmp stores period in nanoseconds. Result fits in u64 since + * val <= 0xfffe and prescaler in [1, 0x1000]. + */ tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr); tmp = DIV_ROUND_UP_ULL(tmp, clkrate); period_us = DIV_ROUND_UP_ULL(tmp, 1000); diff --git a/drivers/pwm/pwm-ipq.c b/drivers/pwm/pwm-ipq.c new file mode 100644 index 000000000000..c53373948136 --- /dev/null +++ b/drivers/pwm/pwm-ipq.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. + * + * Limitations: + * - The PWM controller has no publicly available datasheet. + * - Each of the four channels is programmed via two 32-bit registers + * (REG0 and REG1 at 8-byte stride). + * - Period and duty-cycle reconfiguration is fully atomic: new divider, + * pre-divider, and high-duration values are latched by setting the + * UPDATE bit (bit 30 in REG1). The hardware applies the new settings + * at the beginning of the next period without disabling the output, + * so the currently running period is always completed. + * - On disable (clearing the ENABLE bit 31 in REG1), the hardware + * finishes the current period before stopping the output. The pin + * is then driven to the inactive (low) level. + * - Upon disabling, the hardware resets the pre-divider (PRE_DIV) and divider + * fields (PWM_DIV) in REG0 and REG1 to 0x0000 and 0x0001 respectively. + * - Only normal polarity is supported. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/math64.h> +#include <linux/of_device.h> +#include <linux/bitfield.h> +#include <linux/units.h> + +/* The frequency range supported is 1 Hz to 100 Mhz (clock rate) */ +#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC) +#define IPQ_PWM_MIN_PERIOD_NS 10 + +/* + * Two 32-bit registers for each PWM: REG0, and REG1. + * Base offset for PWM #i is at 8 * #i. + */ +#define IPQ_PWM_REG0 0 +#define IPQ_PWM_REG0_PWM_DIV GENMASK(15, 0) +#define IPQ_PWM_REG0_HI_DURATION GENMASK(31, 16) + +#define IPQ_PWM_REG1 4 +#define IPQ_PWM_REG1_PRE_DIV GENMASK(15, 0) +/* + * Enable bit is set to enable output toggling in pwm device. + * Update bit is set to trigger the change and is unset automatically + * to reflect the changed divider and high duration values in register. + */ +#define IPQ_PWM_REG1_UPDATE BIT(30) +#define IPQ_PWM_REG1_ENABLE BIT(31) + +/* + * The max value specified for each field is based on the number of bits + * in the pwm control register for that field (16-bit) + */ +#define IPQ_PWM_MAX_DIV FIELD_MAX(IPQ_PWM_REG0_PWM_DIV) + +struct ipq_pwm_chip { + void __iomem *mem; + unsigned long clk_rate; +}; + +static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); + unsigned int off = 8 * pwm->hwpwm + reg; + + return readl(ipq_chip->mem + off); +} + +static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg, + unsigned int val) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); + unsigned int off = 8 * pwm->hwpwm + reg; + + writel(val, ipq_chip->mem + off); +} + +static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); + unsigned int pre_div, pwm_div; + u64 period_ns, duty_ns; + unsigned long val = 0; + unsigned long hi_dur; + + if (!state->enabled) { + /* clear IPQ_PWM_REG1_ENABLE */ + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, IPQ_PWM_REG1_UPDATE); + return 0; + } + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + /* + * Check the upper and lower bounds for the period as per + * hardware limits + */ + if (state->period < IPQ_PWM_MIN_PERIOD_NS) + return -ERANGE; + period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS); + duty_ns = min(state->duty_cycle, period_ns); + + /* + * Pick the maximal value for PWM_DIV that still allows a + * 100% relative duty cycle. This allows a fine grained + * selection of duty cycles. + */ + pwm_div = IPQ_PWM_MAX_DIV - 1; + + /* + * although mul_u64_u64_div_u64 returns a u64, in practice it + * won't overflow due to above constraints. Take the max period + * of 10^9 (NSEC_PER_SEC) and the pwm_div + 1 (IPQ_PWM_MAX_DIV) + * 10^9 * 10^8 + * ------------- => which fits well into a 32-bit unsigned int. + * 10^9 * 65,535 + */ + pre_div = mul_u64_u64_div_u64(period_ns, ipq_chip->clk_rate, + (u64)NSEC_PER_SEC * (pwm_div + 1)); + + if (!pre_div) + return -ERANGE; + + pre_div -= 1; + + if (pre_div > IPQ_PWM_MAX_DIV) + pre_div = IPQ_PWM_MAX_DIV; + + /* pwm duty = HI_DUR * (PRE_DIV + 1) / clk_rate */ + hi_dur = mul_u64_u64_div_u64(duty_ns, ipq_chip->clk_rate, + (u64)NSEC_PER_SEC * (pre_div + 1)); + + val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) | + FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div); + ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val); + + val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div); + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); + + /* PWM enable toggle needs a separate write to REG1 */ + val |= IPQ_PWM_REG1_UPDATE | IPQ_PWM_REG1_ENABLE; + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); + + return 0; +} + +static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); + unsigned int pre_div, pwm_div, hi_dur; + u64 effective_div, hi_div; + u32 reg0, reg1; + + reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1); + state->enabled = reg1 & IPQ_PWM_REG1_ENABLE; + + if (!state->enabled) + return 0; + + reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0); + + state->polarity = PWM_POLARITY_NORMAL; + + pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0); + hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0); + pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1); + + effective_div = (u64)(pwm_div + 1) * (pre_div + 1); + + /* + * effective_div <= 0x100000000, so the multiplication doesn't overflow. + */ + state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, + ipq_chip->clk_rate); + + hi_div = hi_dur * (pre_div + 1); + state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, + ipq_chip->clk_rate); + + /* + * ensure a valid config is passed back to PWM core in case duty_cycle + * is > period (>100%) + */ + state->duty_cycle = min(state->duty_cycle, state->period); + + return 0; +} + +static const struct pwm_ops ipq_pwm_ops = { + .apply = ipq_pwm_apply, + .get_state = ipq_pwm_get_state, +}; + +static int ipq_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ipq_pwm_chip *pwm; + struct pwm_chip *chip; + struct clk *clk; + int ret; + + chip = devm_pwmchip_alloc(dev, 4, sizeof(*pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + pwm = ipq_pwm_from_chip(chip); + + pwm->mem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pwm->mem)) + return dev_err_probe(dev, PTR_ERR(pwm->mem), + "Failed to acquire resource\n"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), + "Failed to get clock\n"); + + ret = devm_clk_rate_exclusive_get(dev, clk); + if (ret) + return dev_err_probe(dev, ret, "Failed to lock clock rate\n"); + + pwm->clk_rate = clk_get_rate(clk); + if (!pwm->clk_rate) + return dev_err_probe(dev, -EINVAL, "Failed due to clock rate being zero\n"); + + chip->ops = &ipq_pwm_ops; + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); + + return 0; +} + +static const struct of_device_id pwm_ipq_dt_match[] = { + { .compatible = "qcom,ipq6018-pwm", }, + {} +}; +MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match); + +static struct platform_driver ipq_pwm_driver = { + .driver = { + .name = "ipq-pwm", + .of_match_table = pwm_ipq_dt_match, + }, + .probe = ipq_pwm_probe, +}; + +module_platform_driver(ipq_pwm_driver); + +MODULE_DESCRIPTION("Qualcomm IPQ PWM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index ae25d9321d75..3a0fd6593520 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -48,15 +48,15 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev) } static const struct pci_device_id pwm_lpss_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bxt_info}, - { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info}, - { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info}, - { PCI_VDEVICE(INTEL, 0x11a5), (unsigned long)&pwm_lpss_tng_info}, - { PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bxt_info}, - { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info}, - { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info}, - { PCI_VDEVICE(INTEL, 0x31c8), (unsigned long)&pwm_lpss_bxt_info}, - { PCI_VDEVICE(INTEL, 0x5ac8), (unsigned long)&pwm_lpss_bxt_info}, + { PCI_VDEVICE(INTEL, 0x0ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, + { PCI_VDEVICE(INTEL, 0x0f08), .driver_data = (unsigned long)&pwm_lpss_byt_info }, + { PCI_VDEVICE(INTEL, 0x0f09), .driver_data = (unsigned long)&pwm_lpss_byt_info }, + { PCI_VDEVICE(INTEL, 0x11a5), .driver_data = (unsigned long)&pwm_lpss_tng_info }, + { PCI_VDEVICE(INTEL, 0x1ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, + { PCI_VDEVICE(INTEL, 0x2288), .driver_data = (unsigned long)&pwm_lpss_bsw_info }, + { PCI_VDEVICE(INTEL, 0x2289), .driver_data = (unsigned long)&pwm_lpss_bsw_info }, + { PCI_VDEVICE(INTEL, 0x31c8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, + { PCI_VDEVICE(INTEL, 0x5ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, { }, }; MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 9d206303404a..992137a27750 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -23,6 +23,8 @@ /* PWM registers and bits definitions */ #define PWMCON 0x00 #define PWMCON_CLKDIV GENMASK(2, 0) +#define PWMCON_CLKSEL BIT(3) +#define PWMCON_OLD_PWM_MODE BIT(15) #define PWMHDUR 0x04 #define PWMLDUR 0x08 #define PWMGDUR 0x0c @@ -38,6 +40,7 @@ struct pwm_mediatek_of_data { unsigned int num_pwms; + bool clksel_fixup; bool pwm45_fixup; u16 pwm_ck_26m_sel_reg; unsigned int chanreg_base; @@ -337,6 +340,7 @@ static int pwm_mediatek_write_waveform(struct pwm_chip *chip, if (wfhw->enable) { u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; + u32 con_val = PWMCON_OLD_PWM_MODE | wfhw->con; if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { /* @@ -364,7 +368,11 @@ static int pwm_mediatek_write_waveform(struct pwm_chip *chip, if (pc->soc->pwm_ck_26m_sel_reg) writel(0, pc->regs + pc->soc->pwm_ck_26m_sel_reg); - pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | wfhw->con); + /* Set BIT(3) to disable clock division */ + if (pc->soc->clksel_fixup) + con_val |= PWMCON_CLKSEL; + + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, con_val); pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, wfhw->width); pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, wfhw->thres); } else { @@ -496,6 +504,7 @@ static int pwm_mediatek_probe(struct platform_device *pdev) static const struct pwm_mediatek_of_data mt2712_pwm_data = { .num_pwms = 8, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -503,6 +512,7 @@ static const struct pwm_mediatek_of_data mt2712_pwm_data = { static const struct pwm_mediatek_of_data mt6795_pwm_data = { .num_pwms = 7, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -510,6 +520,7 @@ static const struct pwm_mediatek_of_data mt6795_pwm_data = { static const struct pwm_mediatek_of_data mt7622_pwm_data = { .num_pwms = 6, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -518,6 +529,7 @@ static const struct pwm_mediatek_of_data mt7622_pwm_data = { static const struct pwm_mediatek_of_data mt7623_pwm_data = { .num_pwms = 5, + .clksel_fixup = false, .pwm45_fixup = true, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -525,13 +537,15 @@ static const struct pwm_mediatek_of_data mt7623_pwm_data = { static const struct pwm_mediatek_of_data mt7628_pwm_data = { .num_pwms = 4, - .pwm45_fixup = true, + .clksel_fixup = true, + .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, }; static const struct pwm_mediatek_of_data mt7629_pwm_data = { .num_pwms = 1, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -539,6 +553,7 @@ static const struct pwm_mediatek_of_data mt7629_pwm_data = { static const struct pwm_mediatek_of_data mt7981_pwm_data = { .num_pwms = 3, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x80, @@ -547,6 +562,7 @@ static const struct pwm_mediatek_of_data mt7981_pwm_data = { static const struct pwm_mediatek_of_data mt7986_pwm_data = { .num_pwms = 2, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -555,6 +571,7 @@ static const struct pwm_mediatek_of_data mt7986_pwm_data = { static const struct pwm_mediatek_of_data mt7988_pwm_data = { .num_pwms = 8, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x80, .chanreg_width = 0x40, @@ -562,6 +579,7 @@ static const struct pwm_mediatek_of_data mt7988_pwm_data = { static const struct pwm_mediatek_of_data mt8183_pwm_data = { .num_pwms = 4, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -570,6 +588,7 @@ static const struct pwm_mediatek_of_data mt8183_pwm_data = { static const struct pwm_mediatek_of_data mt8365_pwm_data = { .num_pwms = 3, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -578,6 +597,7 @@ static const struct pwm_mediatek_of_data mt8365_pwm_data = { static const struct pwm_mediatek_of_data mt8516_pwm_data = { .num_pwms = 5, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -586,6 +606,7 @@ static const struct pwm_mediatek_of_data mt8516_pwm_data = { static const struct pwm_mediatek_of_data mt6991_pwm_data = { .num_pwms = 4, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL_V3, .chanreg_base = 0x100, diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 107bebec3546..a02255a64ea8 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -538,7 +538,7 @@ static int __maybe_unused pca9685_pwm_runtime_resume(struct device *dev) } static const struct i2c_device_id pca9685_id[] = { - { "pca9685" }, + { .name = "pca9685" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9685_id); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 0f5bdb0e395e..80d2fa10919f 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -161,6 +161,7 @@ static int pwm_probe(struct platform_device *pdev) const struct platform_device_id *id = platform_get_device_id(pdev); struct pwm_chip *chip; struct pxa_pwm_chip *pc; + struct clk *bus_clk; struct device *dev = &pdev->dev; struct reset_control *rst; int ret = 0; @@ -177,7 +178,12 @@ static int pwm_probe(struct platform_device *pdev) return PTR_ERR(chip); pc = to_pxa_pwm_chip(chip); - pc->clk = devm_clk_get(dev, NULL); + bus_clk = devm_clk_get_optional_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) + return dev_err_probe(dev, PTR_ERR(bus_clk), "Failed to get bus clock\n"); + + /* Get named func clk if bus clock is valid */ + pc->clk = devm_clk_get(dev, bus_clk ? "func" : NULL); if (IS_ERR(pc->clk)) return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n"); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 935257a890b0..c708e4a7ad70 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -193,22 +193,6 @@ out: return ret; } -/* - * This should be moved to lib/math/div64.c. Currently there are some changes - * pending to mul_u64_u64_div_u64. Uwe will care for that when the dust settles. - */ -static u64 stm32_pwm_mul_u64_u64_div_u64_roundup(u64 a, u64 b, u64 c) -{ - u64 res = mul_u64_u64_div_u64(a, b, c); - /* Those multiplications might overflow but it doesn't matter */ - u64 rem = a * b - c * res; - - if (rem) - res += 1; - - return res; -} - static int stm32_pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, const void *_wfhw, @@ -223,16 +207,15 @@ static int stm32_pwm_round_waveform_fromhw(struct pwm_chip *chip, u64 ccr_ns; /* The result doesn't overflow for rate >= 15259 */ - wf->period_length_ns = stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1), - NSEC_PER_SEC, rate); + wf->period_length_ns = mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1), + NSEC_PER_SEC, rate); - ccr_ns = stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * wfhw->ccr, - NSEC_PER_SEC, rate); + ccr_ns = mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * wfhw->ccr, NSEC_PER_SEC, rate); if (wfhw->ccer & TIM_CCER_CCxP(ch + 1)) { wf->duty_length_ns = - stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1 - wfhw->ccr), - NSEC_PER_SEC, rate); + mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1 - wfhw->ccr), + NSEC_PER_SEC, rate); wf->duty_offset_ns = ccr_ns; } else { diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs index ddd44a5ce497..3e3fa51ccef9 100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@ -67,16 +67,10 @@ fn ns_to_cycles(ns: u64, rate_hz: u64) -> u64 { ns.saturating_mul(rate_hz) / NSEC_PER_SEC_U64 } -fn cycles_to_ns(cycles: u64, rate_hz: u64) -> u64 { +fn cycles_to_ns(cycles: u32, rate_hz: u64) -> u64 { const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64; - // TODO: Replace with a kernel helper like `mul_u64_u64_div_u64_roundup` - // once available in Rust. - let numerator = cycles - .saturating_mul(NSEC_PER_SEC_U64) - .saturating_add(rate_hz - 1); - - numerator / rate_hz + (u64::from(cycles) * NSEC_PER_SEC_U64).div_ceil(rate_hz) } /// Hardware-specific waveform representation for TH1520. @@ -92,7 +86,7 @@ struct Th1520WfHw { #[pin_data(PinnedDrop)] struct Th1520PwmDriverData { #[pin] - iomem: devres::Devres<IoMem<TH1520_PWM_REG_SIZE>>, + iomem: devres::Devres<IoMem<'static, TH1520_PWM_REG_SIZE>>, clk: Clk, } @@ -192,15 +186,15 @@ impl pwm::PwmOps for Th1520PwmDriverData { return Ok(()); } - wf.period_length_ns = cycles_to_ns(u64::from(wfhw.period_cycles), rate_hz); + wf.period_length_ns = cycles_to_ns(wfhw.period_cycles, rate_hz); - let duty_cycles = u64::from(wfhw.duty_cycles); + let duty_cycles = wfhw.duty_cycles; if (wfhw.ctrl_val & TH1520_PWM_FPOUT) != 0 { wf.duty_length_ns = cycles_to_ns(duty_cycles, rate_hz); wf.duty_offset_ns = 0; } else { - let period_cycles = u64::from(wfhw.period_cycles); + let period_cycles = wfhw.period_cycles; let original_duty_cycles = period_cycles.saturating_sub(duty_cycles); // For an inverted signal, `duty_length_ns` is the high time (period - low_time). @@ -316,12 +310,13 @@ kernel::of_device_table!( impl platform::Driver for Th1520PwmPlatformDriver { type IdInfo = (); + type Data<'bound> = Self; const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); - fn probe( - pdev: &platform::Device<Core>, - _id_info: Option<&Self::IdInfo>, - ) -> impl PinInit<Self, Error> { + fn probe<'bound>( + pdev: &'bound platform::Device<Core<'_>>, + _id_info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit<Self, Error> + 'bound { let dev = pdev.as_ref(); let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; @@ -351,7 +346,7 @@ impl platform::Driver for Th1520PwmPlatformDriver { dev, TH1520_MAX_PWM_NUM, try_pin_init!(Th1520PwmDriverData { - iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>(), + iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>()?.into_devres(), clk <- clk, }), )?; diff --git a/drivers/ras/amd/atl/Kconfig b/drivers/ras/amd/atl/Kconfig index 6e03942cd7da..44c2fd7febc5 100644 --- a/drivers/ras/amd/atl/Kconfig +++ b/drivers/ras/amd/atl/Kconfig @@ -12,7 +12,6 @@ config AMD_ATL depends on AMD_NB && X86_64 && RAS depends on AMD_NODE depends on MEMORY_FAILURE - default N help This library includes support for implementation-specific address translation procedures needed for various error diff --git a/drivers/ras/amd/fmpm.c b/drivers/ras/amd/fmpm.c index 34ef75af31cb..4ccaaf7b70bf 100644 --- a/drivers/ras/amd/fmpm.c +++ b/drivers/ras/amd/fmpm.c @@ -52,6 +52,7 @@ #include <acpi/apei.h> #include <asm/cpu_device_id.h> +#include <asm/cpuid/api.h> #include <asm/mce.h> #include "../debugfs.h" diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index e6c436955e25..969554725a67 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -371,12 +371,10 @@ static int pm8607_regulator_probe(struct platform_device *pdev) static const struct platform_device_id pm8607_regulator_driver_ids[] = { { .name = "88pm860x-regulator", - .driver_data = 0, }, { .name = "88pm860x-preg", - .driver_data = 0, }, - { }, + { } }; MODULE_DEVICE_TABLE(platform, pm8607_regulator_driver_ids); diff --git a/drivers/regulator/88pm886-regulator.c b/drivers/regulator/88pm886-regulator.c index a38bd4f312b7..7328cd1cf265 100644 --- a/drivers/regulator/88pm886-regulator.c +++ b/drivers/regulator/88pm886-regulator.c @@ -373,7 +373,7 @@ static int pm886_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id pm886_regulator_id_table[] = { - { "88pm886-regulator", }, + { .name = "88pm886-regulator" }, { } }; MODULE_DEVICE_TABLE(platform, pm886_regulator_id_table); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 78076ac6eac4..a54a549196fe 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -977,6 +977,7 @@ config REGULATOR_MT6363 tristate "MT6363 SPMI PMIC regulator driver" depends on SPMI select REGMAP_SPMI + select IRQ_DOMAIN help Say Y here to enable support for regulators found in the MediaTek MT6363 SPMI PMIC. @@ -1055,13 +1056,6 @@ config REGULATOR_PF9453 help Say y here to support the NXP PF9453 PMIC regulator driver. -config REGULATOR_PCAP - tristate "Motorola PCAP2 regulator driver" - depends on EZX_PCAP - help - This driver provides support for the voltage regulators of the - PCAP2 PMIC. - config REGULATOR_PF0900 tristate "NXP PF0900/PF0901/PF09XX regulator driver" depends on I2C @@ -1490,6 +1484,14 @@ config REGULATOR_SC2731 This driver provides support for the voltage regulators on the SC2731 PMIC. +config REGULATOR_SGM3804 + tristate "SGMicro SGM3804 voltage regulator" + depends on I2C && OF + depends on GPIOLIB + select REGMAP_I2C + help + This driver supports SGMicro SGM3804 dual-output voltage regulator. + config REGULATOR_SKY81452 tristate "Skyworks Solutions SKY81452 voltage regulator" depends on MFD_SKY81452 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 35639f3115fd..134eee274dbf 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -142,7 +142,6 @@ obj-$(CONFIG_REGULATOR_PV88090) += pv88090-regulator.o obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o -obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_RAA215300) += raa215300.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) += rpi-panel-attiny-regulator.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2) += rpi-panel-v2-regulator.o @@ -172,6 +171,7 @@ obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o +obj-$(CONFIG_REGULATOR_SGM3804) += sgm3804-regulator.o obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o obj-$(CONFIG_REGULATOR_SPACEMIT_P1) += spacemit-p1.o diff --git a/drivers/regulator/bd71815-regulator.c b/drivers/regulator/bd71815-regulator.c index 668714f35464..4c2b20d1b284 100644 --- a/drivers/regulator/bd71815-regulator.c +++ b/drivers/regulator/bd71815-regulator.c @@ -607,8 +607,8 @@ static int bd7181x_probe(struct platform_device *pdev) } static const struct platform_device_id bd7181x_pmic_id[] = { - { "bd71815-pmic", ROHM_CHIP_TYPE_BD71815 }, - { }, + { .name = "bd71815-pmic", .driver_data = ROHM_CHIP_TYPE_BD71815 }, + { } }; MODULE_DEVICE_TABLE(platform, bd7181x_pmic_id); diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c index 473beb4399d9..bd61caa8284a 100644 --- a/drivers/regulator/bd71828-regulator.c +++ b/drivers/regulator/bd71828-regulator.c @@ -1691,9 +1691,9 @@ static int bd71828_probe(struct platform_device *pdev) } static const struct platform_device_id bd71828_pmic_id[] = { - { "bd71828-pmic", ROHM_CHIP_TYPE_BD71828 }, - { "bd72720-pmic", ROHM_CHIP_TYPE_BD72720 }, - { }, + { .name = "bd71828-pmic", .driver_data = ROHM_CHIP_TYPE_BD71828 }, + { .name = "bd72720-pmic", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { } }; MODULE_DEVICE_TABLE(platform, bd71828_pmic_id); diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 1b5997c8482e..9cc29b9409d0 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -1816,9 +1816,9 @@ static int bd718xx_probe(struct platform_device *pdev) } static const struct platform_device_id bd718x7_pmic_id[] = { - { "bd71837-pmic", ROHM_CHIP_TYPE_BD71837 }, - { "bd71847-pmic", ROHM_CHIP_TYPE_BD71847 }, - { }, + { .name = "bd71837-pmic", .driver_data = ROHM_CHIP_TYPE_BD71837 }, + { .name = "bd71847-pmic", .driver_data = ROHM_CHIP_TYPE_BD71847 }, + { } }; MODULE_DEVICE_TABLE(platform, bd718x7_pmic_id); diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index f4de24a281b1..5bf02dc0d20e 100644 --- a/drivers/regulator/bd9571mwv-regulator.c +++ b/drivers/regulator/bd9571mwv-regulator.c @@ -344,8 +344,8 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id bd9571mwv_regulator_id_table[] = { - { "bd9571mwv-regulator", ROHM_CHIP_TYPE_BD9571 }, - { "bd9574mwf-regulator", ROHM_CHIP_TYPE_BD9574 }, + { .name = "bd9571mwv-regulator", .driver_data = ROHM_CHIP_TYPE_BD9571 }, + { .name = "bd9574mwf-regulator", .driver_data = ROHM_CHIP_TYPE_BD9574 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, bd9571mwv_regulator_id_table); diff --git a/drivers/regulator/bd9576-regulator.c b/drivers/regulator/bd9576-regulator.c index bf5f9c3f2c97..fcdaaa56e356 100644 --- a/drivers/regulator/bd9576-regulator.c +++ b/drivers/regulator/bd9576-regulator.c @@ -1117,9 +1117,9 @@ static int bd957x_probe(struct platform_device *pdev) } static const struct platform_device_id bd957x_pmic_id[] = { - { "bd9573-regulator", ROHM_CHIP_TYPE_BD9573 }, - { "bd9576-regulator", ROHM_CHIP_TYPE_BD9576 }, - { }, + { .name = "bd9573-regulator", .driver_data = ROHM_CHIP_TYPE_BD9573 }, + { .name = "bd9576-regulator", .driver_data = ROHM_CHIP_TYPE_BD9576 }, + { } }; MODULE_DEVICE_TABLE(platform, bd957x_pmic_id); diff --git a/drivers/regulator/bd96801-regulator.c b/drivers/regulator/bd96801-regulator.c index 129b20c33bad..308279b31fd3 100644 --- a/drivers/regulator/bd96801-regulator.c +++ b/drivers/regulator/bd96801-regulator.c @@ -1329,11 +1329,11 @@ static int bd96801_probe(struct platform_device *pdev) } static const struct platform_device_id bd96801_pmic_id[] = { - { "bd96801-regulator", (kernel_ulong_t)&bd96801_data }, - { "bd96802-regulator", (kernel_ulong_t)&bd96802_data }, - { "bd96805-regulator", (kernel_ulong_t)&bd96805_data }, - { "bd96806-regulator", (kernel_ulong_t)&bd96806_data }, - { }, + { .name = "bd96801-regulator", .driver_data = (kernel_ulong_t)&bd96801_data }, + { .name = "bd96802-regulator", .driver_data = (kernel_ulong_t)&bd96802_data }, + { .name = "bd96805-regulator", .driver_data = (kernel_ulong_t)&bd96805_data }, + { .name = "bd96806-regulator", .driver_data = (kernel_ulong_t)&bd96806_data }, + { } }; MODULE_DEVICE_TABLE(platform, bd96801_pmic_id); diff --git a/drivers/regulator/bq257xx-regulator.c b/drivers/regulator/bq257xx-regulator.c index 09c466052c04..577b277efd7f 100644 --- a/drivers/regulator/bq257xx-regulator.c +++ b/drivers/regulator/bq257xx-regulator.c @@ -143,7 +143,6 @@ static int bq257xx_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct bq257xx_reg_data *pdata; - struct device_node *np = dev->of_node; struct regulator_config cfg = {}; device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); @@ -159,7 +158,6 @@ static int bq257xx_regulator_probe(struct platform_device *pdev) cfg.dev = &pdev->dev; cfg.driver_data = pdata; - cfg.of_node = np; cfg.regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!cfg.regmap) return -ENODEV; diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c index cd06030c3587..3373c4fdbddf 100644 --- a/drivers/regulator/hi6421-regulator.c +++ b/drivers/regulator/hi6421-regulator.c @@ -571,7 +571,7 @@ static int hi6421_regulator_probe(struct platform_device *pdev) static const struct platform_device_id hi6421_regulator_table[] = { { .name = "hi6421-regulator" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, hi6421_regulator_table); diff --git a/drivers/regulator/hi6421v530-regulator.c b/drivers/regulator/hi6421v530-regulator.c index 1822f5daf6ce..7f4fc7175bbb 100644 --- a/drivers/regulator/hi6421v530-regulator.c +++ b/drivers/regulator/hi6421v530-regulator.c @@ -187,7 +187,7 @@ static int hi6421v530_regulator_probe(struct platform_device *pdev) static const struct platform_device_id hi6421v530_regulator_table[] = { { .name = "hi6421v530-regulator" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, hi6421v530_regulator_table); diff --git a/drivers/regulator/hi6421v600-regulator.c b/drivers/regulator/hi6421v600-regulator.c index e7c8bc10cf24..c42858c93b47 100644 --- a/drivers/regulator/hi6421v600-regulator.c +++ b/drivers/regulator/hi6421v600-regulator.c @@ -276,7 +276,7 @@ static int hi6421_spmi_regulator_probe(struct platform_device *pdev) static const struct platform_device_id hi6421_spmi_regulator_table[] = { { .name = "hi6421v600-regulator" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, hi6421_spmi_regulator_table); diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c index 1d8211f635b7..b2b454b6a18c 100644 --- a/drivers/regulator/hi655x-regulator.c +++ b/drivers/regulator/hi655x-regulator.c @@ -198,7 +198,7 @@ static int hi655x_regulator_probe(struct platform_device *pdev) static const struct platform_device_id hi655x_regulator_table[] = { { .name = "hi655x-regulator" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, hi655x_regulator_table); diff --git a/drivers/regulator/lp873x-regulator.c b/drivers/regulator/lp873x-regulator.c index 84a134cfcd9c..7e837ddfa236 100644 --- a/drivers/regulator/lp873x-regulator.c +++ b/drivers/regulator/lp873x-regulator.c @@ -180,7 +180,7 @@ static int lp873x_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id lp873x_regulator_id_table[] = { - { "lp873x-regulator", }, + { .name = "lp873x-regulator" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp873x_regulator_id_table); diff --git a/drivers/regulator/lp87565-regulator.c b/drivers/regulator/lp87565-regulator.c index 1259b5d20153..34e7a5d323d7 100644 --- a/drivers/regulator/lp87565-regulator.c +++ b/drivers/regulator/lp87565-regulator.c @@ -229,8 +229,8 @@ static int lp87565_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id lp87565_regulator_id_table[] = { - { "lp87565-regulator", }, - { "lp87565-q1-regulator", }, + { .name = "lp87565-regulator" }, + { .name = "lp87565-q1-regulator" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, lp87565_regulator_id_table); diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index 41fd15adfd1f..c9d8d5e31cbd 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c @@ -235,8 +235,8 @@ static int max14577_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id max14577_regulator_id[] = { - { "max14577-regulator", MAXIM_DEVICE_TYPE_MAX14577, }, - { "max77836-regulator", MAXIM_DEVICE_TYPE_MAX77836, }, + { .name = "max14577-regulator", .driver_data = MAXIM_DEVICE_TYPE_MAX14577 }, + { .name = "max77836-regulator", .driver_data = MAXIM_DEVICE_TYPE_MAX77836 }, { } }; MODULE_DEVICE_TABLE(platform, max14577_regulator_id); diff --git a/drivers/regulator/max77541-regulator.c b/drivers/regulator/max77541-regulator.c index e6b3d9147c37..f2365930e9a9 100644 --- a/drivers/regulator/max77541-regulator.c +++ b/drivers/regulator/max77541-regulator.c @@ -133,8 +133,8 @@ static int max77541_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id max77541_regulator_platform_id[] = { - { "max77540-regulator" }, - { "max77541-regulator" }, + { .name = "max77540-regulator" }, + { .name = "max77541-regulator" }, { } }; MODULE_DEVICE_TABLE(platform, max77541_regulator_platform_id); diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c index 57c54472ec5b..5099c372eea5 100644 --- a/drivers/regulator/max77620-regulator.c +++ b/drivers/regulator/max77620-regulator.c @@ -902,10 +902,10 @@ static const struct dev_pm_ops max77620_regulator_pm_ops = { }; static const struct platform_device_id max77620_regulator_devtype[] = { - { .name = "max77620-pmic", }, - { .name = "max20024-pmic", }, - { .name = "max77663-pmic", }, - {}, + { .name = "max77620-pmic" }, + { .name = "max20024-pmic" }, + { .name = "max77663-pmic" }, + { } }; MODULE_DEVICE_TABLE(platform, max77620_regulator_devtype); diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index c7b270fd9e0c..3a0156f4d6e7 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -517,8 +517,8 @@ static int max77686_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id max77686_pmic_id[] = { - {"max77686-pmic", 0}, - { }, + { .name = "max77686-pmic" }, + { } }; MODULE_DEVICE_TABLE(platform, max77686_pmic_id); diff --git a/drivers/regulator/max77693-regulator.c b/drivers/regulator/max77693-regulator.c index 72a67d0c5f1e..a8b3a2058d34 100644 --- a/drivers/regulator/max77693-regulator.c +++ b/drivers/regulator/max77693-regulator.c @@ -271,9 +271,9 @@ static int max77693_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id max77693_pmic_id[] = { - { "max77693-pmic", TYPE_MAX77693 }, - { "max77843-regulator", TYPE_MAX77843 }, - {}, + { .name = "max77693-pmic", .driver_data = TYPE_MAX77693 }, + { .name = "max77843-regulator", .driver_data = TYPE_MAX77843 }, + { } }; MODULE_DEVICE_TABLE(platform, max77693_pmic_id); diff --git a/drivers/regulator/max77802-regulator.c b/drivers/regulator/max77802-regulator.c index b2e87642bec4..4c05cc9c4bd2 100644 --- a/drivers/regulator/max77802-regulator.c +++ b/drivers/regulator/max77802-regulator.c @@ -546,8 +546,8 @@ static int max77802_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id max77802_pmic_id[] = { - {"max77802-pmic", 0}, - { }, + { .name = "max77802-pmic" }, + { } }; MODULE_DEVICE_TABLE(platform, max77802_pmic_id); diff --git a/drivers/regulator/max8997-regulator.c b/drivers/regulator/max8997-regulator.c index e77621b6466c..e48ba694a906 100644 --- a/drivers/regulator/max8997-regulator.c +++ b/drivers/regulator/max8997-regulator.c @@ -1152,8 +1152,8 @@ static int max8997_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id max8997_pmic_id[] = { - { "max8997-pmic", 0}, - { }, + { .name = "max8997-pmic" }, + { } }; MODULE_DEVICE_TABLE(platform, max8997_pmic_id); diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 254a77887f66..cc85fbe8b77c 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -752,8 +752,8 @@ static int max8998_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id max8998_pmic_id[] = { - { "max8998-pmic", TYPE_MAX8998 }, - { "lp3974-pmic", TYPE_LP3974 }, + { .name = "max8998-pmic", .driver_data = TYPE_MAX8998 }, + { .name = "lp3974-pmic", .driver_data = TYPE_LP3974 }, { } }; MODULE_DEVICE_TABLE(platform, max8998_pmic_id); diff --git a/drivers/regulator/mt6323-regulator.c b/drivers/regulator/mt6323-regulator.c index b43da848a06e..bac226812b0b 100644 --- a/drivers/regulator/mt6323-regulator.c +++ b/drivers/regulator/mt6323-regulator.c @@ -401,8 +401,8 @@ static int mt6323_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6323_platform_ids[] = { - {"mt6323-regulator", 0}, - { /* sentinel */ }, + { .name = "mt6323-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6323_platform_ids); diff --git a/drivers/regulator/mt6331-regulator.c b/drivers/regulator/mt6331-regulator.c index 0059f88c6fd7..eed9dda0481f 100644 --- a/drivers/regulator/mt6331-regulator.c +++ b/drivers/regulator/mt6331-regulator.c @@ -487,8 +487,8 @@ static int mt6331_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6331_platform_ids[] = { - {"mt6331-regulator", 0}, - { /* sentinel */ }, + { .name = "mt6331-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6331_platform_ids); diff --git a/drivers/regulator/mt6332-regulator.c b/drivers/regulator/mt6332-regulator.c index 8d8331a2aca5..949fde37617c 100644 --- a/drivers/regulator/mt6332-regulator.c +++ b/drivers/regulator/mt6332-regulator.c @@ -402,8 +402,8 @@ static int mt6332_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6332_platform_ids[] = { - {"mt6332-regulator", 0}, - { /* sentinel */ }, + { .name = "mt6332-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6332_platform_ids); diff --git a/drivers/regulator/mt6357-regulator.c b/drivers/regulator/mt6357-regulator.c index 09feb454ab6b..815ef7d3e5be 100644 --- a/drivers/regulator/mt6357-regulator.c +++ b/drivers/regulator/mt6357-regulator.c @@ -431,8 +431,8 @@ static int mt6357_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6357_platform_ids[] = { - { "mt6357-regulator" }, - { /* sentinel */ }, + { .name = "mt6357-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6357_platform_ids); diff --git a/drivers/regulator/mt6358-regulator.c b/drivers/regulator/mt6358-regulator.c index 2604f674be49..f2bb3c1523ca 100644 --- a/drivers/regulator/mt6358-regulator.c +++ b/drivers/regulator/mt6358-regulator.c @@ -724,8 +724,8 @@ static int mt6358_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6358_platform_ids[] = { - {"mt6358-regulator", 0}, - { /* sentinel */ }, + { .name = "mt6358-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6358_platform_ids); diff --git a/drivers/regulator/mt6359-regulator.c b/drivers/regulator/mt6359-regulator.c index c8a788858824..7c3a3ecb3641 100644 --- a/drivers/regulator/mt6359-regulator.c +++ b/drivers/regulator/mt6359-regulator.c @@ -38,7 +38,7 @@ struct mt6359_regulator_info { u32 lp_mode_mask; }; -#define MT6359_BUCK(match, _name, min, max, step, \ +#define MT6359_BUCK(match, _name, supply, min, max, step, \ _enable_reg, _status_reg, \ _vsel_reg, _vsel_mask, \ _lp_mode_reg, _lp_mode_shift, \ @@ -46,6 +46,7 @@ struct mt6359_regulator_info { [MT6359_ID_##_name] = { \ .desc = { \ .name = #_name, \ + .supply_name = supply, \ .of_match = of_match_ptr(match), \ .regulators_node = of_match_ptr("regulators"), \ .ops = &mt6359_volt_linear_ops, \ @@ -69,11 +70,12 @@ struct mt6359_regulator_info { .modeset_mask = BIT(_modeset_shift), \ } -#define MT6359_LDO_LINEAR(match, _name, min, max, step, \ +#define MT6359_LDO_LINEAR(match, _name, supply, min, max, step, \ _enable_reg, _status_reg, _vsel_reg, _vsel_mask) \ [MT6359_ID_##_name] = { \ .desc = { \ .name = #_name, \ + .supply_name = supply, \ .of_match = of_match_ptr(match), \ .regulators_node = of_match_ptr("regulators"), \ .ops = &mt6359_volt_linear_ops, \ @@ -92,12 +94,13 @@ struct mt6359_regulator_info { .qi = BIT(0), \ } -#define MT6359_LDO(match, _name, _volt_table, \ +#define MT6359_LDO(match, _name, supply, _volt_table, \ _enable_reg, _enable_mask, _status_reg, \ _vsel_reg, _vsel_mask, _en_delay) \ [MT6359_ID_##_name] = { \ .desc = { \ .name = #_name, \ + .supply_name = supply, \ .of_match = of_match_ptr(match), \ .regulators_node = of_match_ptr("regulators"), \ .ops = &mt6359_volt_table_ops, \ @@ -116,11 +119,13 @@ struct mt6359_regulator_info { .qi = BIT(0), \ } -#define MT6359_REG_FIXED(match, _name, _enable_reg, \ - _status_reg, _fixed_volt) \ +#define MT6359_REG_FIXED(match, _name, supply, \ + _enable_reg, _status_reg, \ + _fixed_volt) \ [MT6359_ID_##_name] = { \ .desc = { \ .name = #_name, \ + .supply_name = supply, \ .of_match = of_match_ptr(match), \ .regulators_node = of_match_ptr("regulators"), \ .ops = &mt6359_volt_fixed_ops, \ @@ -136,12 +141,14 @@ struct mt6359_regulator_info { .qi = BIT(0), \ } -#define MT6359P_LDO1(match, _name, _ops, _volt_table, \ - _enable_reg, _enable_mask, _status_reg, \ - _vsel_reg, _vsel_mask) \ +#define MT6359P_LDO1(match, _name, supply, _ops, \ + _volt_table, _enable_reg, \ + _enable_mask, _status_reg, \ + _vsel_reg, _vsel_mask) \ [MT6359_ID_##_name] = { \ .desc = { \ .name = #_name, \ + .supply_name = supply, \ .of_match = of_match_ptr(match), \ .regulators_node = of_match_ptr("regulators"), \ .ops = &_ops, \ @@ -159,6 +166,20 @@ struct mt6359_regulator_info { .qi = BIT(0), \ } +#define MT6359_LDO_NOOP(match, _name, supply) \ +[MT6359_ID_##_name] = { \ + .desc = { \ + .name = #_name, \ + .supply_name = supply, \ + .of_match = of_match_ptr(match), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &mt6359_noop_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6359_ID_##_name, \ + .owner = THIS_MODULE, \ + }, \ +} + static const unsigned int vsim1_voltages[] = { 0, 0, 0, 1700000, 1800000, 0, 0, 0, 2700000, 0, 0, 3000000, 3100000, }; @@ -251,7 +272,7 @@ static int mt6359_get_status(struct regulator_dev *rdev) { int ret; u32 regval; - struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); + const struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); ret = regmap_read(rdev->regmap, info->status_reg, ®val); if (ret != 0) { @@ -267,7 +288,7 @@ static int mt6359_get_status(struct regulator_dev *rdev) static unsigned int mt6359_regulator_get_mode(struct regulator_dev *rdev) { - struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); + const struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); int ret, regval; ret = regmap_read(rdev->regmap, info->modeset_reg, ®val); @@ -299,7 +320,7 @@ static unsigned int mt6359_regulator_get_mode(struct regulator_dev *rdev) static int mt6359_regulator_set_mode(struct regulator_dev *rdev, unsigned int mode) { - struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); + const struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); int ret = 0, val; int curr_mode; @@ -354,7 +375,7 @@ static int mt6359_regulator_set_mode(struct regulator_dev *rdev, static int mt6359p_vemc_set_voltage_sel(struct regulator_dev *rdev, u32 sel) { - struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); + const struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); int ret; u32 val = 0; @@ -393,7 +414,7 @@ static int mt6359p_vemc_set_voltage_sel(struct regulator_dev *rdev, static int mt6359p_vemc_get_voltage_sel(struct regulator_dev *rdev) { - struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); + const struct mt6359_regulator_info *info = rdev_get_drvdata(rdev); int ret; u32 val = 0; @@ -468,16 +489,19 @@ static const struct regulator_ops mt6359p_vemc_ops = { .get_status = mt6359_get_status, }; +/* Used for backward-compatible placeholder regulators */ +static const struct regulator_ops mt6359_noop_ops = {}; + /* The array is indexed by id(MT6359_ID_XXX) */ -static struct mt6359_regulator_info mt6359_regulators[] = { - MT6359_BUCK("buck_vs1", VS1, 800000, 2200000, 12500, +static const struct mt6359_regulator_info mt6359_regulators[] = { + MT6359_BUCK("buck_vs1", VS1, "vsys-vs1", 800000, 2200000, 12500, MT6359_RG_BUCK_VS1_EN_ADDR, MT6359_DA_VS1_EN_ADDR, MT6359_RG_BUCK_VS1_VOSEL_ADDR, MT6359_RG_BUCK_VS1_VOSEL_MASK << MT6359_RG_BUCK_VS1_VOSEL_SHIFT, MT6359_RG_BUCK_VS1_LP_ADDR, MT6359_RG_BUCK_VS1_LP_SHIFT, MT6359_RG_VS1_FPWM_ADDR, MT6359_RG_VS1_FPWM_SHIFT), - MT6359_BUCK("buck_vgpu11", VGPU11, 400000, 1193750, 6250, + MT6359_BUCK("buck_vgpu11", VGPU11, "vsys-vgpu11", 400000, 1193750, 6250, MT6359_RG_BUCK_VGPU11_EN_ADDR, MT6359_DA_VGPU11_EN_ADDR, MT6359_RG_BUCK_VGPU11_VOSEL_ADDR, MT6359_RG_BUCK_VGPU11_VOSEL_MASK << @@ -485,7 +509,7 @@ static struct mt6359_regulator_info mt6359_regulators[] = { MT6359_RG_BUCK_VGPU11_LP_ADDR, MT6359_RG_BUCK_VGPU11_LP_SHIFT, MT6359_RG_VGPU11_FCCM_ADDR, MT6359_RG_VGPU11_FCCM_SHIFT), - MT6359_BUCK("buck_vmodem", VMODEM, 400000, 1100000, 6250, + MT6359_BUCK("buck_vmodem", VMODEM, "vsys-vmodem", 400000, 1100000, 6250, MT6359_RG_BUCK_VMODEM_EN_ADDR, MT6359_DA_VMODEM_EN_ADDR, MT6359_RG_BUCK_VMODEM_VOSEL_ADDR, MT6359_RG_BUCK_VMODEM_VOSEL_MASK << @@ -493,35 +517,35 @@ static struct mt6359_regulator_info mt6359_regulators[] = { MT6359_RG_BUCK_VMODEM_LP_ADDR, MT6359_RG_BUCK_VMODEM_LP_SHIFT, MT6359_RG_VMODEM_FCCM_ADDR, MT6359_RG_VMODEM_FCCM_SHIFT), - MT6359_BUCK("buck_vpu", VPU, 400000, 1193750, 6250, + MT6359_BUCK("buck_vpu", VPU, "vsys-vpu", 400000, 1193750, 6250, MT6359_RG_BUCK_VPU_EN_ADDR, MT6359_DA_VPU_EN_ADDR, MT6359_RG_BUCK_VPU_VOSEL_ADDR, MT6359_RG_BUCK_VPU_VOSEL_MASK << MT6359_RG_BUCK_VPU_VOSEL_SHIFT, MT6359_RG_BUCK_VPU_LP_ADDR, MT6359_RG_BUCK_VPU_LP_SHIFT, MT6359_RG_VPU_FCCM_ADDR, MT6359_RG_VPU_FCCM_SHIFT), - MT6359_BUCK("buck_vcore", VCORE, 400000, 1193750, 6250, + MT6359_BUCK("buck_vcore", VCORE, "vsys-vcore", 400000, 1193750, 6250, MT6359_RG_BUCK_VCORE_EN_ADDR, MT6359_DA_VCORE_EN_ADDR, MT6359_RG_BUCK_VCORE_VOSEL_ADDR, MT6359_RG_BUCK_VCORE_VOSEL_MASK << MT6359_RG_BUCK_VCORE_VOSEL_SHIFT, MT6359_RG_BUCK_VCORE_LP_ADDR, MT6359_RG_BUCK_VCORE_LP_SHIFT, MT6359_RG_VCORE_FCCM_ADDR, MT6359_RG_VCORE_FCCM_SHIFT), - MT6359_BUCK("buck_vs2", VS2, 800000, 1600000, 12500, + MT6359_BUCK("buck_vs2", VS2, "vsys-vs2", 800000, 1600000, 12500, MT6359_RG_BUCK_VS2_EN_ADDR, MT6359_DA_VS2_EN_ADDR, MT6359_RG_BUCK_VS2_VOSEL_ADDR, MT6359_RG_BUCK_VS2_VOSEL_MASK << MT6359_RG_BUCK_VS2_VOSEL_SHIFT, MT6359_RG_BUCK_VS2_LP_ADDR, MT6359_RG_BUCK_VS2_LP_SHIFT, MT6359_RG_VS2_FPWM_ADDR, MT6359_RG_VS2_FPWM_SHIFT), - MT6359_BUCK("buck_vpa", VPA, 500000, 3650000, 50000, + MT6359_BUCK("buck_vpa", VPA, "vsys-vpa", 500000, 3650000, 50000, MT6359_RG_BUCK_VPA_EN_ADDR, MT6359_DA_VPA_EN_ADDR, MT6359_RG_BUCK_VPA_VOSEL_ADDR, MT6359_RG_BUCK_VPA_VOSEL_MASK << MT6359_RG_BUCK_VPA_VOSEL_SHIFT, MT6359_RG_BUCK_VPA_LP_ADDR, MT6359_RG_BUCK_VPA_LP_SHIFT, MT6359_RG_VPA_MODESET_ADDR, MT6359_RG_VPA_MODESET_SHIFT), - MT6359_BUCK("buck_vproc2", VPROC2, 400000, 1193750, 6250, + MT6359_BUCK("buck_vproc2", VPROC2, "vsys-vproc2", 400000, 1193750, 6250, MT6359_RG_BUCK_VPROC2_EN_ADDR, MT6359_DA_VPROC2_EN_ADDR, MT6359_RG_BUCK_VPROC2_VOSEL_ADDR, MT6359_RG_BUCK_VPROC2_VOSEL_MASK << @@ -529,7 +553,7 @@ static struct mt6359_regulator_info mt6359_regulators[] = { MT6359_RG_BUCK_VPROC2_LP_ADDR, MT6359_RG_BUCK_VPROC2_LP_SHIFT, MT6359_RG_VPROC2_FCCM_ADDR, MT6359_RG_VPROC2_FCCM_SHIFT), - MT6359_BUCK("buck_vproc1", VPROC1, 400000, 1193750, 6250, + MT6359_BUCK("buck_vproc1", VPROC1, "vsys-vproc1", 400000, 1193750, 6250, MT6359_RG_BUCK_VPROC1_EN_ADDR, MT6359_DA_VPROC1_EN_ADDR, MT6359_RG_BUCK_VPROC1_VOSEL_ADDR, MT6359_RG_BUCK_VPROC1_VOSEL_MASK << @@ -537,7 +561,7 @@ static struct mt6359_regulator_info mt6359_regulators[] = { MT6359_RG_BUCK_VPROC1_LP_ADDR, MT6359_RG_BUCK_VPROC1_LP_SHIFT, MT6359_RG_VPROC1_FCCM_ADDR, MT6359_RG_VPROC1_FCCM_SHIFT), - MT6359_BUCK("buck_vcore_sshub", VCORE_SSHUB, 400000, 1193750, 6250, + MT6359_BUCK("buck_vcore_sshub", VCORE_SSHUB, "vsys-vcore", 400000, 1193750, 6250, MT6359_RG_BUCK_VCORE_SSHUB_EN_ADDR, MT6359_DA_VCORE_EN_ADDR, MT6359_RG_BUCK_VCORE_SSHUB_VOSEL_ADDR, @@ -545,175 +569,169 @@ static struct mt6359_regulator_info mt6359_regulators[] = { MT6359_RG_BUCK_VCORE_SSHUB_VOSEL_SHIFT, MT6359_RG_BUCK_VCORE_LP_ADDR, MT6359_RG_BUCK_VCORE_LP_SHIFT, MT6359_RG_VCORE_FCCM_ADDR, MT6359_RG_VCORE_FCCM_SHIFT), - MT6359_REG_FIXED("ldo_vaud18", VAUD18, MT6359_RG_LDO_VAUD18_EN_ADDR, + MT6359_REG_FIXED("ldo_vaud18", VAUD18, "vs1-ldo1", MT6359_RG_LDO_VAUD18_EN_ADDR, MT6359_DA_VAUD18_B_EN_ADDR, 1800000), - MT6359_LDO("ldo_vsim1", VSIM1, vsim1_voltages, + MT6359_LDO("ldo_vsim1", VSIM1, "vsys-ldo2", vsim1_voltages, MT6359_RG_LDO_VSIM1_EN_ADDR, MT6359_RG_LDO_VSIM1_EN_SHIFT, MT6359_DA_VSIM1_B_EN_ADDR, MT6359_RG_VSIM1_VOSEL_ADDR, MT6359_RG_VSIM1_VOSEL_MASK << MT6359_RG_VSIM1_VOSEL_SHIFT, 480), - MT6359_LDO("ldo_vibr", VIBR, vibr_voltages, + MT6359_LDO("ldo_vibr", VIBR, "vsys-ldo1", vibr_voltages, MT6359_RG_LDO_VIBR_EN_ADDR, MT6359_RG_LDO_VIBR_EN_SHIFT, MT6359_DA_VIBR_B_EN_ADDR, MT6359_RG_VIBR_VOSEL_ADDR, MT6359_RG_VIBR_VOSEL_MASK << MT6359_RG_VIBR_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vrf12", VRF12, vrf12_voltages, + MT6359_LDO("ldo_vrf12", VRF12, "vs2-ldo2", vrf12_voltages, MT6359_RG_LDO_VRF12_EN_ADDR, MT6359_RG_LDO_VRF12_EN_SHIFT, MT6359_DA_VRF12_B_EN_ADDR, MT6359_RG_VRF12_VOSEL_ADDR, MT6359_RG_VRF12_VOSEL_MASK << MT6359_RG_VRF12_VOSEL_SHIFT, 120), - MT6359_REG_FIXED("ldo_vusb", VUSB, MT6359_RG_LDO_VUSB_EN_0_ADDR, + MT6359_REG_FIXED("ldo_vusb", VUSB, "vsys-ldo2", MT6359_RG_LDO_VUSB_EN_0_ADDR, MT6359_DA_VUSB_B_EN_ADDR, 3000000), - MT6359_LDO_LINEAR("ldo_vsram_proc2", VSRAM_PROC2, 500000, 1293750, 6250, + MT6359_LDO_LINEAR("ldo_vsram_proc2", VSRAM_PROC2, "vs2-ldo1", 500000, 1293750, 6250, MT6359_RG_LDO_VSRAM_PROC2_EN_ADDR, MT6359_DA_VSRAM_PROC2_B_EN_ADDR, MT6359_RG_LDO_VSRAM_PROC2_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_PROC2_VOSEL_MASK << MT6359_RG_LDO_VSRAM_PROC2_VOSEL_SHIFT), - MT6359_LDO("ldo_vio18", VIO18, volt18_voltages, + MT6359_LDO("ldo_vio18", VIO18, "vs1-ldo2", volt18_voltages, MT6359_RG_LDO_VIO18_EN_ADDR, MT6359_RG_LDO_VIO18_EN_SHIFT, MT6359_DA_VIO18_B_EN_ADDR, MT6359_RG_VIO18_VOSEL_ADDR, MT6359_RG_VIO18_VOSEL_MASK << MT6359_RG_VIO18_VOSEL_SHIFT, 960), - MT6359_LDO("ldo_vcamio", VCAMIO, volt18_voltages, + MT6359_LDO("ldo_vcamio", VCAMIO, "vs1-ldo1", volt18_voltages, MT6359_RG_LDO_VCAMIO_EN_ADDR, MT6359_RG_LDO_VCAMIO_EN_SHIFT, MT6359_DA_VCAMIO_B_EN_ADDR, MT6359_RG_VCAMIO_VOSEL_ADDR, MT6359_RG_VCAMIO_VOSEL_MASK << MT6359_RG_VCAMIO_VOSEL_SHIFT, 1290), - MT6359_REG_FIXED("ldo_vcn18", VCN18, MT6359_RG_LDO_VCN18_EN_ADDR, + MT6359_REG_FIXED("ldo_vcn18", VCN18, "vs1-ldo2", MT6359_RG_LDO_VCN18_EN_ADDR, MT6359_DA_VCN18_B_EN_ADDR, 1800000), - MT6359_REG_FIXED("ldo_vfe28", VFE28, MT6359_RG_LDO_VFE28_EN_ADDR, + MT6359_REG_FIXED("ldo_vfe28", VFE28, "vsys-ldo1", MT6359_RG_LDO_VFE28_EN_ADDR, MT6359_DA_VFE28_B_EN_ADDR, 2800000), - MT6359_LDO("ldo_vcn13", VCN13, vcn13_voltages, + MT6359_LDO("ldo_vcn13", VCN13, "vs2-ldo2", vcn13_voltages, MT6359_RG_LDO_VCN13_EN_ADDR, MT6359_RG_LDO_VCN13_EN_SHIFT, MT6359_DA_VCN13_B_EN_ADDR, MT6359_RG_VCN13_VOSEL_ADDR, MT6359_RG_VCN13_VOSEL_MASK << MT6359_RG_VCN13_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vcn33_1_bt", VCN33_1_BT, vcn33_voltages, + MT6359_LDO("ldo_vcn33_1", VCN33_1, "vsys-ldo1", vcn33_voltages, MT6359_RG_LDO_VCN33_1_EN_0_ADDR, MT6359_RG_LDO_VCN33_1_EN_0_SHIFT, MT6359_DA_VCN33_1_B_EN_ADDR, MT6359_RG_VCN33_1_VOSEL_ADDR, MT6359_RG_VCN33_1_VOSEL_MASK << MT6359_RG_VCN33_1_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vcn33_1_wifi", VCN33_1_WIFI, vcn33_voltages, - MT6359_RG_LDO_VCN33_1_EN_1_ADDR, - MT6359_RG_LDO_VCN33_1_EN_1_SHIFT, - MT6359_DA_VCN33_1_B_EN_ADDR, MT6359_RG_VCN33_1_VOSEL_ADDR, - MT6359_RG_VCN33_1_VOSEL_MASK << - MT6359_RG_VCN33_1_VOSEL_SHIFT, 240), - MT6359_REG_FIXED("ldo_vaux18", VAUX18, MT6359_RG_LDO_VAUX18_EN_ADDR, + MT6359_REG_FIXED("ldo_vaux18", VAUX18, "vsys-ldo2", MT6359_RG_LDO_VAUX18_EN_ADDR, MT6359_DA_VAUX18_B_EN_ADDR, 1800000), - MT6359_LDO_LINEAR("ldo_vsram_others", VSRAM_OTHERS, 500000, 1293750, + MT6359_LDO_LINEAR("ldo_vsram_others", VSRAM_OTHERS, "vs2-ldo1", 500000, 1293750, 6250, MT6359_RG_LDO_VSRAM_OTHERS_EN_ADDR, MT6359_DA_VSRAM_OTHERS_B_EN_ADDR, MT6359_RG_LDO_VSRAM_OTHERS_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_OTHERS_VOSEL_MASK << MT6359_RG_LDO_VSRAM_OTHERS_VOSEL_SHIFT), - MT6359_LDO("ldo_vefuse", VEFUSE, vefuse_voltages, + MT6359_LDO("ldo_vefuse", VEFUSE, "vs1-ldo2", vefuse_voltages, MT6359_RG_LDO_VEFUSE_EN_ADDR, MT6359_RG_LDO_VEFUSE_EN_SHIFT, MT6359_DA_VEFUSE_B_EN_ADDR, MT6359_RG_VEFUSE_VOSEL_ADDR, MT6359_RG_VEFUSE_VOSEL_MASK << MT6359_RG_VEFUSE_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vxo22", VXO22, vxo22_voltages, + MT6359_LDO("ldo_vxo22", VXO22, "vsys-ldo2", vxo22_voltages, MT6359_RG_LDO_VXO22_EN_ADDR, MT6359_RG_LDO_VXO22_EN_SHIFT, MT6359_DA_VXO22_B_EN_ADDR, MT6359_RG_VXO22_VOSEL_ADDR, MT6359_RG_VXO22_VOSEL_MASK << MT6359_RG_VXO22_VOSEL_SHIFT, 120), - MT6359_LDO("ldo_vrfck", VRFCK, vrfck_voltages, + MT6359_LDO("ldo_vrfck", VRFCK, "vsys-ldo2", vrfck_voltages, MT6359_RG_LDO_VRFCK_EN_ADDR, MT6359_RG_LDO_VRFCK_EN_SHIFT, MT6359_DA_VRFCK_B_EN_ADDR, MT6359_RG_VRFCK_VOSEL_ADDR, MT6359_RG_VRFCK_VOSEL_MASK << MT6359_RG_VRFCK_VOSEL_SHIFT, 480), - MT6359_REG_FIXED("ldo_vbif28", VBIF28, MT6359_RG_LDO_VBIF28_EN_ADDR, + MT6359_REG_FIXED("ldo_vbif28", VBIF28, "vsys-ldo2", MT6359_RG_LDO_VBIF28_EN_ADDR, MT6359_DA_VBIF28_B_EN_ADDR, 2800000), - MT6359_LDO("ldo_vio28", VIO28, vio28_voltages, + MT6359_LDO("ldo_vio28", VIO28, "vsys-ldo2", vio28_voltages, MT6359_RG_LDO_VIO28_EN_ADDR, MT6359_RG_LDO_VIO28_EN_SHIFT, MT6359_DA_VIO28_B_EN_ADDR, MT6359_RG_VIO28_VOSEL_ADDR, MT6359_RG_VIO28_VOSEL_MASK << MT6359_RG_VIO28_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vemc", VEMC, vemc_voltages, + MT6359_LDO("ldo_vemc", VEMC, "vsys-ldo2", vemc_voltages, MT6359_RG_LDO_VEMC_EN_ADDR, MT6359_RG_LDO_VEMC_EN_SHIFT, MT6359_DA_VEMC_B_EN_ADDR, MT6359_RG_VEMC_VOSEL_ADDR, MT6359_RG_VEMC_VOSEL_MASK << MT6359_RG_VEMC_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vcn33_2_bt", VCN33_2_BT, vcn33_voltages, + MT6359_LDO("ldo_vcn33_2", VCN33_2, "vsys-ldo1", vcn33_voltages, MT6359_RG_LDO_VCN33_2_EN_0_ADDR, MT6359_RG_LDO_VCN33_2_EN_0_SHIFT, MT6359_DA_VCN33_2_B_EN_ADDR, MT6359_RG_VCN33_2_VOSEL_ADDR, MT6359_RG_VCN33_2_VOSEL_MASK << MT6359_RG_VCN33_2_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vcn33_2_wifi", VCN33_2_WIFI, vcn33_voltages, - MT6359_RG_LDO_VCN33_2_EN_1_ADDR, - MT6359_RG_LDO_VCN33_2_EN_1_SHIFT, - MT6359_DA_VCN33_2_B_EN_ADDR, MT6359_RG_VCN33_2_VOSEL_ADDR, - MT6359_RG_VCN33_2_VOSEL_MASK << - MT6359_RG_VCN33_2_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_va12", VA12, va12_voltages, + MT6359_LDO("ldo_va12", VA12, "vs2-ldo2", va12_voltages, MT6359_RG_LDO_VA12_EN_ADDR, MT6359_RG_LDO_VA12_EN_SHIFT, MT6359_DA_VA12_B_EN_ADDR, MT6359_RG_VA12_VOSEL_ADDR, MT6359_RG_VA12_VOSEL_MASK << MT6359_RG_VA12_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_va09", VA09, va09_voltages, + MT6359_LDO("ldo_va09", VA09, "vs2-ldo2", va09_voltages, MT6359_RG_LDO_VA09_EN_ADDR, MT6359_RG_LDO_VA09_EN_SHIFT, MT6359_DA_VA09_B_EN_ADDR, MT6359_RG_VA09_VOSEL_ADDR, MT6359_RG_VA09_VOSEL_MASK << MT6359_RG_VA09_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vrf18", VRF18, vrf18_voltages, + MT6359_LDO("ldo_vrf18", VRF18, "vs1-ldo2", vrf18_voltages, MT6359_RG_LDO_VRF18_EN_ADDR, MT6359_RG_LDO_VRF18_EN_SHIFT, MT6359_DA_VRF18_B_EN_ADDR, MT6359_RG_VRF18_VOSEL_ADDR, MT6359_RG_VRF18_VOSEL_MASK << MT6359_RG_VRF18_VOSEL_SHIFT, 120), - MT6359_LDO_LINEAR("ldo_vsram_md", VSRAM_MD, 500000, 1100000, 6250, + MT6359_LDO_LINEAR("ldo_vsram_md", VSRAM_MD, "vs2-ldo1", 500000, 1100000, 6250, MT6359_RG_LDO_VSRAM_MD_EN_ADDR, MT6359_DA_VSRAM_MD_B_EN_ADDR, MT6359_RG_LDO_VSRAM_MD_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_MD_VOSEL_MASK << MT6359_RG_LDO_VSRAM_MD_VOSEL_SHIFT), - MT6359_LDO("ldo_vufs", VUFS, volt18_voltages, + MT6359_LDO("ldo_vufs", VUFS, "vs1-ldo1", volt18_voltages, MT6359_RG_LDO_VUFS_EN_ADDR, MT6359_RG_LDO_VUFS_EN_SHIFT, MT6359_DA_VUFS_B_EN_ADDR, MT6359_RG_VUFS_VOSEL_ADDR, MT6359_RG_VUFS_VOSEL_MASK << MT6359_RG_VUFS_VOSEL_SHIFT, 1920), - MT6359_LDO("ldo_vm18", VM18, volt18_voltages, + MT6359_LDO("ldo_vm18", VM18, "vs1-ldo1", volt18_voltages, MT6359_RG_LDO_VM18_EN_ADDR, MT6359_RG_LDO_VM18_EN_SHIFT, MT6359_DA_VM18_B_EN_ADDR, MT6359_RG_VM18_VOSEL_ADDR, MT6359_RG_VM18_VOSEL_MASK << MT6359_RG_VM18_VOSEL_SHIFT, 1920), - MT6359_LDO("ldo_vbbck", VBBCK, vbbck_voltages, + /* vbbck is fed from vio18 internally. */ + MT6359_LDO("ldo_vbbck", VBBCK, "LDO_VIO18", vbbck_voltages, MT6359_RG_LDO_VBBCK_EN_ADDR, MT6359_RG_LDO_VBBCK_EN_SHIFT, MT6359_DA_VBBCK_B_EN_ADDR, MT6359_RG_VBBCK_VOSEL_ADDR, MT6359_RG_VBBCK_VOSEL_MASK << MT6359_RG_VBBCK_VOSEL_SHIFT, 240), - MT6359_LDO_LINEAR("ldo_vsram_proc1", VSRAM_PROC1, 500000, 1293750, 6250, + MT6359_LDO_LINEAR("ldo_vsram_proc1", VSRAM_PROC1, "vs2-ldo1", 500000, 1293750, 6250, MT6359_RG_LDO_VSRAM_PROC1_EN_ADDR, MT6359_DA_VSRAM_PROC1_B_EN_ADDR, MT6359_RG_LDO_VSRAM_PROC1_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_PROC1_VOSEL_MASK << MT6359_RG_LDO_VSRAM_PROC1_VOSEL_SHIFT), - MT6359_LDO("ldo_vsim2", VSIM2, vsim2_voltages, + MT6359_LDO("ldo_vsim2", VSIM2, "vsys-ldo2", vsim2_voltages, MT6359_RG_LDO_VSIM2_EN_ADDR, MT6359_RG_LDO_VSIM2_EN_SHIFT, MT6359_DA_VSIM2_B_EN_ADDR, MT6359_RG_VSIM2_VOSEL_ADDR, MT6359_RG_VSIM2_VOSEL_MASK << MT6359_RG_VSIM2_VOSEL_SHIFT, 480), - MT6359_LDO_LINEAR("ldo_vsram_others_sshub", VSRAM_OTHERS_SSHUB, + MT6359_LDO_LINEAR("ldo_vsram_others_sshub", VSRAM_OTHERS_SSHUB, "vs2-ldo1", 500000, 1293750, 6250, MT6359_RG_LDO_VSRAM_OTHERS_SSHUB_EN_ADDR, MT6359_DA_VSRAM_OTHERS_B_EN_ADDR, MT6359_RG_LDO_VSRAM_OTHERS_SSHUB_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_OTHERS_SSHUB_VOSEL_MASK << MT6359_RG_LDO_VSRAM_OTHERS_SSHUB_VOSEL_SHIFT), + /* Placeholders for DT backward compatibility */ + MT6359_LDO_NOOP("ldo_vcn33_1_bt", VCN33_1_BT, "LDO_VCN33_1"), + MT6359_LDO_NOOP("ldo_vcn33_1_wifi", VCN33_1_WIFI, "LDO_VCN33_1"), + MT6359_LDO_NOOP("ldo_vcn33_2_bt", VCN33_2_BT, "LDO_VCN33_2"), + MT6359_LDO_NOOP("ldo_vcn33_2_wifi", VCN33_2_WIFI, "LDO_VCN33_2"), }; -static struct mt6359_regulator_info mt6359p_regulators[] = { - MT6359_BUCK("buck_vs1", VS1, 800000, 2200000, 12500, +static const struct mt6359_regulator_info mt6359p_regulators[] = { + MT6359_BUCK("buck_vs1", VS1, "vsys-vs1", 800000, 2200000, 12500, MT6359_RG_BUCK_VS1_EN_ADDR, MT6359_DA_VS1_EN_ADDR, MT6359_RG_BUCK_VS1_VOSEL_ADDR, MT6359_RG_BUCK_VS1_VOSEL_MASK << MT6359_RG_BUCK_VS1_VOSEL_SHIFT, MT6359_RG_BUCK_VS1_LP_ADDR, MT6359_RG_BUCK_VS1_LP_SHIFT, MT6359_RG_VS1_FPWM_ADDR, MT6359_RG_VS1_FPWM_SHIFT), - MT6359_BUCK("buck_vgpu11", VGPU11, 400000, 1193750, 6250, + MT6359_BUCK("buck_vgpu11", VGPU11, "vsys-vgpu11", 400000, 1193750, 6250, MT6359_RG_BUCK_VGPU11_EN_ADDR, MT6359_DA_VGPU11_EN_ADDR, MT6359P_RG_BUCK_VGPU11_VOSEL_ADDR, MT6359_RG_BUCK_VGPU11_VOSEL_MASK << @@ -721,7 +739,7 @@ static struct mt6359_regulator_info mt6359p_regulators[] = { MT6359_RG_BUCK_VGPU11_LP_ADDR, MT6359_RG_BUCK_VGPU11_LP_SHIFT, MT6359_RG_VGPU11_FCCM_ADDR, MT6359_RG_VGPU11_FCCM_SHIFT), - MT6359_BUCK("buck_vmodem", VMODEM, 400000, 1100000, 6250, + MT6359_BUCK("buck_vmodem", VMODEM, "vsys-vmodem", 400000, 1100000, 6250, MT6359_RG_BUCK_VMODEM_EN_ADDR, MT6359_DA_VMODEM_EN_ADDR, MT6359_RG_BUCK_VMODEM_VOSEL_ADDR, MT6359_RG_BUCK_VMODEM_VOSEL_MASK << @@ -729,35 +747,35 @@ static struct mt6359_regulator_info mt6359p_regulators[] = { MT6359_RG_BUCK_VMODEM_LP_ADDR, MT6359_RG_BUCK_VMODEM_LP_SHIFT, MT6359_RG_VMODEM_FCCM_ADDR, MT6359_RG_VMODEM_FCCM_SHIFT), - MT6359_BUCK("buck_vpu", VPU, 400000, 1193750, 6250, + MT6359_BUCK("buck_vpu", VPU, "vsys-vpu", 400000, 1193750, 6250, MT6359_RG_BUCK_VPU_EN_ADDR, MT6359_DA_VPU_EN_ADDR, MT6359_RG_BUCK_VPU_VOSEL_ADDR, MT6359_RG_BUCK_VPU_VOSEL_MASK << MT6359_RG_BUCK_VPU_VOSEL_SHIFT, MT6359_RG_BUCK_VPU_LP_ADDR, MT6359_RG_BUCK_VPU_LP_SHIFT, MT6359_RG_VPU_FCCM_ADDR, MT6359_RG_VPU_FCCM_SHIFT), - MT6359_BUCK("buck_vcore", VCORE, 506250, 1300000, 6250, + MT6359_BUCK("buck_vcore", VCORE, "vsys-vcore", 506250, 1300000, 6250, MT6359_RG_BUCK_VCORE_EN_ADDR, MT6359_DA_VCORE_EN_ADDR, MT6359P_RG_BUCK_VCORE_VOSEL_ADDR, MT6359_RG_BUCK_VCORE_VOSEL_MASK << MT6359_RG_BUCK_VCORE_VOSEL_SHIFT, MT6359_RG_BUCK_VCORE_LP_ADDR, MT6359_RG_BUCK_VCORE_LP_SHIFT, MT6359_RG_VCORE_FCCM_ADDR, MT6359_RG_VCORE_FCCM_SHIFT), - MT6359_BUCK("buck_vs2", VS2, 800000, 1600000, 12500, + MT6359_BUCK("buck_vs2", VS2, "vsys-vs2", 800000, 1600000, 12500, MT6359_RG_BUCK_VS2_EN_ADDR, MT6359_DA_VS2_EN_ADDR, MT6359_RG_BUCK_VS2_VOSEL_ADDR, MT6359_RG_BUCK_VS2_VOSEL_MASK << MT6359_RG_BUCK_VS2_VOSEL_SHIFT, MT6359_RG_BUCK_VS2_LP_ADDR, MT6359_RG_BUCK_VS2_LP_SHIFT, MT6359_RG_VS2_FPWM_ADDR, MT6359_RG_VS2_FPWM_SHIFT), - MT6359_BUCK("buck_vpa", VPA, 500000, 3650000, 50000, + MT6359_BUCK("buck_vpa", VPA, "vsys-vpa", 500000, 3650000, 50000, MT6359_RG_BUCK_VPA_EN_ADDR, MT6359_DA_VPA_EN_ADDR, MT6359_RG_BUCK_VPA_VOSEL_ADDR, MT6359_RG_BUCK_VPA_VOSEL_MASK << MT6359_RG_BUCK_VPA_VOSEL_SHIFT, MT6359_RG_BUCK_VPA_LP_ADDR, MT6359_RG_BUCK_VPA_LP_SHIFT, MT6359_RG_VPA_MODESET_ADDR, MT6359_RG_VPA_MODESET_SHIFT), - MT6359_BUCK("buck_vproc2", VPROC2, 400000, 1193750, 6250, + MT6359_BUCK("buck_vproc2", VPROC2, "vsys-vproc2", 400000, 1193750, 6250, MT6359_RG_BUCK_VPROC2_EN_ADDR, MT6359_DA_VPROC2_EN_ADDR, MT6359_RG_BUCK_VPROC2_VOSEL_ADDR, MT6359_RG_BUCK_VPROC2_VOSEL_MASK << @@ -765,7 +783,7 @@ static struct mt6359_regulator_info mt6359p_regulators[] = { MT6359_RG_BUCK_VPROC2_LP_ADDR, MT6359_RG_BUCK_VPROC2_LP_SHIFT, MT6359_RG_VPROC2_FCCM_ADDR, MT6359_RG_VPROC2_FCCM_SHIFT), - MT6359_BUCK("buck_vproc1", VPROC1, 400000, 1193750, 6250, + MT6359_BUCK("buck_vproc1", VPROC1, "vsys-vproc1", 400000, 1193750, 6250, MT6359_RG_BUCK_VPROC1_EN_ADDR, MT6359_DA_VPROC1_EN_ADDR, MT6359_RG_BUCK_VPROC1_VOSEL_ADDR, MT6359_RG_BUCK_VPROC1_VOSEL_MASK << @@ -773,7 +791,7 @@ static struct mt6359_regulator_info mt6359p_regulators[] = { MT6359_RG_BUCK_VPROC1_LP_ADDR, MT6359_RG_BUCK_VPROC1_LP_SHIFT, MT6359_RG_VPROC1_FCCM_ADDR, MT6359_RG_VPROC1_FCCM_SHIFT), - MT6359_BUCK("buck_vgpu11_sshub", VGPU11_SSHUB, 400000, 1193750, 6250, + MT6359_BUCK("buck_vgpu11_sshub", VGPU11_SSHUB, "vsys-vgpu11", 400000, 1193750, 6250, MT6359P_RG_BUCK_VGPU11_SSHUB_EN_ADDR, MT6359_DA_VGPU11_EN_ADDR, MT6359P_RG_BUCK_VGPU11_SSHUB_VOSEL_ADDR, @@ -782,203 +800,339 @@ static struct mt6359_regulator_info mt6359p_regulators[] = { MT6359_RG_BUCK_VGPU11_LP_ADDR, MT6359_RG_BUCK_VGPU11_LP_SHIFT, MT6359_RG_VGPU11_FCCM_ADDR, MT6359_RG_VGPU11_FCCM_SHIFT), - MT6359_REG_FIXED("ldo_vaud18", VAUD18, MT6359P_RG_LDO_VAUD18_EN_ADDR, + MT6359_REG_FIXED("ldo_vaud18", VAUD18, "vs1-ldo1", MT6359P_RG_LDO_VAUD18_EN_ADDR, MT6359P_DA_VAUD18_B_EN_ADDR, 1800000), - MT6359_LDO("ldo_vsim1", VSIM1, vsim1_voltages, + MT6359_LDO("ldo_vsim1", VSIM1, "vsys-ldo2", vsim1_voltages, MT6359P_RG_LDO_VSIM1_EN_ADDR, MT6359P_RG_LDO_VSIM1_EN_SHIFT, MT6359P_DA_VSIM1_B_EN_ADDR, MT6359P_RG_VSIM1_VOSEL_ADDR, MT6359_RG_VSIM1_VOSEL_MASK << MT6359_RG_VSIM1_VOSEL_SHIFT, 480), - MT6359_LDO("ldo_vibr", VIBR, vibr_voltages, + MT6359_LDO("ldo_vibr", VIBR, "vsys-ldo1", vibr_voltages, MT6359P_RG_LDO_VIBR_EN_ADDR, MT6359P_RG_LDO_VIBR_EN_SHIFT, MT6359P_DA_VIBR_B_EN_ADDR, MT6359P_RG_VIBR_VOSEL_ADDR, MT6359_RG_VIBR_VOSEL_MASK << MT6359_RG_VIBR_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vrf12", VRF12, vrf12_voltages, + MT6359_LDO("ldo_vrf12", VRF12, "vs2-ldo2", vrf12_voltages, MT6359P_RG_LDO_VRF12_EN_ADDR, MT6359P_RG_LDO_VRF12_EN_SHIFT, MT6359P_DA_VRF12_B_EN_ADDR, MT6359P_RG_VRF12_VOSEL_ADDR, MT6359_RG_VRF12_VOSEL_MASK << MT6359_RG_VRF12_VOSEL_SHIFT, 480), - MT6359_REG_FIXED("ldo_vusb", VUSB, MT6359P_RG_LDO_VUSB_EN_0_ADDR, + MT6359_REG_FIXED("ldo_vusb", VUSB, "vsys-ldo2", MT6359P_RG_LDO_VUSB_EN_0_ADDR, MT6359P_DA_VUSB_B_EN_ADDR, 3000000), - MT6359_LDO_LINEAR("ldo_vsram_proc2", VSRAM_PROC2, 500000, 1293750, 6250, + MT6359_LDO_LINEAR("ldo_vsram_proc2", VSRAM_PROC2, "vs2-ldo1", 500000, 1293750, 6250, MT6359P_RG_LDO_VSRAM_PROC2_EN_ADDR, MT6359P_DA_VSRAM_PROC2_B_EN_ADDR, MT6359P_RG_LDO_VSRAM_PROC2_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_PROC2_VOSEL_MASK << MT6359_RG_LDO_VSRAM_PROC2_VOSEL_SHIFT), - MT6359_LDO("ldo_vio18", VIO18, volt18_voltages, + MT6359_LDO("ldo_vio18", VIO18, "vs1-ldo2", volt18_voltages, MT6359P_RG_LDO_VIO18_EN_ADDR, MT6359P_RG_LDO_VIO18_EN_SHIFT, MT6359P_DA_VIO18_B_EN_ADDR, MT6359P_RG_VIO18_VOSEL_ADDR, MT6359_RG_VIO18_VOSEL_MASK << MT6359_RG_VIO18_VOSEL_SHIFT, 960), - MT6359_LDO("ldo_vcamio", VCAMIO, volt18_voltages, + MT6359_LDO("ldo_vcamio", VCAMIO, "vs1-ldo1", volt18_voltages, MT6359P_RG_LDO_VCAMIO_EN_ADDR, MT6359P_RG_LDO_VCAMIO_EN_SHIFT, MT6359P_DA_VCAMIO_B_EN_ADDR, MT6359P_RG_VCAMIO_VOSEL_ADDR, MT6359_RG_VCAMIO_VOSEL_MASK << MT6359_RG_VCAMIO_VOSEL_SHIFT, 1290), - MT6359_REG_FIXED("ldo_vcn18", VCN18, MT6359P_RG_LDO_VCN18_EN_ADDR, + MT6359_REG_FIXED("ldo_vcn18", VCN18, "vs1-ldo2", MT6359P_RG_LDO_VCN18_EN_ADDR, MT6359P_DA_VCN18_B_EN_ADDR, 1800000), - MT6359_REG_FIXED("ldo_vfe28", VFE28, MT6359P_RG_LDO_VFE28_EN_ADDR, + MT6359_REG_FIXED("ldo_vfe28", VFE28, "vsys-ldo1", MT6359P_RG_LDO_VFE28_EN_ADDR, MT6359P_DA_VFE28_B_EN_ADDR, 2800000), - MT6359_LDO("ldo_vcn13", VCN13, vcn13_voltages, + MT6359_LDO("ldo_vcn13", VCN13, "vs2-ldo2", vcn13_voltages, MT6359P_RG_LDO_VCN13_EN_ADDR, MT6359P_RG_LDO_VCN13_EN_SHIFT, MT6359P_DA_VCN13_B_EN_ADDR, MT6359P_RG_VCN13_VOSEL_ADDR, MT6359_RG_VCN13_VOSEL_MASK << MT6359_RG_VCN13_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vcn33_1_bt", VCN33_1_BT, vcn33_voltages, + MT6359_LDO("ldo_vcn33_1", VCN33_1, "vsys-ldo1", vcn33_voltages, MT6359P_RG_LDO_VCN33_1_EN_0_ADDR, MT6359_RG_LDO_VCN33_1_EN_0_SHIFT, MT6359P_DA_VCN33_1_B_EN_ADDR, MT6359P_RG_VCN33_1_VOSEL_ADDR, MT6359_RG_VCN33_1_VOSEL_MASK << MT6359_RG_VCN33_1_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vcn33_1_wifi", VCN33_1_WIFI, vcn33_voltages, - MT6359P_RG_LDO_VCN33_1_EN_1_ADDR, - MT6359P_RG_LDO_VCN33_1_EN_1_SHIFT, - MT6359P_DA_VCN33_1_B_EN_ADDR, MT6359P_RG_VCN33_1_VOSEL_ADDR, - MT6359_RG_VCN33_1_VOSEL_MASK << - MT6359_RG_VCN33_1_VOSEL_SHIFT, 240), - MT6359_REG_FIXED("ldo_vaux18", VAUX18, MT6359P_RG_LDO_VAUX18_EN_ADDR, + MT6359_REG_FIXED("ldo_vaux18", VAUX18, "vsys-ldo2", MT6359P_RG_LDO_VAUX18_EN_ADDR, MT6359P_DA_VAUX18_B_EN_ADDR, 1800000), - MT6359_LDO_LINEAR("ldo_vsram_others", VSRAM_OTHERS, 500000, 1293750, + MT6359_LDO_LINEAR("ldo_vsram_others", VSRAM_OTHERS, "vs2-ldo1", 500000, 1293750, 6250, MT6359P_RG_LDO_VSRAM_OTHERS_EN_ADDR, MT6359P_DA_VSRAM_OTHERS_B_EN_ADDR, MT6359P_RG_LDO_VSRAM_OTHERS_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_OTHERS_VOSEL_MASK << MT6359_RG_LDO_VSRAM_OTHERS_VOSEL_SHIFT), - MT6359_LDO("ldo_vefuse", VEFUSE, vefuse_voltages, + MT6359_LDO("ldo_vefuse", VEFUSE, "vs1-ldo2", vefuse_voltages, MT6359P_RG_LDO_VEFUSE_EN_ADDR, MT6359P_RG_LDO_VEFUSE_EN_SHIFT, MT6359P_DA_VEFUSE_B_EN_ADDR, MT6359P_RG_VEFUSE_VOSEL_ADDR, MT6359_RG_VEFUSE_VOSEL_MASK << MT6359_RG_VEFUSE_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vxo22", VXO22, vxo22_voltages, + MT6359_LDO("ldo_vxo22", VXO22, "vsys-ldo2", vxo22_voltages, MT6359P_RG_LDO_VXO22_EN_ADDR, MT6359P_RG_LDO_VXO22_EN_SHIFT, MT6359P_DA_VXO22_B_EN_ADDR, MT6359P_RG_VXO22_VOSEL_ADDR, MT6359_RG_VXO22_VOSEL_MASK << MT6359_RG_VXO22_VOSEL_SHIFT, 480), - MT6359_LDO("ldo_vrfck_1", VRFCK, vrfck_voltages_1, + MT6359_LDO("ldo_vrfck_1", VRFCK, "vsys-ldo2", vrfck_voltages_1, MT6359P_RG_LDO_VRFCK_EN_ADDR, MT6359P_RG_LDO_VRFCK_EN_SHIFT, MT6359P_DA_VRFCK_B_EN_ADDR, MT6359P_RG_VRFCK_VOSEL_ADDR, MT6359_RG_VRFCK_VOSEL_MASK << MT6359_RG_VRFCK_VOSEL_SHIFT, 480), - MT6359_REG_FIXED("ldo_vbif28", VBIF28, MT6359P_RG_LDO_VBIF28_EN_ADDR, + MT6359_REG_FIXED("ldo_vbif28", VBIF28, "vsys-ldo2", MT6359P_RG_LDO_VBIF28_EN_ADDR, MT6359P_DA_VBIF28_B_EN_ADDR, 2800000), - MT6359_LDO("ldo_vio28", VIO28, vio28_voltages, + MT6359_LDO("ldo_vio28", VIO28, "vsys-ldo2", vio28_voltages, MT6359P_RG_LDO_VIO28_EN_ADDR, MT6359P_RG_LDO_VIO28_EN_SHIFT, MT6359P_DA_VIO28_B_EN_ADDR, MT6359P_RG_VIO28_VOSEL_ADDR, MT6359_RG_VIO28_VOSEL_MASK << MT6359_RG_VIO28_VOSEL_SHIFT, 1920), - MT6359P_LDO1("ldo_vemc_1", VEMC, mt6359p_vemc_ops, vemc_voltages_1, + MT6359P_LDO1("ldo_vemc_1", VEMC, "vsys-ldo2", mt6359p_vemc_ops, vemc_voltages_1, MT6359P_RG_LDO_VEMC_EN_ADDR, MT6359P_RG_LDO_VEMC_EN_SHIFT, MT6359P_DA_VEMC_B_EN_ADDR, MT6359P_RG_LDO_VEMC_VOSEL_0_ADDR, MT6359P_RG_LDO_VEMC_VOSEL_0_MASK << MT6359P_RG_LDO_VEMC_VOSEL_0_SHIFT), - MT6359_LDO("ldo_vcn33_2_bt", VCN33_2_BT, vcn33_voltages, + MT6359_LDO("ldo_vcn33_2", VCN33_2, "vsys-ldo1", vcn33_voltages, MT6359P_RG_LDO_VCN33_2_EN_0_ADDR, MT6359P_RG_LDO_VCN33_2_EN_0_SHIFT, MT6359P_DA_VCN33_2_B_EN_ADDR, MT6359P_RG_VCN33_2_VOSEL_ADDR, MT6359_RG_VCN33_2_VOSEL_MASK << MT6359_RG_VCN33_2_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_vcn33_2_wifi", VCN33_2_WIFI, vcn33_voltages, - MT6359P_RG_LDO_VCN33_2_EN_1_ADDR, - MT6359_RG_LDO_VCN33_2_EN_1_SHIFT, - MT6359P_DA_VCN33_2_B_EN_ADDR, MT6359P_RG_VCN33_2_VOSEL_ADDR, - MT6359_RG_VCN33_2_VOSEL_MASK << - MT6359_RG_VCN33_2_VOSEL_SHIFT, 240), - MT6359_LDO("ldo_va12", VA12, va12_voltages, + MT6359_LDO("ldo_va12", VA12, "vs2-ldo2", va12_voltages, MT6359P_RG_LDO_VA12_EN_ADDR, MT6359P_RG_LDO_VA12_EN_SHIFT, MT6359P_DA_VA12_B_EN_ADDR, MT6359P_RG_VA12_VOSEL_ADDR, MT6359_RG_VA12_VOSEL_MASK << MT6359_RG_VA12_VOSEL_SHIFT, 960), - MT6359_LDO("ldo_va09", VA09, va09_voltages, + MT6359_LDO("ldo_va09", VA09, "vs2-ldo2", va09_voltages, MT6359P_RG_LDO_VA09_EN_ADDR, MT6359P_RG_LDO_VA09_EN_SHIFT, MT6359P_DA_VA09_B_EN_ADDR, MT6359P_RG_VA09_VOSEL_ADDR, MT6359_RG_VA09_VOSEL_MASK << MT6359_RG_VA09_VOSEL_SHIFT, 960), - MT6359_LDO("ldo_vrf18", VRF18, vrf18_voltages, + MT6359_LDO("ldo_vrf18", VRF18, "vs1-ldo2", vrf18_voltages, MT6359P_RG_LDO_VRF18_EN_ADDR, MT6359P_RG_LDO_VRF18_EN_SHIFT, MT6359P_DA_VRF18_B_EN_ADDR, MT6359P_RG_VRF18_VOSEL_ADDR, MT6359_RG_VRF18_VOSEL_MASK << MT6359_RG_VRF18_VOSEL_SHIFT, 240), - MT6359_LDO_LINEAR("ldo_vsram_md", VSRAM_MD, 500000, 1293750, 6250, + MT6359_LDO_LINEAR("ldo_vsram_md", VSRAM_MD, "vs2-ldo1", 500000, 1293750, 6250, MT6359P_RG_LDO_VSRAM_MD_EN_ADDR, MT6359P_DA_VSRAM_MD_B_EN_ADDR, MT6359P_RG_LDO_VSRAM_MD_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_MD_VOSEL_MASK << MT6359_RG_LDO_VSRAM_MD_VOSEL_SHIFT), - MT6359_LDO("ldo_vufs", VUFS, volt18_voltages, + MT6359_LDO("ldo_vufs", VUFS, "vs1-ldo1", volt18_voltages, MT6359P_RG_LDO_VUFS_EN_ADDR, MT6359P_RG_LDO_VUFS_EN_SHIFT, MT6359P_DA_VUFS_B_EN_ADDR, MT6359P_RG_VUFS_VOSEL_ADDR, MT6359_RG_VUFS_VOSEL_MASK << MT6359_RG_VUFS_VOSEL_SHIFT, 1920), - MT6359_LDO("ldo_vm18", VM18, volt18_voltages, + MT6359_LDO("ldo_vm18", VM18, "vs1-ldo1", volt18_voltages, MT6359P_RG_LDO_VM18_EN_ADDR, MT6359P_RG_LDO_VM18_EN_SHIFT, MT6359P_DA_VM18_B_EN_ADDR, MT6359P_RG_VM18_VOSEL_ADDR, MT6359_RG_VM18_VOSEL_MASK << MT6359_RG_VM18_VOSEL_SHIFT, 1920), - MT6359_LDO("ldo_vbbck", VBBCK, vbbck_voltages, + /* vbbck is fed from vio18 internally. */ + MT6359_LDO("ldo_vbbck", VBBCK, "LDO_VIO18", vbbck_voltages, MT6359P_RG_LDO_VBBCK_EN_ADDR, MT6359P_RG_LDO_VBBCK_EN_SHIFT, MT6359P_DA_VBBCK_B_EN_ADDR, MT6359P_RG_VBBCK_VOSEL_ADDR, MT6359P_RG_VBBCK_VOSEL_MASK << MT6359P_RG_VBBCK_VOSEL_SHIFT, 480), - MT6359_LDO_LINEAR("ldo_vsram_proc1", VSRAM_PROC1, 500000, 1293750, 6250, + MT6359_LDO_LINEAR("ldo_vsram_proc1", VSRAM_PROC1, "vs2-ldo1", 500000, 1293750, 6250, MT6359P_RG_LDO_VSRAM_PROC1_EN_ADDR, MT6359P_DA_VSRAM_PROC1_B_EN_ADDR, MT6359P_RG_LDO_VSRAM_PROC1_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_PROC1_VOSEL_MASK << MT6359_RG_LDO_VSRAM_PROC1_VOSEL_SHIFT), - MT6359_LDO("ldo_vsim2", VSIM2, vsim2_voltages, + MT6359_LDO("ldo_vsim2", VSIM2, "vsys-ldo2", vsim2_voltages, MT6359P_RG_LDO_VSIM2_EN_ADDR, MT6359P_RG_LDO_VSIM2_EN_SHIFT, MT6359P_DA_VSIM2_B_EN_ADDR, MT6359P_RG_VSIM2_VOSEL_ADDR, MT6359_RG_VSIM2_VOSEL_MASK << MT6359_RG_VSIM2_VOSEL_SHIFT, 480), - MT6359_LDO_LINEAR("ldo_vsram_others_sshub", VSRAM_OTHERS_SSHUB, + MT6359_LDO_LINEAR("ldo_vsram_others_sshub", VSRAM_OTHERS_SSHUB, "vs2-ldo1", 500000, 1293750, 6250, MT6359P_RG_LDO_VSRAM_OTHERS_SSHUB_EN_ADDR, MT6359P_DA_VSRAM_OTHERS_B_EN_ADDR, MT6359P_RG_LDO_VSRAM_OTHERS_SSHUB_VOSEL_ADDR, MT6359_RG_LDO_VSRAM_OTHERS_SSHUB_VOSEL_MASK << MT6359_RG_LDO_VSRAM_OTHERS_SSHUB_VOSEL_SHIFT), + /* Placeholders for DT backward compatibility */ + MT6359_LDO_NOOP("ldo_vcn33_1_bt", VCN33_1_BT, "LDO_VCN33_1"), + MT6359_LDO_NOOP("ldo_vcn33_1_wifi", VCN33_1_WIFI, "LDO_VCN33_1"), + MT6359_LDO_NOOP("ldo_vcn33_2_bt", VCN33_2_BT, "LDO_VCN33_2"), + MT6359_LDO_NOOP("ldo_vcn33_2_wifi", VCN33_2_WIFI, "LDO_VCN33_2"), +}; + +struct mt6359_vcn33_regs { + u32 wifi_en_reg; + u32 wifi_en_mask; + u32 bt_en_reg; + u32 bt_en_mask; +}; + +static const struct mt6359_vcn33_regs vcn33_regs[][2] = { + { /* MT6359 */ + { + .wifi_en_reg = MT6359_RG_LDO_VCN33_1_EN_1_ADDR, + .wifi_en_mask = BIT(MT6359_RG_LDO_VCN33_1_EN_1_SHIFT), + .bt_en_reg = MT6359_RG_LDO_VCN33_1_EN_0_ADDR, + .bt_en_mask = BIT(MT6359_RG_LDO_VCN33_1_EN_0_SHIFT), + }, { + .wifi_en_reg = MT6359_RG_LDO_VCN33_2_EN_1_ADDR, + .wifi_en_mask = BIT(MT6359_RG_LDO_VCN33_2_EN_1_SHIFT), + .bt_en_reg = MT6359_RG_LDO_VCN33_2_EN_0_ADDR, + .bt_en_mask = BIT(MT6359_RG_LDO_VCN33_2_EN_0_SHIFT), + } + }, { /* MT6359P */ + { + .wifi_en_reg = MT6359P_RG_LDO_VCN33_1_EN_1_ADDR, + .wifi_en_mask = BIT(MT6359P_RG_LDO_VCN33_1_EN_1_SHIFT), + .bt_en_reg = MT6359P_RG_LDO_VCN33_1_EN_0_ADDR, + .bt_en_mask = BIT(MT6359_RG_LDO_VCN33_1_EN_0_SHIFT), + }, { + .wifi_en_reg = MT6359P_RG_LDO_VCN33_2_EN_1_ADDR, + .wifi_en_mask = BIT(MT6359_RG_LDO_VCN33_2_EN_1_SHIFT), + .bt_en_reg = MT6359P_RG_LDO_VCN33_2_EN_0_ADDR, + .bt_en_mask = BIT(MT6359P_RG_LDO_VCN33_2_EN_0_SHIFT), + } + } }; +static int mt6359_sync_vcn33_setting(struct device *dev, unsigned int idx) +{ + struct mt6397_chip *mt6397 = dev_get_drvdata(dev->parent); + unsigned int val; + int ret; + + /* + * VCN33_[12]_WIFI and VCN33_[12]_BT are two separate enable bits for + * the same regulator. They share the same voltage setting and output + * pin. Instead of having two potentially conflicting regulators, just + * have one regulator. Sync the two enable bits and only use one in + * the regulator device. + */ + for (unsigned int i = 0; i < ARRAY_SIZE(vcn33_regs[0]); i++) { + u32 bt_en_mask = vcn33_regs[idx][i].bt_en_mask; + u32 wifi_en_mask = vcn33_regs[idx][i].wifi_en_mask; + + ret = regmap_read(mt6397->regmap, vcn33_regs[idx][i].wifi_en_reg, &val); + if (ret) + return dev_err_probe(dev, ret, "Failed to read VCN33_%u_WIFI setting\n", + i + 1); + + if (!(val & wifi_en_mask)) + continue; + + /* Sync VCN33_[12]_WIFI enable status to VCN33_[12]_BT */ + ret = regmap_update_bits(mt6397->regmap, vcn33_regs[idx][i].bt_en_reg, + bt_en_mask, bt_en_mask); + if (ret) + return dev_err_probe(dev, ret, + "Failed to sync VCN33_%u_WIFI setting to VCN33_%u_BT\n", + i + 1, i + 1); + + /* Disable VCN33_[12]_WIFI */ + ret = regmap_update_bits(mt6397->regmap, vcn33_regs[idx][i].wifi_en_reg, + wifi_en_mask, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to disable VCN33_%u_WIFI\n", i + 1); + } + + return 0; +} + static int mt6359_regulator_probe(struct platform_device *pdev) { struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); struct regulator_config config = {}; struct regulator_dev *rdev; - struct mt6359_regulator_info *mt6359_info; + const struct mt6359_regulator_info *mt6359_info; + const char *vio18_name, *vcn33_1_name, *vcn33_2_name; int i, hw_ver, ret; ret = regmap_read(mt6397->regmap, MT6359P_HWCID, &hw_ver); if (ret) return ret; - if (hw_ver >= MT6359P_CHIP_VER) + if (hw_ver >= MT6359P_CHIP_VER) { mt6359_info = mt6359p_regulators; - else + ret = mt6359_sync_vcn33_setting(&pdev->dev, 1); + if (ret) + return ret; + } else { mt6359_info = mt6359_regulators; + ret = mt6359_sync_vcn33_setting(&pdev->dev, 0); + if (ret) + return ret; + } + + vio18_name = mt6359_info[MT6359_ID_VIO18].desc.name; + vcn33_1_name = mt6359_info[MT6359_ID_VCN33_1].desc.name; + vcn33_2_name = mt6359_info[MT6359_ID_VCN33_2].desc.name; config.dev = mt6397->dev; config.regmap = mt6397->regmap; for (i = 0; i < MT6359_MAX_REGULATOR; i++, mt6359_info++) { - config.driver_data = mt6359_info; - rdev = devm_regulator_register(&pdev->dev, &mt6359_info->desc, &config); + const struct regulator_desc *desc = &mt6359_info->desc; + struct regulator_desc *_desc; + + /* drop const here, but all uses in the driver are const */ + config.driver_data = (void *)mt6359_info; + + /* Use vio18's actual name as supply_name for vbbck */ + if (i == MT6359_ID_VBBCK && strcmp(desc->supply_name, vio18_name) != 0) { + _desc = devm_kzalloc(&pdev->dev, sizeof(*_desc), GFP_KERNEL); + if (!_desc) + return -ENOMEM; + + memcpy(_desc, desc, sizeof(*_desc)); + _desc->supply_name = vio18_name; + desc = _desc; + } + + /* Use vcn33_1's actual name as supply_name for vcn33_1_(bt|wifi) */ + if ((i == MT6359_ID_VCN33_1_BT || i == MT6359_ID_VCN33_1_WIFI) && + strcmp(desc->supply_name, vcn33_1_name) != 0) { + _desc = devm_kzalloc(&pdev->dev, sizeof(*_desc), GFP_KERNEL); + if (!_desc) + return -ENOMEM; + + memcpy(_desc, desc, sizeof(*_desc)); + _desc->supply_name = vcn33_1_name; + desc = _desc; + } + + /* Use vcn33_2's actual name as supply_name for vcn33_2_(bt|wifi) */ + if ((i == MT6359_ID_VCN33_2_BT || i == MT6359_ID_VCN33_2_WIFI) && + strcmp(desc->supply_name, vcn33_2_name) != 0) { + _desc = devm_kzalloc(&pdev->dev, sizeof(*_desc), GFP_KERNEL); + if (!_desc) + return -ENOMEM; + + memcpy(_desc, desc, sizeof(*_desc)); + _desc->supply_name = vcn33_2_name; + desc = _desc; + } + + rdev = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s\n", mt6359_info->desc.name); return PTR_ERR(rdev); } + + /* Save vio18 name for vbbck */ + if (i == MT6359_ID_VIO18) + vio18_name = rdev_get_name(rdev); + + /* Save vcn33_1 name for vbbck */ + if (i == MT6359_ID_VCN33_1) + vcn33_1_name = rdev_get_name(rdev); + + /* Save vcn33_2 name for vbbck */ + if (i == MT6359_ID_VCN33_2) + vcn33_2_name = rdev_get_name(rdev); } return 0; } static const struct platform_device_id mt6359_platform_ids[] = { - {"mt6359-regulator", 0}, - { /* sentinel */ }, + { .name = "mt6359-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6359_platform_ids); diff --git a/drivers/regulator/mt6360-regulator.c b/drivers/regulator/mt6360-regulator.c index 24cc9fc94e90..ed25e9603b2b 100644 --- a/drivers/regulator/mt6360-regulator.c +++ b/drivers/regulator/mt6360-regulator.c @@ -446,8 +446,8 @@ static int mt6360_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6360_regulator_id_table[] = { - { "mt6360-regulator", 0 }, - {}, + { .name = "mt6360-regulator" }, + { } }; MODULE_DEVICE_TABLE(platform, mt6360_regulator_id_table); diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c index c2cea904b0ca..a4ac4a42c108 100644 --- a/drivers/regulator/mt6370-regulator.c +++ b/drivers/regulator/mt6370-regulator.c @@ -371,8 +371,8 @@ static int mt6370_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6370_devid_table[] = { - { "mt6370-regulator", 0}, - {} + { .name = "mt6370-regulator" }, + { } }; MODULE_DEVICE_TABLE(platform, mt6370_devid_table); diff --git a/drivers/regulator/mt6380-regulator.c b/drivers/regulator/mt6380-regulator.c index 83e50df7f7c3..da901a75137a 100644 --- a/drivers/regulator/mt6380-regulator.c +++ b/drivers/regulator/mt6380-regulator.c @@ -314,8 +314,8 @@ static int mt6380_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6380_platform_ids[] = { - {"mt6380-regulator", 0}, - { /* sentinel */ }, + { .name = "mt6380-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6380_platform_ids); diff --git a/drivers/regulator/mt6397-regulator.c b/drivers/regulator/mt6397-regulator.c index 92a2d92f84f9..e68df86ca4d9 100644 --- a/drivers/regulator/mt6397-regulator.c +++ b/drivers/regulator/mt6397-regulator.c @@ -392,8 +392,8 @@ static int mt6397_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id mt6397_platform_ids[] = { - {"mt6397-regulator", 0}, - { /* sentinel */ }, + { .name = "mt6397-regulator" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, mt6397_platform_ids); diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 60656a815b9e..f82618a70106 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -1590,6 +1590,7 @@ static const struct of_device_id of_palmas_match_tbl[] = { }, { /* end */ } }; +MODULE_DEVICE_TABLE(of, of_palmas_match_tbl); static int palmas_regulators_probe(struct platform_device *pdev) { @@ -1684,4 +1685,3 @@ MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); MODULE_DESCRIPTION("Palmas voltage regulator driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:palmas-pmic"); -MODULE_DEVICE_TABLE(of, of_palmas_match_tbl); diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c deleted file mode 100644 index 441c9344aef7..000000000000 --- a/drivers/regulator/pcap-regulator.c +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * PCAP2 Regulator Driver - * - * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/platform_device.h> -#include <linux/regulator/driver.h> -#include <linux/regulator/machine.h> -#include <linux/mfd/ezx-pcap.h> - -static const unsigned int V1_table[] = { - 2775000, 1275000, 1600000, 1725000, 1825000, 1925000, 2075000, 2275000, -}; - -static const unsigned int V2_table[] = { - 2500000, 2775000, -}; - -static const unsigned int V3_table[] = { - 1075000, 1275000, 1550000, 1725000, 1876000, 1950000, 2075000, 2275000, -}; - -static const unsigned int V4_table[] = { - 1275000, 1550000, 1725000, 1875000, 1950000, 2075000, 2275000, 2775000, -}; - -static const unsigned int V5_table[] = { - 1875000, 2275000, 2475000, 2775000, -}; - -static const unsigned int V6_table[] = { - 2475000, 2775000, -}; - -static const unsigned int V7_table[] = { - 1875000, 2775000, -}; - -#define V8_table V4_table - -static const unsigned int V9_table[] = { - 1575000, 1875000, 2475000, 2775000, -}; - -static const unsigned int V10_table[] = { - 5000000, -}; - -static const unsigned int VAUX1_table[] = { - 1875000, 2475000, 2775000, 3000000, -}; - -#define VAUX2_table VAUX1_table - -static const unsigned int VAUX3_table[] = { - 1200000, 1200000, 1200000, 1200000, 1400000, 1600000, 1800000, 2000000, - 2200000, 2400000, 2600000, 2800000, 3000000, 3200000, 3400000, 3600000, -}; - -static const unsigned int VAUX4_table[] = { - 1800000, 1800000, 3000000, 5000000, -}; - -static const unsigned int VSIM_table[] = { - 1875000, 3000000, -}; - -static const unsigned int VSIM2_table[] = { - 1875000, -}; - -static const unsigned int VVIB_table[] = { - 1300000, 1800000, 2000000, 3000000, -}; - -static const unsigned int SW1_table[] = { - 900000, 950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, - 1300000, 1350000, 1400000, 1450000, 1500000, 1600000, 1875000, 2250000, -}; - -#define SW2_table SW1_table - -struct pcap_regulator { - const u8 reg; - const u8 en; - const u8 index; - const u8 stby; - const u8 lowpwr; -}; - -#define NA 0xff - -#define VREG_INFO(_vreg, _reg, _en, _index, _stby, _lowpwr) \ - [_vreg] = { \ - .reg = _reg, \ - .en = _en, \ - .index = _index, \ - .stby = _stby, \ - .lowpwr = _lowpwr, \ - } - -static const struct pcap_regulator vreg_table[] = { - VREG_INFO(V1, PCAP_REG_VREG1, 1, 2, 18, 0), - VREG_INFO(V2, PCAP_REG_VREG1, 5, 6, 19, 22), - VREG_INFO(V3, PCAP_REG_VREG1, 7, 8, 20, 23), - VREG_INFO(V4, PCAP_REG_VREG1, 11, 12, 21, 24), - /* V5 STBY and LOWPWR are on PCAP_REG_VREG2 */ - VREG_INFO(V5, PCAP_REG_VREG1, 15, 16, 12, 19), - - VREG_INFO(V6, PCAP_REG_VREG2, 1, 2, 14, 20), - VREG_INFO(V7, PCAP_REG_VREG2, 3, 4, 15, 21), - VREG_INFO(V8, PCAP_REG_VREG2, 5, 6, 16, 22), - VREG_INFO(V9, PCAP_REG_VREG2, 9, 10, 17, 23), - VREG_INFO(V10, PCAP_REG_VREG2, 10, NA, 18, 24), - - VREG_INFO(VAUX1, PCAP_REG_AUXVREG, 1, 2, 22, 23), - /* VAUX2 ... VSIM2 STBY and LOWPWR are on PCAP_REG_LOWPWR */ - VREG_INFO(VAUX2, PCAP_REG_AUXVREG, 4, 5, 0, 1), - VREG_INFO(VAUX3, PCAP_REG_AUXVREG, 7, 8, 2, 3), - VREG_INFO(VAUX4, PCAP_REG_AUXVREG, 12, 13, 4, 5), - VREG_INFO(VSIM, PCAP_REG_AUXVREG, 17, 18, NA, 6), - VREG_INFO(VSIM2, PCAP_REG_AUXVREG, 16, NA, NA, 7), - VREG_INFO(VVIB, PCAP_REG_AUXVREG, 19, 20, NA, NA), - - VREG_INFO(SW1, PCAP_REG_SWCTRL, 1, 2, NA, NA), - VREG_INFO(SW2, PCAP_REG_SWCTRL, 6, 7, NA, NA), - /* SW3 STBY is on PCAP_REG_AUXVREG */ - VREG_INFO(SW3, PCAP_REG_SWCTRL, 11, 12, 24, NA), - - /* SWxS used to control SWx voltage on standby */ -/* VREG_INFO(SW1S, PCAP_REG_LOWPWR, NA, 12, NA, NA), - VREG_INFO(SW2S, PCAP_REG_LOWPWR, NA, 20, NA, NA), */ -}; - -static int pcap_regulator_set_voltage_sel(struct regulator_dev *rdev, - unsigned selector) -{ - const struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; - void *pcap = rdev_get_drvdata(rdev); - - /* the regulator doesn't support voltage switching */ - if (rdev->desc->n_voltages == 1) - return -EINVAL; - - return ezx_pcap_set_bits(pcap, vreg->reg, - (rdev->desc->n_voltages - 1) << vreg->index, - selector << vreg->index); -} - -static int pcap_regulator_get_voltage_sel(struct regulator_dev *rdev) -{ - const struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; - void *pcap = rdev_get_drvdata(rdev); - u32 tmp; - - if (rdev->desc->n_voltages == 1) - return 0; - - ezx_pcap_read(pcap, vreg->reg, &tmp); - tmp = ((tmp >> vreg->index) & (rdev->desc->n_voltages - 1)); - return tmp; -} - -static int pcap_regulator_enable(struct regulator_dev *rdev) -{ - const struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; - void *pcap = rdev_get_drvdata(rdev); - - if (vreg->en == NA) - return -EINVAL; - - return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 1 << vreg->en); -} - -static int pcap_regulator_disable(struct regulator_dev *rdev) -{ - const struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; - void *pcap = rdev_get_drvdata(rdev); - - if (vreg->en == NA) - return -EINVAL; - - return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 0); -} - -static int pcap_regulator_is_enabled(struct regulator_dev *rdev) -{ - const struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; - void *pcap = rdev_get_drvdata(rdev); - u32 tmp; - - if (vreg->en == NA) - return -EINVAL; - - ezx_pcap_read(pcap, vreg->reg, &tmp); - return (tmp >> vreg->en) & 1; -} - -static const struct regulator_ops pcap_regulator_ops = { - .list_voltage = regulator_list_voltage_table, - .set_voltage_sel = pcap_regulator_set_voltage_sel, - .get_voltage_sel = pcap_regulator_get_voltage_sel, - .enable = pcap_regulator_enable, - .disable = pcap_regulator_disable, - .is_enabled = pcap_regulator_is_enabled, -}; - -#define VREG(_vreg) \ - [_vreg] = { \ - .name = #_vreg, \ - .id = _vreg, \ - .n_voltages = ARRAY_SIZE(_vreg##_table), \ - .volt_table = _vreg##_table, \ - .ops = &pcap_regulator_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - } - -static const struct regulator_desc pcap_regulators[] = { - VREG(V1), VREG(V2), VREG(V3), VREG(V4), VREG(V5), VREG(V6), VREG(V7), - VREG(V8), VREG(V9), VREG(V10), VREG(VAUX1), VREG(VAUX2), VREG(VAUX3), - VREG(VAUX4), VREG(VSIM), VREG(VSIM2), VREG(VVIB), VREG(SW1), VREG(SW2), -}; - -static int pcap_regulator_probe(struct platform_device *pdev) -{ - struct regulator_dev *rdev; - void *pcap = dev_get_drvdata(pdev->dev.parent); - struct regulator_config config = { }; - - config.dev = &pdev->dev; - config.init_data = dev_get_platdata(&pdev->dev); - config.driver_data = pcap; - - rdev = devm_regulator_register(&pdev->dev, &pcap_regulators[pdev->id], - &config); - if (IS_ERR(rdev)) - return PTR_ERR(rdev); - - platform_set_drvdata(pdev, rdev); - - return 0; -} - -static struct platform_driver pcap_regulator_driver = { - .driver = { - .name = "pcap-regulator", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, - .probe = pcap_regulator_probe, -}; - -static int __init pcap_regulator_init(void) -{ - return platform_driver_register(&pcap_regulator_driver); -} - -static void __exit pcap_regulator_exit(void) -{ - platform_driver_unregister(&pcap_regulator_driver); -} - -subsys_initcall(pcap_regulator_init); -module_exit(pcap_regulator_exit); - -MODULE_AUTHOR("Daniel Ribeiro <drwyrm@gmail.com>"); -MODULE_DESCRIPTION("PCAP2 Regulator Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/pf1550-regulator.c b/drivers/regulator/pf1550-regulator.c index 1d1726528460..610eac9bb9cb 100644 --- a/drivers/regulator/pf1550-regulator.c +++ b/drivers/regulator/pf1550-regulator.c @@ -409,7 +409,7 @@ static int pf1550_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id pf1550_regulator_id[] = { - { "pf1550-regulator", }, + { .name = "pf1550-regulator" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, pf1550_regulator_id); diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c index 90c78ee1c37b..9c9b8be2e15a 100644 --- a/drivers/regulator/qcom-pm8008-regulator.c +++ b/drivers/regulator/qcom-pm8008-regulator.c @@ -180,7 +180,7 @@ static int pm8008_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id pm8008_regulator_id_table[] = { - { "pm8008-regulator" }, + { .name = "pm8008-regulator" }, { } }; MODULE_DEVICE_TABLE(platform, pm8008_regulator_id_table); diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 0dcb50bf5c35..756a4201225e 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1100,6 +1100,21 @@ static const struct rpmh_vreg_init_data pm8998_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmau0102_vreg_data[] = { + RPMH_VREG("smps1", SMPS, 1, &pmic5_ftsmps527, "vdd-s1"), + RPMH_VREG("smps2", SMPS, 2, &pmic5_ftsmps527, "vdd-s2"), + RPMH_VREG("smps3", SMPS, 3, &pmic5_ftsmps527, "vdd-s3"), + RPMH_VREG("smps4", SMPS, 4, &pmic5_ftsmps527, "vdd-s4"), + RPMH_VREG("smps5", SMPS, 5, &pmic5_ftsmps527, "vdd-s5"), + RPMH_VREG("smps6", SMPS, 6, &pmic5_ftsmps527, "vdd-s6"), + RPMH_VREG("smps7", SMPS, 7, &pmic5_ftsmps527, "vdd-s7"), + RPMH_VREG("smps8", SMPS, 8, &pmic5_ftsmps527, "vdd-s8"), + RPMH_VREG("ldo1", LDO, 1, &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", LDO, 2, &pmic5_nldo515, "vdd-l2"), + RPMH_VREG("ldo3", LDO, 3, &pmic5_pldo515_mv, "vdd-l3"), + {} +}; + static const struct rpmh_vreg_init_data pmg1110_vreg_data[] = { RPMH_VREG("smps1", SMPS, 1, &pmic5_ftsmps510, "vdd-s1"), {} @@ -1878,6 +1893,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .data = pm8998_vreg_data, }, { + .compatible = "qcom,pmau0102-rpmh-regulators", + .data = pmau0102_vreg_data, + }, + { .compatible = "qcom,pmg1110-rpmh-regulators", .data = pmg1110_vreg_data, }, diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index 25ed9f713974..23ed594b574e 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -913,6 +913,60 @@ static const struct rpm_regulator_data rpm_pm660l_regulators[] = { { } }; +static const struct rpm_regulator_data rpm_pm8019_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pma8084_hfsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pma8084_hfsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pma8084_hfsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPA, 4, &pma8084_hfsmps, "vdd_s4" }, + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8916_nldo, "vdd_l1" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pma8084_pldo, "vdd_l2_l3" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pma8084_pldo, "vdd_l2_l3" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pma8084_pldo, "vdd_l4_l5_l6" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pma8084_pldo, "vdd_l4_l5_l6" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pma8084_pldo, "vdd_l4_l5_l6" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pma8084_pldo, "vdd_l7_l8_l11" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pma8084_pldo, "vdd_l7_l8_l11" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8916_nldo, "vdd_l9" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8916_nldo, "vdd_l10"}, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pma8084_pldo, "vdd_l7_l8_l11"}, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8916_nldo, "vdd_l12"}, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pma8084_pldo, "vdd_l13_l14"}, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pma8084_pldo, "vdd_l13_l14"}, + {} +}; + +static const struct rpm_regulator_data rpm_pm8150_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pmic5_ftsmps520, "vdd-s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pmic5_ftsmps520, "vdd-s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pmic5_ftsmps520, "vdd-s3" }, + { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8998_hfsmps, "vdd-s4" }, + { "s5", QCOM_SMD_RPM_SMPA, 5, &pm8998_hfsmps, "vdd-s5" }, + { "s6", QCOM_SMD_RPM_SMPA, 6, &pmic5_ftsmps520, "vdd-s6" }, + { "s7", QCOM_SMD_RPM_SMPA, 7, &pmic5_ftsmps520, "vdd-s7" }, + { "s8", QCOM_SMD_RPM_SMPA, 8, &pmic5_ftsmps520, "vdd-s8" }, + { "s9", QCOM_SMD_RPM_SMPA, 9, &pmic5_ftsmps520, "vdd-s9" }, + { "s10", QCOM_SMD_RPM_SMPA, 10, &pmic5_ftsmps520, "vdd-s10" }, + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm660_nldo660, "vdd-l1-l8-l11" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pm660_pldo660, "vdd-l2-l10" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pm660_nldo660, "vdd-l3-l4-l5-l18" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pm660_nldo660, "vdd-l3-l4-l5-l18" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pm660_nldo660, "vdd-l3-l4-l5-l18" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pm660_nldo660, "vdd-l6-l9" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pm660_pldo660, "vdd-l7-l12-l14-l15" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pm660_nldo660, "vdd-l1-l8-l11" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm660_nldo660, "vdd-l6-l9" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm660_pldo660, "vdd-l2-l10" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pm660_nldo660, "vdd-l1-l8-l11" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm660_ht_lvpldo, "vdd-l7-l12-l14-l15" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pm660_pldo660, "vdd-l13-l16-l17" }, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pm660_ht_lvpldo, "vdd-l7-l12-l14-l15" }, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pm660_ht_lvpldo, "vdd-l7-l12-l14-l15" }, + { "l16", QCOM_SMD_RPM_LDOA, 16, &pm660_pldo660, "vdd-l13-l16-l17" }, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pm660_pldo660, "vdd-l13-l16-l17" }, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pm660_nldo660, "vdd-l3-l4-l5-l18" }, + { } +}; + static const struct rpm_regulator_data rpm_pm8226_regulators[] = { { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8226_hfsmps, "vdd_s1" }, { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8226_ftsmps, "vdd_s2" }, @@ -1358,6 +1412,8 @@ static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm6125-regulators", .data = &rpm_pm6125_regulators }, { .compatible = "qcom,rpm-pm660-regulators", .data = &rpm_pm660_regulators }, { .compatible = "qcom,rpm-pm660l-regulators", .data = &rpm_pm660l_regulators }, + { .compatible = "qcom,rpm-pm8019-regulators", .data = &rpm_pm8019_regulators }, + { .compatible = "qcom,rpm-pm8150-regulators", .data = &rpm_pm8150_regulators }, { .compatible = "qcom,rpm-pm8226-regulators", .data = &rpm_pm8226_regulators }, { .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators }, { .compatible = "qcom,rpm-pm8909-regulators", .data = &rpm_pm8909_regulators }, diff --git a/drivers/regulator/rt4831-regulator.c b/drivers/regulator/rt4831-regulator.c index dfc868a24056..5c47fd57fdef 100644 --- a/drivers/regulator/rt4831-regulator.c +++ b/drivers/regulator/rt4831-regulator.c @@ -186,8 +186,8 @@ static int rt4831_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id rt4831_regulator_match[] = { - { "rt4831-regulator", 0 }, - {} + { .name = "rt4831-regulator" }, + { } }; MODULE_DEVICE_TABLE(platform, rt4831_regulator_match); diff --git a/drivers/regulator/rt5033-regulator.c b/drivers/regulator/rt5033-regulator.c index 2ba74f205543..3aeab9a57871 100644 --- a/drivers/regulator/rt5033-regulator.c +++ b/drivers/regulator/rt5033-regulator.c @@ -116,7 +116,7 @@ static int rt5033_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id rt5033_regulator_id[] = { - { "rt5033-regulator", }, + { .name = "rt5033-regulator" }, { } }; MODULE_DEVICE_TABLE(platform, rt5033_regulator_id); diff --git a/drivers/regulator/rt5120-regulator.c b/drivers/regulator/rt5120-regulator.c index f0d3efd160d4..a2a13ce2e67b 100644 --- a/drivers/regulator/rt5120-regulator.c +++ b/drivers/regulator/rt5120-regulator.c @@ -401,8 +401,8 @@ static int rt5120_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id rt5120_regulator_dev_table[] = { - { "rt5120-regulator", 0 }, - {} + { .name = "rt5120-regulator" }, + { } }; MODULE_DEVICE_TABLE(platform, rt5120_regulator_dev_table); diff --git a/drivers/regulator/s2dos05-regulator.c b/drivers/regulator/s2dos05-regulator.c index a1c394ddbaff..6e25f663496a 100644 --- a/drivers/regulator/s2dos05-regulator.c +++ b/drivers/regulator/s2dos05-regulator.c @@ -146,8 +146,8 @@ static int s2dos05_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id s2dos05_pmic_id[] = { - { "s2dos05-regulator" }, - { }, + { .name = "s2dos05-regulator" }, + { } }; MODULE_DEVICE_TABLE(platform, s2dos05_pmic_id); diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index c22fdde67f9c..8171503861c1 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c @@ -367,8 +367,8 @@ static int s2mpa01_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id s2mpa01_pmic_id[] = { - { "s2mpa01-pmic", 0}, - { }, + { .name = "s2mpa01-pmic" }, + { } }; MODULE_DEVICE_TABLE(platform, s2mpa01_pmic_id); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 81cfd60460f8..0fb54617b8a7 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -2266,15 +2266,15 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id s2mps11_pmic_id[] = { - { "s2mpg10-regulator", S2MPG10}, - { "s2mpg11-regulator", S2MPG11}, - { "s2mps11-regulator", S2MPS11X}, - { "s2mps13-regulator", S2MPS13X}, - { "s2mps14-regulator", S2MPS14X}, - { "s2mps15-regulator", S2MPS15X}, - { "s2mpu02-regulator", S2MPU02}, - { "s2mpu05-regulator", S2MPU05}, - { }, + { .name = "s2mpg10-regulator", .driver_data = S2MPG10 }, + { .name = "s2mpg11-regulator", .driver_data = S2MPG11 }, + { .name = "s2mps11-regulator", .driver_data = S2MPS11X }, + { .name = "s2mps13-regulator", .driver_data = S2MPS13X }, + { .name = "s2mps14-regulator", .driver_data = S2MPS14X }, + { .name = "s2mps15-regulator", .driver_data = S2MPS15X }, + { .name = "s2mpu02-regulator", .driver_data = S2MPU02 }, + { .name = "s2mpu05-regulator", .driver_data = S2MPU05 }, + { } }; MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index fe2631378ccd..2794245d4042 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -916,8 +916,8 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id s5m8767_pmic_id[] = { - { "s5m8767-pmic", 0}, - { }, + { .name = "s5m8767-pmic" }, + { } }; MODULE_DEVICE_TABLE(platform, s5m8767_pmic_id); diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c index 6d609c42e479..c005e65ba0ec 100644 --- a/drivers/regulator/scmi-regulator.c +++ b/drivers/regulator/scmi-regulator.c @@ -345,8 +345,10 @@ static int scmi_regulator_probe(struct scmi_device *sdev) for_each_child_of_node_scoped(np, child) { ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo); /* abort on any mem issue */ - if (ret == -ENOMEM) + if (ret == -ENOMEM) { + of_node_put(np); return ret; + } } of_node_put(np); /* diff --git a/drivers/regulator/sgm3804-regulator.c b/drivers/regulator/sgm3804-regulator.c new file mode 100644 index 000000000000..c3406cfb73d0 --- /dev/null +++ b/drivers/regulator/sgm3804-regulator.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// SGMicro SGM3804 regulator Driver +// +// Copyright (C) 2025 Kancy Joe <kancy2333@outlook.com> +// Copyright (C) 2026 Linaro Limited +// Author: Neil Armstrong <neil.armstrong@linaro.org> + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> +#include <linux/gpio/consumer.h> + +#define SGM3804_POS_RAIL_VOLTAGE_REG 0x0 +#define SGM3804_NEG_RAIL_VOLTAGE_REG 0x1 +#define SGM3804_RAIL_DISCHARGE_REG 0x3 + +#define RAIL_VOLTAGE_MASK GENMASK(5, 0) + +#define POS_RAIL_DISCHARGE_EN BIT(1) +#define NEG_RAIL_DISCHARGE_EN BIT(0) + +#define RAIL_VOLTAGE_INVALID RAIL_VOLTAGE_MASK +#define RAIL_DISCHARGE_REG_DEFAULT (POS_RAIL_DISCHARGE_EN | NEG_RAIL_DISCHARGE_EN) + +#define SGM3804_VOLTAGES_MAX_SELECTOR 0x2f + +enum { + SGM3804_POS_RAIL = 0, + SGM3804_NEG_RAIL, + SGM3804_RAIL_COUNT, +}; + +/* + * The registers are only writable when the gpio is enabled, so + * we need to use the cache for read operations and set the regmap + * as cache_only when both GPIOs are down. + */ +struct sgm3804_data { + struct regmap *regmap; + /* Protects the regcache state update */ + struct mutex lock; + struct gpio_desc *gpios[SGM3804_RAIL_COUNT]; +}; + +static const struct linear_range sgm3804_voltages[] = { + REGULATOR_LINEAR_RANGE(2400000, 0x20, 0x2f, 100000), + REGULATOR_LINEAR_RANGE(4000000, 0x00, 0x17, 100000), +}; + +/* + * The cache is populated with those hardware default values + * so the regmap_update_bits operation will use the cached + * value to build a new register value and write it when GPIOs + * are enabled. + */ +static const struct reg_default sgm3804_reg_defaults[] = { + { SGM3804_POS_RAIL_VOLTAGE_REG, RAIL_VOLTAGE_INVALID }, + { SGM3804_NEG_RAIL_VOLTAGE_REG, RAIL_VOLTAGE_INVALID }, + { SGM3804_RAIL_DISCHARGE_REG, RAIL_DISCHARGE_REG_DEFAULT }, +}; + +/* Registers are only writable */ +static bool sgm3804_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SGM3804_POS_RAIL_VOLTAGE_REG: + case SGM3804_NEG_RAIL_VOLTAGE_REG: + case SGM3804_RAIL_DISCHARGE_REG: + return true; + default: + return false; + } +} + +/* + * Since all registers are only writeable, regmap will only read from the cache data. + */ +static bool sgm3804_readable_reg(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct regmap_config sgm3804_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x03, + .writeable_reg = sgm3804_writeable_reg, + .readable_reg = sgm3804_readable_reg, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = sgm3804_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sgm3804_reg_defaults), +}; + +static int sgm3804_sync_regcache_state(struct sgm3804_data *ctx) +{ + guard(mutex)(&ctx->lock); + + /* If both GPIOs are down, IC is powered down and I2C writes will fail */ + if (!gpiod_get_value_cansleep(ctx->gpios[SGM3804_POS_RAIL]) && + !gpiod_get_value_cansleep(ctx->gpios[SGM3804_NEG_RAIL])) { + regcache_cache_only(ctx->regmap, true); + regcache_mark_dirty(ctx->regmap); + } else { + int ret; + + /* At least a GPIO is up, we can write registers */ + regcache_cache_only(ctx->regmap, false); + ret = regcache_sync(ctx->regmap); + if (ret) { + regcache_cache_only(ctx->regmap, true); + return ret; + } + } + + return 0; +} + +static int sgm3804_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret; + + ret = regulator_get_voltage_sel_regmap(rdev); + if (ret < 0) + return ret; + + /* Force setting a voltage on probe */ + if (ret == RAIL_VOLTAGE_INVALID) + return -ENOTRECOVERABLE; + + return ret; +} + +static int sgm3804_enable(struct regulator_dev *rdev) +{ + struct sgm3804_data *ctx = rdev->reg_data; + int ret; + + ret = gpiod_set_value_cansleep(ctx->gpios[rdev_get_id(rdev)], 1); + if (ret) + return ret; + + ret = sgm3804_sync_regcache_state(ctx); + if (ret) + goto err; + + return 0; + +err: + gpiod_set_value_cansleep(ctx->gpios[rdev_get_id(rdev)], 0); + return ret; +} + +static int sgm3804_disable(struct regulator_dev *rdev) +{ + struct sgm3804_data *ctx = rdev->reg_data; + int ret; + + ret = gpiod_set_value_cansleep(ctx->gpios[rdev_get_id(rdev)], 0); + if (ret) + return ret; + + return sgm3804_sync_regcache_state(ctx); +} + +static int sgm3804_is_enabled(struct regulator_dev *rdev) +{ + struct sgm3804_data *ctx = rdev->reg_data; + + return gpiod_get_value_cansleep(ctx->gpios[rdev_get_id(rdev)]); +} + +static const struct regulator_ops sgm3804_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = sgm3804_get_voltage_sel, + .set_active_discharge = regulator_set_active_discharge_regmap, + .enable = sgm3804_enable, + .disable = sgm3804_disable, + .is_enabled = sgm3804_is_enabled, +}; + +static const struct regulator_desc sgm3804_regulator_desc[] = { + /* Positive Output */ + { + .name = "pos", + .of_match = "pos", + .supply_name = "vin", + .id = SGM3804_POS_RAIL, + .ops = &sgm3804_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = sgm3804_voltages, + .n_linear_ranges = ARRAY_SIZE(sgm3804_voltages), + .n_voltages = SGM3804_VOLTAGES_MAX_SELECTOR + 1, + .vsel_reg = SGM3804_POS_RAIL_VOLTAGE_REG, + .vsel_mask = RAIL_VOLTAGE_MASK, + .active_discharge_on = POS_RAIL_DISCHARGE_EN, + .active_discharge_mask = POS_RAIL_DISCHARGE_EN, + .active_discharge_reg = SGM3804_RAIL_DISCHARGE_REG, + .enable_time = 40000, + .owner = THIS_MODULE, + }, + /* Negative Output */ + { + .name = "neg", + .of_match = "neg", + .supply_name = "vin", + .id = SGM3804_NEG_RAIL, + .ops = &sgm3804_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = sgm3804_voltages, + .n_linear_ranges = ARRAY_SIZE(sgm3804_voltages), + .n_voltages = SGM3804_VOLTAGES_MAX_SELECTOR + 1, + .vsel_reg = SGM3804_NEG_RAIL_VOLTAGE_REG, + .vsel_mask = RAIL_VOLTAGE_MASK, + .active_discharge_on = NEG_RAIL_DISCHARGE_EN, + .active_discharge_mask = NEG_RAIL_DISCHARGE_EN, + .active_discharge_reg = SGM3804_RAIL_DISCHARGE_REG, + .enable_time = 40000, + .owner = THIS_MODULE, + }, +}; + +static int sgm3804_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct sgm3804_data *ctx; + int ret, i; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_init(&ctx->lock); + + ctx->regmap = devm_regmap_init_i2c(i2c, &sgm3804_regmap_config); + if (IS_ERR(ctx->regmap)) + return dev_err_probe(dev, PTR_ERR(ctx->regmap), + "failed to init regmap\n"); + + /* Get enable GPIOs */ + for (i = 0; i < ARRAY_SIZE(sgm3804_regulator_desc); i++) { + const struct regulator_desc *reg = &sgm3804_regulator_desc[i]; + struct fwnode_handle *child; + + child = device_get_named_child_node(dev, reg->of_match); + if (!child) { + dev_err(dev, "missing child '%s'\n", reg->of_match); + return -EINVAL; + } + + ctx->gpios[i] = devm_fwnode_gpiod_get(dev, child, "enable", + GPIOD_ASIS, reg->name); + fwnode_handle_put(child); + if (IS_ERR(ctx->gpios[i])) + return dev_err_probe(dev, PTR_ERR(ctx->gpios[i]), + "failed to get '%s' enable GPIO\n", + reg->name); + } + + ret = sgm3804_sync_regcache_state(ctx); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(sgm3804_regulator_desc); i++) { + struct regulator_config config = { }; + struct regulator_dev *rdev; + + config.dev = dev; + config.regmap = ctx->regmap; + config.of_node = dev_of_node(dev); + config.driver_data = ctx; + rdev = devm_regulator_register(dev, &sgm3804_regulator_desc[i], + &config); + if (IS_ERR(rdev)) + return dev_err_probe(dev, PTR_ERR(rdev), + "failed to register regulator %d\n", i); + } + + return 0; +} + +static const struct i2c_device_id sgm3804_id[] = { + { "sgm3804" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sgm3804_id); + +static const struct of_device_id sgm3804_of_match[] = { + { .compatible = "sgmicro,sgm3804" }, + { } +}; +MODULE_DEVICE_TABLE(of, sgm3804_of_match); + +static struct i2c_driver sgm3804_regulator_driver = { + .driver = { + .name = "sgm3804", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = sgm3804_of_match, + }, + .probe = sgm3804_probe, + .id_table = sgm3804_id, +}; + +module_i2c_driver(sgm3804_regulator_driver); + +MODULE_DESCRIPTION("SGMicro SGM3804 regulator Driver"); +MODULE_AUTHOR("Kancy Joe <kancy2333@outlook.com>"); +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/sy7636a-regulator.c b/drivers/regulator/sy7636a-regulator.c index 551647bc1052..c44c445ea139 100644 --- a/drivers/regulator/sy7636a-regulator.c +++ b/drivers/regulator/sy7636a-regulator.c @@ -147,7 +147,7 @@ static int sy7636a_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id sy7636a_regulator_id_table[] = { - { "sy7636a-regulator", }, + { .name = "sy7636a-regulator" }, { } }; MODULE_DEVICE_TABLE(platform, sy7636a_regulator_id_table); diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c index 2d284c64eeb7..94bf96856d10 100644 --- a/drivers/regulator/tps65086-regulator.c +++ b/drivers/regulator/tps65086-regulator.c @@ -399,7 +399,7 @@ static int tps65086_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id tps65086_regulator_id_table[] = { - { "tps65086-regulator", }, + { .name = "tps65086-regulator" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65086_regulator_id_table); diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index f44b5767099c..8df81ceeb845 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -341,7 +341,7 @@ static int tps65218_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id tps65218_regulator_id_table[] = { - { "tps65218-regulator", }, + { .name = "tps65218-regulator" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65218_regulator_id_table); diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c index 324c3a33af8a..a667c3f44bb7 100644 --- a/drivers/regulator/tps65219-regulator.c +++ b/drivers/regulator/tps65219-regulator.c @@ -541,9 +541,9 @@ static int tps65219_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id tps65219_regulator_id_table[] = { - { "tps65214-regulator", TPS65214 }, - { "tps65215-regulator", TPS65215 }, - { "tps65219-regulator", TPS65219 }, + { .name = "tps65214-regulator", .driver_data = TPS65214 }, + { .name = "tps65215-regulator", .driver_data = TPS65215 }, + { .name = "tps65219-regulator", .driver_data = TPS65219 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65219_regulator_id_table); diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 7ff7877a2e09..4317ec62f18f 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -142,7 +142,7 @@ static int tps65912_regulator_probe(struct platform_device *pdev) } static const struct platform_device_id tps65912_regulator_id_table[] = { - { "tps65912-regulator", }, + { .name = "tps65912-regulator" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65912_regulator_id_table); diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 988fc291241d..b69f99488111 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -224,6 +224,24 @@ static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val #define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc, MSMON_##reg, val) +static bool mpam_msc_check_aidr(struct mpam_msc *msc) +{ + u32 aidr = __mpam_read_reg(msc, MPAMF_AIDR); + u32 major = FIELD_GET(MPAMF_AIDR_ARCH_MAJOR_REV, aidr); + u32 minor = FIELD_GET(MPAMF_AIDR_ARCH_MINOR_REV, aidr); + + /* + * v0.0 and >v2.x aren't supported, but anything else should be backward + * compatible to v0.1 or v1.0. + */ + if (!major && !minor) + return false; + if (major > 1) + return false; + + return true; +} + static u64 mpam_msc_read_idr(struct mpam_msc *msc) { u64 idr_high = 0, idr_low; @@ -945,9 +963,8 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) lockdep_assert_held(&msc->probe_lock); - idr = __mpam_read_reg(msc, MPAMF_AIDR); - if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) { - dev_err_once(dev, "MSC does not match MPAM architecture v1.x\n"); + if (!mpam_msc_check_aidr(msc)) { + dev_err_once(dev, "MSC does not match architecture v1.x\n"); return -EIO; } diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 401a4ece0c97..d9d4468e4cbd 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1626,7 +1626,6 @@ static void qcom_glink_rpdev_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); - kfree(rpdev->driver_override); kfree(rpdev); } @@ -1862,7 +1861,6 @@ static void qcom_glink_device_release(struct device *dev) /* Release qcom_glink_alloc_channel() reference */ kref_put(&channel->refcount, qcom_glink_channel_release); - kfree(rpdev->driver_override); kfree(rpdev); } diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index e7f7831d37f8..c56f69c22e42 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -358,33 +358,6 @@ rpmsg_show_attr(src, src, "0x%x\n"); rpmsg_show_attr(dst, dst, "0x%x\n"); rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - int ret; - - ret = driver_set_override(dev, &rpdev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", rpdev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -405,7 +378,6 @@ static struct attribute *rpmsg_dev_attrs[] = { &dev_attr_dst.attr, &dev_attr_src.attr, &dev_attr_announce.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(rpmsg_dev); @@ -424,9 +396,11 @@ static int rpmsg_dev_match(struct device *dev, const struct device_driver *drv) const struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); const struct rpmsg_device_id *ids = rpdrv->id_table; unsigned int i; + int ret; - if (rpdev->driver_override) - return !strcmp(rpdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; if (ids) for (i = 0; ids[i].name[0]; i++) @@ -535,6 +509,7 @@ static const struct bus_type rpmsg_bus = { .name = "rpmsg", .match = rpmsg_dev_match, .dev_groups = rpmsg_dev_groups, + .driver_override = true, .uevent = rpmsg_uevent, .probe = rpmsg_dev_probe, .remove = rpmsg_dev_remove, @@ -560,11 +535,9 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, device_initialize(dev); if (driver_override) { - ret = driver_set_override(dev, &rpdev->driver_override, - driver_override, - strlen(driver_override)); + ret = device_set_driver_override(dev, driver_override); if (ret) { - dev_err(dev, "device_set_override failed: %d\n", ret); + dev_err(dev, "device_set_driver_override() failed: %d\n", ret); put_device(dev); return ret; } @@ -573,8 +546,6 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, ret = device_add(dev); if (ret) { dev_err(dev, "device_add failed: %d\n", ret); - kfree(rpdev->driver_override); - rpdev->driver_override = NULL; put_device(dev); } diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 5ae15111fb4f..1b8bb05924af 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -374,7 +374,6 @@ static void virtio_rpmsg_release_device(struct device *dev) struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); - kfree(rpdev->driver_override); kfree(vch); } diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 14e58c336baa..74fe73b5738a 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -5569,7 +5569,7 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, dev = &device->cdev->dev; - page = (char *) get_zeroed_page(GFP_ATOMIC); + page = kzalloc(PAGE_SIZE, GFP_ATOMIC); if (page == NULL) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "No memory to dump sense data\n"); @@ -5644,7 +5644,7 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, } dasd_eckd_dump_ccw_range(device, from, last, page + len); } - free_page((unsigned long) page); + kfree(page); } @@ -5659,7 +5659,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, struct tsb *tsb; u8 *sense, *rcq; - page = (char *) get_zeroed_page(GFP_ATOMIC); + page = kzalloc(PAGE_SIZE, GFP_ATOMIC); if (page == NULL) { DBF_DEV_EVENT(DBF_WARNING, device, " %s", "No memory to dump sense data"); @@ -5759,7 +5759,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, sprintf(page + len, "SORRY - NO TSB DATA AVAILABLE\n"); } dev_err(&device->cdev->dev, "%s", page); - free_page((unsigned long) page); + kfree(page); } static void dasd_eckd_dump_sense(struct dasd_device *device, diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 78d66e2711cd..e96d1805b7bb 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -211,7 +211,7 @@ static void dasd_eer_free_buffer_pages(char **buf, int no_pages) int i; for (i = 0; i < no_pages; i++) - free_page((unsigned long) buf[i]); + kfree(buf[i]); } /* @@ -222,7 +222,7 @@ static int dasd_eer_allocate_buffer_pages(char **buf, int no_pages) int i; for (i = 0; i < no_pages; i++) { - buf[i] = (char *) get_zeroed_page(GFP_KERNEL); + buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!buf[i]) { dasd_eer_free_buffer_pages(buf, i); return -ENOMEM; diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 644d3679748d..c39da2ec22b4 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -880,7 +880,7 @@ static void tty3270_free_view(struct tty3270 *tp) raw3270_request_free(tp->kreset); raw3270_request_free(tp->read); raw3270_request_free(tp->write); - free_page((unsigned long)tp->converted_line); + kfree(tp->converted_line); tty_port_destroy(&tp->port); kfree(tp); } @@ -1063,7 +1063,7 @@ static void tty3270_free(struct raw3270_view *view) timer_delete_sync(&tp->timer); tty3270_free_screen(tp->screen, tp->allocated_lines); - free_page((unsigned long)tp->converted_line); + kfree(tp->converted_line); kfree(tp->input); kfree(tp->prompt); tty3270_free_view(tp); @@ -1121,7 +1121,7 @@ tty3270_create_view(int index, struct tty3270 **newtp) goto out_put_view; } - tp->converted_line = (void *)__get_free_page(GFP_KERNEL); + tp->converted_line = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!tp->converted_line) { rc = -ENOMEM; goto out_free_screen; @@ -1167,7 +1167,7 @@ out_free_prompt: out_free_input: kfree(tp->input); out_free_converted_line: - free_page((unsigned long)tp->converted_line); + kfree(tp->converted_line); out_free_screen: tty3270_free_screen(tp->screen, tp->view.rows); out_put_view: diff --git a/drivers/s390/char/sclp_pci.c b/drivers/s390/char/sclp_pci.c index 899063e64aef..d61a7fc0dd61 100644 --- a/drivers/s390/char/sclp_pci.c +++ b/drivers/s390/char/sclp_pci.c @@ -98,6 +98,7 @@ static int sclp_pci_check_report(struct zpci_report_error_header *report) case SCLP_ERRNOTIFY_AQ_REPAIR: case SCLP_ERRNOTIFY_AQ_INFO_LOG: case SCLP_ERRNOTIFY_AQ_OPTICS_DATA: + case SCLP_ERRNOTIFY_AQ_NVME_SMART_LOG: break; default: return -EINVAL; diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 62979adcb381..4e2bf8186a5d 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -81,9 +81,6 @@ static struct timer_list sclp_vt220_timer; * yet sent */ static struct sclp_vt220_request *sclp_vt220_current_request; -/* Number of characters in current request buffer */ -static int sclp_vt220_buffered_chars; - /* Counter controlling core driver initialization. */ static int __initdata sclp_vt220_init_count; @@ -689,7 +686,6 @@ static int __init __sclp_vt220_init(int num_pages) timer_setup(&sclp_vt220_timer, sclp_vt220_timeout, 0); tty_port_init(&sclp_vt220_port); sclp_vt220_current_request = NULL; - sclp_vt220_buffered_chars = 0; sclp_vt220_flush_later = 0; /* Allocate pages for output buffering */ diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index b26b5fca6ce8..1ab7400a3c10 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -46,8 +46,6 @@ struct ipib_info { static struct debug_info *zcore_dbf; static int hsa_available; static struct dentry *zcore_dir; -static struct dentry *zcore_reipl_file; -static struct dentry *zcore_hsa_file; static struct ipl_parameter_block *zcore_ipl_block; static unsigned long os_info_flags; @@ -353,10 +351,8 @@ static int __init zcore_init(void) goto fail; zcore_dir = debugfs_create_dir("zcore" , NULL); - zcore_reipl_file = debugfs_create_file("reipl", S_IRUSR, zcore_dir, - NULL, &zcore_reipl_fops); - zcore_hsa_file = debugfs_create_file("hsa", S_IRUSR|S_IWUSR, zcore_dir, - NULL, &zcore_hsa_fops); + debugfs_create_file("reipl", 0400, zcore_dir, NULL, &zcore_reipl_fops); + debugfs_create_file("hsa", 0600, zcore_dir, NULL, &zcore_hsa_fops); register_reboot_notifier(&zcore_reboot_notifier); atomic_notifier_chain_register(&panic_notifier_list, &zcore_on_panic_notifier); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 5bbb112b1619..fb591118ecb2 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1328,7 +1328,7 @@ static int purge_fn(struct subchannel *sch, void *data) cdev = sch_get_cdev(sch); if (cdev) { - if (cdev->private->state != DEV_STATE_OFFLINE) + if (cdev->online) goto unlock; if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index f24e27add721..6a7497db5fb9 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -744,6 +744,23 @@ void ap_send_online_uevent(struct ap_device *ap_dev, int online) } EXPORT_SYMBOL(ap_send_online_uevent); +void ap_send_se_bind_uevent(struct ap_device *ap_dev) +{ + char *envp[] = { "SE_BIND=1", NULL }; + + kobject_uevent_env(&ap_dev->device.kobj, KOBJ_CHANGE, envp); +} + +void ap_send_se_assoc_uevent(struct ap_device *ap_dev, unsigned int assoc_idx) +{ + char buf[32]; + char *envp[] = { buf, NULL }; + + snprintf(buf, sizeof(buf), "SE_ASSOC=%u", assoc_idx); + + kobject_uevent_env(&ap_dev->device.kobj, KOBJ_CHANGE, envp); +} + static void ap_send_mask_changed_uevent(unsigned long *newapm, unsigned long *newaqm) { diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 04ea256ecf91..b2e57e5d6c3f 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -134,8 +134,6 @@ struct ap_message; struct ap_driver { struct device_driver driver; - struct ap_device_id *ids; - unsigned int flags; int (*probe)(struct ap_device *); void (*remove)(struct ap_device *); @@ -156,6 +154,9 @@ struct ap_driver { */ void (*on_scan_complete)(struct ap_config_info *new_config_info, struct ap_config_info *old_config_info); + + struct ap_device_id *ids; + unsigned int flags; }; #define to_ap_drv(x) container_of_const((x), struct ap_driver, driver) @@ -173,11 +174,11 @@ struct ap_device { struct ap_card { struct ap_device ap_dev; struct ap_tapq_hwinfo hwinfo; /* TAPQ GR2 content */ - int id; /* AP card number. */ + atomic64_t total_request_count; /* # requests ever for this AP device.*/ unsigned int maxmsgsize; /* AP msg limit for this card */ + int id; /* AP card number. */ bool config; /* configured state */ bool chkstop; /* checkstop state */ - atomic64_t total_request_count; /* # requests ever for this AP device.*/ }; #define TAPQ_CARD_HWINFO_MASK 0xFFFF0000FFFF0F0FUL @@ -190,16 +191,14 @@ struct ap_queue { struct hlist_node hnode; /* Node for the ap_queues hashtable */ struct ap_card *card; /* Ptr to assoc. AP card. */ spinlock_t lock; /* Per device lock. */ + u64 total_request_count; /* # requests ever for this AP device.*/ enum ap_dev_state dev_state; /* queue device state */ - bool config; /* configured state */ - bool chkstop; /* checkstop state */ ap_qid_t qid; /* AP queue id. */ unsigned int se_bstate; /* SE bind state (BS) */ unsigned int assoc_idx; /* SE association index */ int queue_count; /* # messages currently on AP queue. */ int pendingq_count; /* # requests on pendingq list. */ int requestq_count; /* # requests on requestq list. */ - u64 total_request_count; /* # requests ever for this AP device.*/ int request_timeout; /* Request timeout in jiffies. */ struct timer_list timeout; /* Timer for request timeouts. */ struct list_head pendingq; /* List of message sent to AP queue. */ @@ -208,6 +207,8 @@ struct ap_queue { enum ap_sm_state sm_state; /* ap queue state machine state */ int rapq_fbit; /* fbit arg for next rapq invocation */ int last_err_rc; /* last error state response code */ + bool config; /* configured state */ + bool chkstop; /* checkstop state */ }; #define to_ap_queue(x) container_of((x), struct ap_queue, ap_dev.device) @@ -225,12 +226,12 @@ struct ap_message { void *msg; /* Pointer to message buffer. */ size_t len; /* actual msg len in msg buffer */ size_t bufsize; /* allocated msg buffer size */ - u16 flags; /* Flags, see AP_MSG_FLAG_xxx */ - int rc; /* Return code for this message */ - struct ap_response_type response; /* receive is called from tasklet context */ void (*receive)(struct ap_queue *, struct ap_message *, struct ap_message *); + struct ap_response_type response; + int rc; /* Return code for this message */ + u16 flags; /* Flags, see AP_MSG_FLAG_xxx */ }; #define AP_MSG_FLAG_SPECIAL 0x0001 /* flag msg as 'special' with NQAP */ @@ -373,5 +374,7 @@ int ap_wait_apqn_bindings_complete(unsigned long timeout); void ap_send_config_uevent(struct ap_device *ap_dev, bool cfg); void ap_send_online_uevent(struct ap_device *ap_dev, int online); +void ap_send_se_bind_uevent(struct ap_device *ap_dev); +void ap_send_se_assoc_uevent(struct ap_device *ap_dev, unsigned int assoc_idx); #endif /* _AP_BUS_H_ */ diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index ca9819e6f7e7..bc03b2101e39 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -478,6 +478,7 @@ static enum ap_sm_wait ap_sm_assoc_wait(struct ap_queue *aq) pr_debug("queue 0x%02x.%04x associated with %u\n", AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid), aq->assoc_idx); + ap_send_se_assoc_uevent(&aq->ap_dev, aq->assoc_idx); return AP_SM_WAIT_NONE; case AP_BS_Q_USABLE_NO_SECURE_KEY: /* association still pending */ @@ -960,34 +961,37 @@ static ssize_t se_bind_store(struct device *dev, __ap_flush_queue(aq); aq->rapq_fbit = 1; _ap_queue_init_state(aq); - rc = count; - goto out; + spin_unlock_bh(&aq->lock); + return count; } + /* lock this ap to have fetch and update in an atomic way */ + spin_lock_bh(&aq->lock); + /* Bind. Check current SE bind state */ status = ap_test_queue(aq->qid, 1, &hwinfo); if (status.response_code) { AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n", __func__, status.response_code, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); - return -EIO; + rc = -EIO; + goto error; } /* Update BS state */ - spin_lock_bh(&aq->lock); aq->se_bstate = hwinfo.bs; if (hwinfo.bs != AP_BS_Q_AVAIL_FOR_BINDING) { AP_DBF_WARN("%s bind attempt with bs %d on queue 0x%02x.%04x\n", __func__, hwinfo.bs, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); rc = -EINVAL; - goto out; + goto error; } /* Check SM state */ if (aq->sm_state < AP_SM_STATE_IDLE) { rc = -EBUSY; - goto out; + goto error; } /* invoke BAPQ */ @@ -997,7 +1001,7 @@ static ssize_t se_bind_store(struct device *dev, __func__, status.response_code, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); rc = -EIO; - goto out; + goto error; } aq->assoc_idx = ASSOC_IDX_INVALID; @@ -1008,7 +1012,7 @@ static ssize_t se_bind_store(struct device *dev, __func__, status.response_code, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); rc = -EIO; - goto out; + goto error; } aq->se_bstate = hwinfo.bs; if (!(hwinfo.bs == AP_BS_Q_USABLE || @@ -1017,15 +1021,19 @@ static ssize_t se_bind_store(struct device *dev, __func__, hwinfo.bs, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); rc = -EIO; - goto out; + goto error; } /* SE bind was successful */ + spin_unlock_bh(&aq->lock); + AP_DBF_INFO("%s bapq(0x%02x.%04x) success\n", __func__, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); - rc = count; + ap_send_se_bind_uevent(&aq->ap_dev); -out: + return count; + +error: spin_unlock_bh(&aq->lock); return rc; } @@ -1091,15 +1099,18 @@ static ssize_t se_associate_store(struct device *dev, if (value >= ASSOC_IDX_INVALID) return -EINVAL; + /* lock this ap to have fetch and update in an atomic way */ + spin_lock_bh(&aq->lock); + /* check current SE bind state */ status = ap_test_queue(aq->qid, 1, &hwinfo); if (status.response_code) { AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n", __func__, status.response_code, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); - return -EIO; + rc = -EIO; + goto out; } - spin_lock_bh(&aq->lock); aq->se_bstate = hwinfo.bs; if (hwinfo.bs != AP_BS_Q_USABLE_NO_SECURE_KEY) { AP_DBF_WARN("%s association attempt with bs %d on queue 0x%02x.%04x\n", @@ -1123,17 +1134,16 @@ static ssize_t se_associate_store(struct device *dev, aq->sm_state = AP_SM_STATE_ASSOC_WAIT; aq->assoc_idx = value; ap_wait(ap_sm_event(aq, AP_SM_EVENT_POLL)); + rc = count; break; default: AP_DBF_WARN("%s RC 0x%02x on aapq(0x%02x.%04x)\n", __func__, status.response_code, AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); rc = -EIO; - goto out; + break; } - rc = count; - out: spin_unlock_bh(&aq->lock); return rc; diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index d6a455df228d..f57189c2b839 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1782,7 +1782,7 @@ int zcrypt_rng_device_add(void) mutex_lock(&zcrypt_rng_mutex); if (zcrypt_rng_device_count == 0) { - zcrypt_rng_buffer = (u32 *)get_zeroed_page(GFP_KERNEL); + zcrypt_rng_buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!zcrypt_rng_buffer) { rc = -ENOMEM; goto out; @@ -1799,7 +1799,7 @@ int zcrypt_rng_device_add(void) return 0; out_free: - free_page((unsigned long)zcrypt_rng_buffer); + kfree(zcrypt_rng_buffer); out: mutex_unlock(&zcrypt_rng_mutex); return rc; @@ -1811,7 +1811,7 @@ void zcrypt_rng_device_remove(void) zcrypt_rng_device_count--; if (zcrypt_rng_device_count == 0) { hwrng_unregister(&zcrypt_rng_dev); - free_page((unsigned long)zcrypt_rng_buffer); + kfree(zcrypt_rng_buffer); } mutex_unlock(&zcrypt_rng_mutex); } diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 6ef8850a42df..9f8df809bb85 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -104,33 +104,28 @@ struct zcrypt_card { struct list_head list; /* Device list. */ struct list_head zqueues; /* List of zcrypt queues */ struct kref refcount; /* device refcounting */ - struct ap_card *card; /* The "real" ap card device. */ int online; /* User online/offline */ - - int user_space_type; /* User space device id. */ + struct ap_card *card; /* The "real" ap card device. */ char *type_string; /* User space device name. */ + int user_space_type; /* User space device id. */ int min_mod_size; /* Min number of bits. */ int max_mod_size; /* Max number of bits. */ int max_exp_bit_length; const int *speed_rating; /* Speed idx of crypto ops. */ atomic_t load; /* Utilization of the crypto device */ - int request_count; /* # current requests. */ }; struct zcrypt_queue { struct list_head list; /* Device list. */ struct kref refcount; /* device refcounting */ + int online; /* User online/offline */ struct zcrypt_card *zcard; struct zcrypt_ops *ops; /* Crypto operations. */ struct ap_queue *queue; /* The "real" ap queue device. */ - int online; /* User online/offline */ - + struct ap_message reply; /* Per-device reply structure. */ atomic_t load; /* Utilization of the crypto device */ - int request_count; /* # current requests. */ - - struct ap_message reply; /* Per-device reply structure. */ }; /* transport layer rescanning */ diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h index 06507363947b..07bbb1c20022 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.h +++ b/drivers/s390/crypto/zcrypt_ccamisc.h @@ -235,7 +235,16 @@ int cca_findcard2(u32 *apqns, u32 *nr_apqns, u16 cardnr, u16 domain, /* struct to hold info for each CCA queue */ struct cca_info { - int hwtype; /* one of the defined AP_DEVICE_TYPE_* */ + u8 new_asym_mkvp[16]; /* verify pattern of new asym master key */ + u8 cur_asym_mkvp[16]; /* verify pattern of current asym master key */ + u8 old_asym_mkvp[16]; /* verify pattern of old asym master key */ + u8 new_aes_mkvp[8]; /* truncated sha256 of new aes master key */ + u8 cur_aes_mkvp[8]; /* truncated sha256 of current aes master key */ + u8 old_aes_mkvp[8]; /* truncated sha256 of old aes master key */ + u8 new_apka_mkvp[8]; /* truncated sha256 of new apka master key */ + u8 cur_apka_mkvp[8]; /* truncated sha256 of current apka mk */ + u8 old_apka_mkvp[8]; /* truncated sha256 of old apka mk */ + char serial[9]; /* serial number (8 ascii numbers + 0x00) */ char new_aes_mk_state; /* '1' empty, '2' partially full, '3' full */ char cur_aes_mk_state; /* '1' invalid, '2' valid */ char old_aes_mk_state; /* '1' invalid, '2' valid */ @@ -245,16 +254,7 @@ struct cca_info { char new_asym_mk_state; /* '1' empty, '2' partially full, '3' full */ char cur_asym_mk_state; /* '1' invalid, '2' valid */ char old_asym_mk_state; /* '1' invalid, '2' valid */ - u8 new_aes_mkvp[8]; /* truncated sha256 of new aes master key */ - u8 cur_aes_mkvp[8]; /* truncated sha256 of current aes master key */ - u8 old_aes_mkvp[8]; /* truncated sha256 of old aes master key */ - u8 new_apka_mkvp[8]; /* truncated sha256 of new apka master key */ - u8 cur_apka_mkvp[8]; /* truncated sha256 of current apka mk */ - u8 old_apka_mkvp[8]; /* truncated sha256 of old apka mk */ - u8 new_asym_mkvp[16]; /* verify pattern of new asym master key */ - u8 cur_asym_mkvp[16]; /* verify pattern of current asym master key */ - u8 old_asym_mkvp[16]; /* verify pattern of old asym master key */ - char serial[9]; /* serial number (8 ascii numbers + 0x00) */ + int hwtype; /* one of the defined AP_DEVICE_TYPE_* */ }; /* diff --git a/drivers/s390/crypto/zcrypt_ep11misc.h b/drivers/s390/crypto/zcrypt_ep11misc.h index b5e6fd861815..05006817b6c0 100644 --- a/drivers/s390/crypto/zcrypt_ep11misc.h +++ b/drivers/s390/crypto/zcrypt_ep11misc.h @@ -86,19 +86,19 @@ int ep11_check_aes_key(debug_info_t *dbg, int dbflvl, /* EP11 card info struct */ struct ep11_card_info { + u64 op_mode; /* card operational mode(s) */ + char serial[16]; /* serial number string (16 ascii, no 0x00 !) */ u32 API_ord_nr; /* API ordinal number */ u16 FW_version; /* Firmware major and minor version */ - char serial[16]; /* serial number string (16 ascii, no 0x00 !) */ - u64 op_mode; /* card operational mode(s) */ }; /* EP11 domain info struct */ struct ep11_domain_info { - char cur_wk_state; /* '0' invalid, '1' valid */ - char new_wk_state; /* '0' empty, '1' uncommitted, '2' committed */ + u64 op_mode; /* domain operational mode(s) */ u8 cur_wkvp[32]; /* current wrapping key verification pattern */ u8 new_wkvp[32]; /* new wrapping key verification pattern */ - u64 op_mode; /* domain operational mode(s) */ + char cur_wk_state; /* '0' invalid, '1' valid */ + char new_wk_state; /* '0' empty, '1' uncommitted, '2' committed */ }; /* diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index cf5f760d0e02..20fb0d2e02a9 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3362,9 +3362,9 @@ static int qeth_query_setdiagass(struct qeth_card *card) static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) { - unsigned long info = get_zeroed_page(GFP_KERNEL); - struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; - struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info; + void *info = kzalloc(PAGE_SIZE, GFP_KERNEL); + struct sysinfo_2_2_2 *info222 = info; + struct sysinfo_3_2_2 *info322 = info; struct ccw_dev_id ccwid; int level; @@ -3381,7 +3381,7 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name)); memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname)); } - free_page(info); + kfree(info); } static int qeth_hw_trap_cb(struct qeth_card *card, diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 02cd4410efca..496ddd45f74d 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1385,7 +1385,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, while (rlen >= sizeof(*desc)) { dlen = desc->fip_dlen * FIP_BPW; - if (dlen > rlen) + if (dlen < sizeof(*desc) || dlen > rlen) goto err; /* Drop CVL if there are duplicate critical descriptors */ if ((desc->fip_dtype < 32) && diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 2699e4e09b5b..056cbe50e19e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -3612,6 +3612,15 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, complete(&cmd_fusion->done); break; case MPI2_FUNCTION_SCSI_IO_REQUEST: /*Fast Path IO.*/ + /* + * Firmware can send stale/duplicate completions for + * commands already returned to the pool. scmd_local + * would be NULL for such cases. Skip processing to + * avoid NULL pointer access. + */ + if (!scmd_local) + break; + /* Update load balancing info */ if (fusion->load_balance_info && (megasas_priv(cmd_fusion->scmd)->status & diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 1515495fd9ea..040c5e1e713a 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -6953,7 +6953,7 @@ static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt) ++num_dev_resets; if (SDEBUG_OPT_ALL_NOISE & sdebug_opts) - sdev_printk(KERN_INFO, sdp, "doing device reset"); + sdev_printk(KERN_INFO, sdp, "doing device reset\n"); scsi_debug_stop_all_queued(sdp); if (devip) { diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 68a992494b12..c6defe1c3152 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -218,6 +218,7 @@ static struct { {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"Promise", "VTrak E310f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, {"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, {"Promise", "", NULL, BLIST_SPARSELUN}, {"QEMU", "QEMU CD-ROM", NULL, BLIST_SKIP_VPD_PAGES}, diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 6e8c7a42603e..22e2e3223440 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -575,10 +575,33 @@ void scsi_requeue_run_queue(struct work_struct *work) void scsi_run_host_queues(struct Scsi_Host *shost) { - struct scsi_device *sdev; + struct scsi_device *sdev, *prev = NULL; + unsigned long flags; - shost_for_each_device(sdev, shost) + spin_lock_irqsave(shost->host_lock, flags); + __shost_for_each_device(sdev, shost) { + /* + * Only skip devices so deep into removal they will never need + * another kick to their queues. Thus scsi_device_get() cannot + * be used as it would skip devices in SDEV_CANCEL state which + * may need a queue kick. + */ + if (sdev->sdev_state == SDEV_DEL || + !get_device(&sdev->sdev_gendev)) + continue; + spin_unlock_irqrestore(shost->host_lock, flags); + + if (prev) + put_device(&prev->sdev_gendev); scsi_run_queue(sdev->request_queue); + + prev = sdev; + + spin_lock_irqsave(shost->host_lock, flags); + } + spin_unlock_irqrestore(shost->host_lock, flags); + if (prev) + put_device(&prev->sdev_gendev); } static void scsi_uninit_cmd(struct scsi_cmnd *cmd) @@ -1950,7 +1973,7 @@ out_put_budget: } static int scsi_mq_init_request(struct blk_mq_tag_set *set, struct request *rq, - unsigned int hctx_idx, unsigned int numa_node) + unsigned int hctx_idx, int numa_node) { struct Scsi_Host *shost = set->driver_data; struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index dce95e361daf..173ed6373f04 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -737,6 +737,37 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats) } } +static void +fc_fpin_pname_stats_update(struct Scsi_Host *shost, + struct fc_rport *attach_rport, u16 event_type, + u32 desc_len, u32 fixed_len, u32 pname_count, + __be64 *pname_list, + void (*stats_update)(u16 event_type, + struct fc_fpin_stats *stats)) +{ + u32 i; + struct fc_rport *rport; + u64 wwpn; + + if (desc_len < fixed_len) + pname_count = 0; + else + pname_count = min(pname_count, (desc_len - fixed_len) / + sizeof(pname_list[0])); + + for (i = 0; i < pname_count; i++) { + wwpn = be64_to_cpu(pname_list[i]); + rport = fc_find_rport_by_wwpn(shost, wwpn); + if (rport && + (rport->roles & FC_PORT_ROLE_FCP_TARGET || + rport->roles & FC_PORT_ROLE_NVME_TARGET)) { + if (rport == attach_rport) + continue; + stats_update(event_type, &rport->fpin_stats); + } + } +} + /* * fc_fpin_li_stats_update - routine to update Link Integrity * event statistics. @@ -747,13 +778,11 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats) static void fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) { - u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv; u16 event_type = be16_to_cpu(li_desc->event_type); - u64 wwpn; rport = fc_find_rport_by_wwpn(shost, be64_to_cpu(li_desc->attached_wwpn)); @@ -764,22 +793,11 @@ fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) fc_li_stats_update(event_type, &attach_rport->fpin_stats); } - if (be32_to_cpu(li_desc->pname_count) > 0) { - for (i = 0; - i < be32_to_cpu(li_desc->pname_count); - i++) { - wwpn = be64_to_cpu(li_desc->pname_list[i]); - rport = fc_find_rport_by_wwpn(shost, wwpn); - if (rport && - (rport->roles & FC_PORT_ROLE_FCP_TARGET || - rport->roles & FC_PORT_ROLE_NVME_TARGET)) { - if (rport == attach_rport) - continue; - fc_li_stats_update(event_type, - &rport->fpin_stats); - } - } - } + fc_fpin_pname_stats_update(shost, attach_rport, event_type, + be32_to_cpu(li_desc->desc_len), + FC_TLV_DESC_LENGTH_FROM_SZ(*li_desc), + be32_to_cpu(li_desc->pname_count), + li_desc->pname_list, fc_li_stats_update); if (fc_host->port_name == be64_to_cpu(li_desc->attached_wwpn)) fc_li_stats_update(event_type, &fc_host->fpin_stats); @@ -827,13 +845,11 @@ static void fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) { - u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_fn_peer_congn_desc *pc_desc = (struct fc_fn_peer_congn_desc *)tlv; u16 event_type = be16_to_cpu(pc_desc->event_type); - u64 wwpn; rport = fc_find_rport_by_wwpn(shost, be64_to_cpu(pc_desc->attached_wwpn)); @@ -844,22 +860,11 @@ fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, fc_cn_stats_update(event_type, &attach_rport->fpin_stats); } - if (be32_to_cpu(pc_desc->pname_count) > 0) { - for (i = 0; - i < be32_to_cpu(pc_desc->pname_count); - i++) { - wwpn = be64_to_cpu(pc_desc->pname_list[i]); - rport = fc_find_rport_by_wwpn(shost, wwpn); - if (rport && - (rport->roles & FC_PORT_ROLE_FCP_TARGET || - rport->roles & FC_PORT_ROLE_NVME_TARGET)) { - if (rport == attach_rport) - continue; - fc_cn_stats_update(event_type, - &rport->fpin_stats); - } - } - } + fc_fpin_pname_stats_update(shost, attach_rport, event_type, + be32_to_cpu(pc_desc->desc_len), + FC_TLV_DESC_LENGTH_FROM_SZ(*pc_desc), + be32_to_cpu(pc_desc->pname_count), + pc_desc->pname_list, fc_cn_stats_update); } /* diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 1ed6be6e85d2..3071e46d03be 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1471,15 +1471,12 @@ static int qcom_slim_ngd_ssr_pdr_notify(struct qcom_slim_ngd_ctrl *ctrl, switch (action) { case QCOM_SSR_BEFORE_SHUTDOWN: case SERVREG_SERVICE_STATE_DOWN: - /* Make sure the last dma xfer is finished */ - mutex_lock(&ctrl->tx_lock); if (ctrl->state != QCOM_SLIM_NGD_CTRL_DOWN) { pm_runtime_get_noresume(ctrl->ctrl.dev); ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN; qcom_slim_ngd_down(ctrl); qcom_slim_ngd_exit_dma(ctrl); } - mutex_unlock(&ctrl->tx_lock); break; case QCOM_SSR_AFTER_POWERUP: case SERVREG_SERVICE_STATE_UP: @@ -1542,7 +1539,7 @@ static int of_qcom_slim_ngd_register(struct device *parent, kfree(ngd); return ret; } - ngd->pdev->dev.of_node = node; + ngd->pdev->dev.of_node = of_node_get(node); ctrl->ngd = ngd; ret = platform_device_add(ngd->pdev); @@ -1560,6 +1557,13 @@ static int of_qcom_slim_ngd_register(struct device *parent, return -ENODEV; } +static void qcom_slim_ngd_unregister(struct qcom_slim_ngd_ctrl *ctrl) +{ + struct qcom_slim_ngd *ngd = ctrl->ngd; + + platform_device_del(ngd->pdev); +} + static int qcom_slim_ngd_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1577,24 +1581,10 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev) ret = qcom_slim_ngd_qmi_svc_event_init(ctrl); if (ret) { dev_err(&pdev->dev, "QMI service registration failed:%d", ret); - return ret; + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); } - INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker); - INIT_WORK(&ctrl->ngd_up_work, qcom_slim_ngd_up_worker); - ctrl->mwq = create_singlethread_workqueue("ngd_master"); - if (!ctrl->mwq) { - dev_err(&pdev->dev, "Failed to start master worker\n"); - ret = -ENOMEM; - goto wq_err; - } - - return 0; -wq_err: - qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi); - if (ctrl->mwq) - destroy_workqueue(ctrl->mwq); - return ret; } @@ -1602,6 +1592,7 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct qcom_slim_ngd_ctrl *ctrl; + int irq; int ret; struct pdr_service *pds; @@ -1615,20 +1606,16 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) if (IS_ERR(ctrl->base)) return PTR_ERR(ctrl->base); - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; - ret = devm_request_irq(dev, ret, qcom_slim_ngd_interrupt, - IRQF_TRIGGER_HIGH, "slim-ngd", ctrl); + ret = devm_request_irq(dev, irq, qcom_slim_ngd_interrupt, + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + "slim-ngd", ctrl); if (ret) return dev_err_probe(&pdev->dev, ret, "request IRQ failed\n"); - ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify; - ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb); - if (IS_ERR(ctrl->notifier)) - return PTR_ERR(ctrl->notifier); - ctrl->dev = dev; ctrl->framer.rootfreq = SLIM_ROOT_FREQ >> 3; ctrl->framer.superfreq = @@ -1649,48 +1636,71 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) init_completion(&ctrl->qmi.qmi_comp); init_completion(&ctrl->qmi_up); + INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker); + INIT_WORK(&ctrl->ngd_up_work, qcom_slim_ngd_up_worker); + + ctrl->mwq = create_singlethread_workqueue("ngd_master"); + if (!ctrl->mwq) + return dev_err_probe(dev, -ENOMEM, "Failed to start master worker\n"); + ctrl->pdr = pdr_handle_alloc(slim_pd_status, ctrl); if (IS_ERR(ctrl->pdr)) { - ret = dev_err_probe(dev, PTR_ERR(ctrl->pdr), - "Failed to init PDR handle\n"); - goto err_pdr_alloc; + ret = dev_err_probe(dev, PTR_ERR(ctrl->pdr), "Failed to init PDR handle\n"); + goto err_destroy_mwq; } + ret = of_qcom_slim_ngd_register(dev, ctrl); + if (ret) + goto err_pdr_release; + pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd"); if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { ret = dev_err_probe(dev, PTR_ERR(pds), "pdr add lookup failed\n"); - goto err_pdr_lookup; + goto err_unregister_ngd; + } + + ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify; + ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb); + if (IS_ERR(ctrl->notifier)) { + ret = PTR_ERR(ctrl->notifier); + goto err_unregister_ngd; } - platform_driver_register(&qcom_slim_ngd_driver); - return of_qcom_slim_ngd_register(dev, ctrl); + enable_irq(irq); -err_pdr_alloc: - qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb); + return 0; -err_pdr_lookup: +err_unregister_ngd: + qcom_slim_ngd_unregister(ctrl); +err_pdr_release: pdr_handle_release(ctrl->pdr); +err_destroy_mwq: + destroy_workqueue(ctrl->mwq); return ret; } static void qcom_slim_ngd_ctrl_remove(struct platform_device *pdev) { - platform_driver_unregister(&qcom_slim_ngd_driver); + struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev); + + pdr_handle_release(ctrl->pdr); + qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb); + + qcom_slim_ngd_unregister(ctrl); + + destroy_workqueue(ctrl->mwq); } static void qcom_slim_ngd_remove(struct platform_device *pdev) { struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev); + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); - pdr_handle_release(ctrl->pdr); - qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb); qcom_slim_ngd_enable(ctrl, false); qcom_slim_ngd_exit_dma(ctrl); qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi); - if (ctrl->mwq) - destroy_workqueue(ctrl->mwq); kfree(ctrl->ngd); ctrl->ngd = NULL; @@ -1752,6 +1762,28 @@ static struct platform_driver qcom_slim_ngd_driver = { }, }; -module_platform_driver(qcom_slim_ngd_ctrl_driver); +static int qcom_slim_ngd_init(void) +{ + int ret; + + ret = platform_driver_register(&qcom_slim_ngd_driver); + if (ret) + return ret; + + ret = platform_driver_register(&qcom_slim_ngd_ctrl_driver); + if (ret) + platform_driver_unregister(&qcom_slim_ngd_driver); + + return ret; +} + +static void qcom_slim_ngd_exit(void) +{ + platform_driver_unregister(&qcom_slim_ngd_ctrl_driver); + platform_driver_unregister(&qcom_slim_ngd_driver); +} + +module_init(qcom_slim_ngd_init); +module_exit(qcom_slim_ngd_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Qualcomm SLIMBus NGD controller"); diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c index 77763a107edb..fc080e56f50d 100644 --- a/drivers/soc/imx/soc-imx8m.c +++ b/drivers/soc/imx/soc-imx8m.c @@ -247,7 +247,7 @@ static int imx8m_soc_probe(struct platform_device *pdev) if (ret) return ret; - data = device_get_match_data(dev); + data = of_machine_get_match_data(imx8_soc_match); if (data) { soc_dev_attr->soc_id = data->name; ret = imx8m_soc_prepare(pdev, data->ocotp_compatible); diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c index 92d1142a59e6..0400a01b2338 100644 --- a/drivers/soc/microchip/mpfs-sys-controller.c +++ b/drivers/soc/microchip/mpfs-sys-controller.c @@ -158,8 +158,8 @@ no_flash: of_data = (struct mpfs_syscon_config *) device_get_match_data(dev); if (!of_data) { - dev_err(dev, "Error getting match data\n"); - return -EINVAL; + ret = dev_err_probe(dev, -EINVAL, "Error getting match data\n"); + goto out_free_channel; } for (i = 0; i < of_data->nb_subdevs; i++) { @@ -173,6 +173,8 @@ no_flash: return 0; +out_free_channel: + mbox_free_channel(sys_controller->chan); out_free: kfree(sys_controller); return ret; diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index b203bc685cad..5f20108aa03e 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -16,6 +16,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/xarray.h> #include <linux/firmware/qcom/qcom_scm.h> @@ -108,11 +109,15 @@ struct qcom_ice { void __iomem *base; struct clk *core_clk; + struct clk *iface_clk; bool use_hwkm; bool hwkm_init_complete; u8 hwkm_version; }; +static DEFINE_XARRAY(ice_handles); +static DEFINE_MUTEX(ice_mutex); + static bool qcom_ice_check_supported(struct qcom_ice *ice) { u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); @@ -312,8 +317,13 @@ int qcom_ice_resume(struct qcom_ice *ice) err = clk_prepare_enable(ice->core_clk); if (err) { - dev_err(dev, "failed to enable core clock (%d)\n", - err); + dev_err(dev, "Failed to enable core clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(ice->iface_clk); + if (err) { + dev_err(dev, "Failed to enable iface clock: %d\n", err); return err; } qcom_ice_hwkm_init(ice); @@ -323,6 +333,7 @@ EXPORT_SYMBOL_GPL(qcom_ice_resume); int qcom_ice_suspend(struct qcom_ice *ice) { + clk_disable_unprepare(ice->iface_clk); clk_disable_unprepare(ice->core_clk); ice->hwkm_init_complete = false; @@ -559,7 +570,7 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, if (!qcom_scm_ice_available()) { dev_warn(dev, "ICE SCM interface not found\n"); - return NULL; + return ERR_PTR(-EOPNOTSUPP); } engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL); @@ -580,10 +591,16 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, if (!engine->core_clk) engine->core_clk = devm_clk_get_optional_enabled(dev, "ice"); if (!engine->core_clk) + engine->core_clk = devm_clk_get_optional_enabled(dev, "core"); + if (!engine->core_clk) engine->core_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(engine->core_clk)) return ERR_CAST(engine->core_clk); + engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface"); + if (IS_ERR(engine->iface_clk)) + return ERR_CAST(engine->iface_clk); + if (!qcom_ice_check_supported(engine)) return ERR_PTR(-EOPNOTSUPP); @@ -631,6 +648,8 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) return qcom_ice_create(&pdev->dev, base); } + guard(mutex)(&ice_mutex); + /* * If the consumer node does not provider an 'ice' reg range * (legacy DT binding), then it must at least provide a phandle @@ -639,20 +658,21 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) - return NULL; + return ERR_PTR(-EOPNOTSUPP); pdev = of_find_device_by_node(node); if (!pdev) { dev_err(dev, "Cannot find device node %s\n", node->name); - return ERR_PTR(-EPROBE_DEFER); + return ERR_PTR(-ENODEV); } - ice = platform_get_drvdata(pdev); - if (!ice) { - dev_err(dev, "Cannot get ice instance from %s\n", - dev_name(&pdev->dev)); + ice = xa_load(&ice_handles, pdev->dev.of_node->phandle); + if (IS_ERR_OR_NULL(ice)) { platform_device_put(pdev); - return ERR_PTR(-EPROBE_DEFER); + if (!ice) + return ERR_PTR(-EPROBE_DEFER); + else + return ice; } link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); @@ -691,8 +711,7 @@ static void devm_of_qcom_ice_put(struct device *dev, void *res) * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already * be created and so this function will return that instead. * - * Return: ICE pointer on success, NULL if there is no ICE data provided by the - * consumer or ERR_PTR() on error. + * Return: ICE pointer on success, ERR_PTR() on error. */ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) { @@ -703,7 +722,7 @@ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) return ERR_PTR(-ENOMEM); ice = of_qcom_ice_get(dev); - if (!IS_ERR_OR_NULL(ice)) { + if (!IS_ERR(ice)) { *dr = ice; devres_add(dev, dr); } else { @@ -716,24 +735,40 @@ EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get); static int qcom_ice_probe(struct platform_device *pdev) { + unsigned long phandle = pdev->dev.of_node->phandle; struct qcom_ice *engine; void __iomem *base; + guard(mutex)(&ice_mutex); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { dev_warn(&pdev->dev, "ICE registers not found\n"); + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL); return PTR_ERR(base); } engine = qcom_ice_create(&pdev->dev, base); - if (IS_ERR(engine)) + if (IS_ERR(engine)) { + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return PTR_ERR(engine); + } - platform_set_drvdata(pdev, engine); + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return 0; } +static void qcom_ice_remove(struct platform_device *pdev) +{ + unsigned long phandle = pdev->dev.of_node->phandle; + + guard(mutex)(&ice_mutex); + xa_store(&ice_handles, phandle, NULL, GFP_KERNEL); +} + static const struct of_device_id qcom_ice_of_match_table[] = { { .compatible = "qcom,inline-crypto-engine" }, { }, @@ -742,6 +777,7 @@ MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table); static struct platform_driver qcom_ice_driver = { .probe = qcom_ice_probe, + .remove = qcom_ice_remove, .driver = { .name = "qcom-ice", .of_match_table = qcom_ice_of_match_table, diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index c255662b8fc3..259c41f0c34e 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -381,7 +381,7 @@ static int qmp_cooling_device_add(struct qmp *qmp, qmp_cdev->qmp = qmp; qmp_cdev->state = !qmp_cdev_max_state; qmp_cdev->name = cdev_name; - qmp_cdev->cdev = devm_thermal_of_cooling_device_register + qmp_cdev->cdev = devm_thermal_of_child_cooling_device_register (qmp->dev, node, cdev_name, qmp_cdev, &qmp_cooling_device_ops); diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c index ab75d50cc85c..2f69e104c838 100644 --- a/drivers/soc/tegra/cbb/tegra194-cbb.c +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c @@ -2342,7 +2342,7 @@ static int __init tegra194_cbb_init(void) { return platform_driver_register(&tegra194_cbb_driver); } -pure_initcall(tegra194_cbb_init); +core_initcall(tegra194_cbb_init); static void __exit tegra194_cbb_exit(void) { diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c b/drivers/soc/tegra/cbb/tegra234-cbb.c index fb26f085f691..785072fa4e85 100644 --- a/drivers/soc/tegra/cbb/tegra234-cbb.c +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c @@ -1774,7 +1774,7 @@ static int __init tegra234_cbb_init(void) { return platform_driver_register(&tegra234_cbb_driver); } -pure_initcall(tegra234_cbb_init); +core_initcall(tegra234_cbb_init); static void __exit tegra234_cbb_exit(void) { diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b563f49e2197..8782514bb89b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1085,6 +1085,15 @@ config SPI_SG2044_NOR also supporting 3Byte address devices and 4Byte address devices. +config SPI_SPACEMIT_K1 + tristate "K1 SPI Controller" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF + imply MMP_PDMA if ARCH_SPACEMIT + default m if ARCH_SPACEMIT + help + Enable support for the SpacemiT K1 SPI controller. + config SPI_SPRD tristate "Spreadtrum SPI controller" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9d36190a9884..9fa12498ce8c 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -143,6 +143,7 @@ obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o obj-$(CONFIG_SPI_SG2044_NOR) += spi-sg2044-nor.o +obj-$(CONFIG_SPI_SPACEMIT_K1) += spi-spacemit-k1.o obj-$(CONFIG_SPI_SPRD) += spi-sprd.o obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o obj-$(CONFIG_SPI_STM32) += spi-stm32.o diff --git a/drivers/spi/spi-airoha-snfi.c b/drivers/spi/spi-airoha-snfi.c index 7b6c09f91fef..95bfde7c8e7f 100644 --- a/drivers/spi/spi-airoha-snfi.c +++ b/drivers/spi/spi-airoha-snfi.c @@ -546,7 +546,7 @@ static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc) if (desc->info.length > SPI_NAND_CACHE_SIZE) return -E2BIG; - if (!airoha_snand_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!airoha_snand_supports_op(desc->mem, desc->info.op_tmpl)) return -EOPNOTSUPP; return 0; @@ -572,7 +572,7 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, * DUALIO and QUADIO opcodes are not supported by the spi controller, * replace them with supported opcodes. */ - opcode = desc->info.op_tmpl.cmd.opcode; + opcode = desc->info.op_tmpl->cmd.opcode; switch (opcode) { case SPI_NAND_OP_READ_FROM_CACHE_SINGLE: case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST: @@ -761,7 +761,7 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, /* minimum oob size is 64 */ bytes = round_up(offs + len, 64); - opcode = desc->info.op_tmpl.cmd.opcode; + opcode = desc->info.op_tmpl->cmd.opcode; switch (opcode) { case SPI_NAND_OP_PROGRAM_LOAD_SINGLE: case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE: diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c index fc81de2610ef..3de7df73f216 100644 --- a/drivers/spi/spi-altera-platform.c +++ b/drivers/spi/spi-altera-platform.c @@ -39,12 +39,12 @@ static int altera_spi_probe(struct platform_device *pdev) enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; struct altera_spi *hw; struct spi_controller *host; - int err = -ENODEV; + int err; u16 i; - host = spi_alloc_host(&pdev->dev, sizeof(struct altera_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct altera_spi)); if (!host) - return err; + return -ENOMEM; /* setup the host state. */ host->bus_num = -1; @@ -54,8 +54,7 @@ static int altera_spi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid number of chipselect: %u\n", pdata->num_chipselect); - err = -EINVAL; - goto exit; + return -EINVAL; } host->num_chipselect = pdata->num_chipselect; @@ -80,7 +79,7 @@ static int altera_spi_probe(struct platform_device *pdev) hw->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!hw->regmap) { dev_err(&pdev->dev, "get regmap failed\n"); - goto exit; + return -ENODEV; } regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); @@ -90,17 +89,14 @@ static int altera_spi_probe(struct platform_device *pdev) void __iomem *res; res = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(res)) { - err = PTR_ERR(res); - goto exit; - } + if (IS_ERR(res)) + return PTR_ERR(res); hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, &spi_altera_config); if (IS_ERR(hw->regmap)) { dev_err(&pdev->dev, "regmap mmio init failed\n"); - err = PTR_ERR(hw->regmap); - goto exit; + return PTR_ERR(hw->regmap); } } @@ -112,12 +108,12 @@ static int altera_spi_probe(struct platform_device *pdev) err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, pdev->name, host); if (err) - goto exit; + return err; } err = devm_spi_register_controller(&pdev->dev, host); if (err) - goto exit; + return err; if (pdata) { for (i = 0; i < pdata->num_devices; i++) { @@ -131,9 +127,6 @@ static int altera_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq); return 0; -exit: - spi_controller_put(host); - return err; } #ifdef CONFIG_OF @@ -146,8 +139,8 @@ MODULE_DEVICE_TABLE(of, altera_spi_match); #endif /* CONFIG_OF */ static const struct platform_device_id altera_spi_ids[] = { - { DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, - { "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV }, + { .name = DRV_NAME, .driver_data = ALTERA_SPI_TYPE_UNKNOWN }, + { .name = "subdev_spi_altera", .driver_data = ALTERA_SPI_TYPE_SUBDEV }, { } }; MODULE_DEVICE_TABLE(platform, altera_spi_ids); diff --git a/drivers/spi/spi-amlogic-spifc-a1.c b/drivers/spi/spi-amlogic-spifc-a1.c index 7ee4c92e6e09..77a2c11bec5e 100644 --- a/drivers/spi/spi-amlogic-spifc-a1.c +++ b/drivers/spi/spi-amlogic-spifc-a1.c @@ -206,10 +206,9 @@ static int amlogic_spifc_a1_read(struct amlogic_spifc_a1 *spifc, void *buf, u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL3_REG); int ret; - val &= ~(SPIFC_A1_USER_DIN_MODE | SPIFC_A1_USER_DIN_BYTES); val |= SPIFC_A1_USER_DIN_ENABLE; - val |= FIELD_PREP(SPIFC_A1_USER_DIN_MODE, mode); - val |= FIELD_PREP(SPIFC_A1_USER_DIN_BYTES, size); + FIELD_MODIFY(SPIFC_A1_USER_DIN_MODE, &val, mode); + FIELD_MODIFY(SPIFC_A1_USER_DIN_BYTES, &val, size); writel(val, spifc->base + SPIFC_A1_USER_CTRL3_REG); ret = amlogic_spifc_a1_request(spifc, true); diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c index f9de2d2c9213..afc8af04638d 100644 --- a/drivers/spi/spi-amlogic-spisg.c +++ b/drivers/spi/spi-amlogic-spisg.c @@ -258,8 +258,7 @@ static int aml_spisg_setup_transfer(struct spisg_device *spisg, int ret; memset(desc, 0, sizeof(*desc)); - if (exdesc) - memset(exdesc, 0, sizeof(*exdesc)); + memset(exdesc, 0, sizeof(*exdesc)); aml_spisg_set_speed(spisg, xfer->speed_hz); xfer->effective_speed_hz = spisg->effective_speed_hz; @@ -601,14 +600,11 @@ static int aml_spisg_prepare_message(struct spi_controller *ctlr, spisg->bytes_per_word = spi->bits_per_word >> 3; - spisg->cfg_spi &= ~CFG_SLAVE_SELECT; - spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0)); - - spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_B_L_ENDIAN | CFG_HALF_DUPLEX); - spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) | - FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) | - FIELD_PREP(CFG_B_L_ENDIAN, !!(spi->mode & SPI_LSB_FIRST)) | - FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE)); + FIELD_MODIFY(CFG_SLAVE_SELECT, &spisg->cfg_spi, spi_get_chipselect(spi, 0)); + FIELD_MODIFY(CFG_CPOL, &spisg->cfg_bus, !!(spi->mode & SPI_CPOL)); + FIELD_MODIFY(CFG_CPHA, &spisg->cfg_bus, !!(spi->mode & SPI_CPHA)); + FIELD_MODIFY(CFG_B_L_ENDIAN, &spisg->cfg_bus, !!(spi->mode & SPI_LSB_FIRST)); + FIELD_MODIFY(CFG_HALF_DUPLEX, &spisg->cfg_bus, !!(spi->mode & SPI_3WIRE)); return 0; } diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 78248729d3e9..ca2faa265ca0 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -818,17 +818,13 @@ static int a3700_spi_probe(struct platform_device *pdev) u32 num_cs = 0; int irq, ret = 0; - host = spi_alloc_host(dev, sizeof(*spi)); - if (!host) { - dev_err(dev, "host allocation failed\n"); - ret = -ENOMEM; - goto out; - } + host = devm_spi_alloc_host(dev, sizeof(*spi)); + if (!host) + return -ENOMEM; if (of_property_read_u32(dev->of_node, "num-cs", &num_cs)) { dev_err(dev, "could not find num-cs\n"); - ret = -ENXIO; - goto error; + return -ENXIO; } host->bus_num = pdev->id; @@ -849,25 +845,20 @@ static int a3700_spi_probe(struct platform_device *pdev) spi->host = host; spi->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(spi->base)) { - ret = PTR_ERR(spi->base); - goto error; - } + if (IS_ERR(spi->base)) + return PTR_ERR(spi->base); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = -ENXIO; - goto error; - } + if (irq < 0) + return -ENXIO; + spi->irq = irq; init_completion(&spi->done); spi->clk = devm_clk_get_prepared(dev, NULL); - if (IS_ERR(spi->clk)) { - dev_err(dev, "could not find clk: %ld\n", PTR_ERR(spi->clk)); - goto error; - } + if (IS_ERR(spi->clk)) + return dev_err_probe(dev, PTR_ERR(spi->clk), "could not find clk\n"); host->max_speed_hz = min_t(unsigned long, A3700_SPI_MAX_SPEED_HZ, clk_get_rate(spi->clk)); @@ -878,23 +869,16 @@ static int a3700_spi_probe(struct platform_device *pdev) ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0, dev_name(dev), host); - if (ret) { - dev_err(dev, "could not request IRQ: %d\n", ret); - goto error; - } + if (ret) + return dev_err_probe(dev, ret, "could not request IRQ\n"); ret = devm_spi_register_controller(dev, host); if (ret) { dev_err(dev, "Failed to register host\n"); - goto error; + return ret; } return 0; - -error: - spi_controller_put(host); -out: - return ret; } static struct platform_driver a3700_spi_driver = { diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index c21323e07d3c..e9fd9f3b552b 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -697,7 +697,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) { struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->controller); struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)]; - struct spi_mem_op *op = &desc->info.op_tmpl; + struct spi_mem_op *op = desc->info.op_tmpl; u32 ctl_val; int ret = 0; @@ -769,7 +769,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, if (chip->ahb_window_size < offset + len || chip->force_user_mode) { int ret; - ret = aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf); + ret = aspeed_spi_read_user(chip, desc->info.op_tmpl, offset, len, buf); if (ret < 0) return ret; } else { @@ -891,7 +891,7 @@ static int aspeed_spi_user_unprepare_msg(struct spi_controller *ctlr, static void aspeed_spi_user_transfer_tx(struct aspeed_spi *aspi, struct spi_device *spi, const u8 *tx_buf, u8 *rx_buf, - void *dst, u32 len) + void __iomem *dst, u32 len) { const struct aspeed_spi_data *data = aspi->data; bool full_duplex_transfer = data->full_duplex && tx_buf == rx_buf; @@ -936,7 +936,7 @@ static int aspeed_spi_user_transfer(struct spi_controller *ctlr, aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA); aspeed_spi_user_transfer_tx(aspi, spi, tx_buf, rx_buf, - (void *)ahb_base, xfer->len); + ahb_base, xfer->len); } if (rx_buf && rx_buf != tx_buf) { @@ -1467,8 +1467,7 @@ end_calib: * must contains the highest number of consecutive "pass" * results and not span across multiple rows. */ -static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, - u8 buf[rows][cols]) +static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, u8 *buf) { int r = 0, c = 0; int max = 0; @@ -1478,7 +1477,7 @@ static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, for (j = 0; j < cols;) { int k = j; - while (k < cols && buf[i][k]) + while (k < cols && buf[(i * cols) + k]) k++; if (k - j > max) { @@ -1541,7 +1540,7 @@ static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, } } - calib_point = aspeed_spi_ast2600_optimized_timing(6, 17, calib_res); + calib_point = aspeed_spi_ast2600_optimized_timing(6, 17, &calib_res[0][0]); /* No good setting for this frequency */ if (calib_point == 0) return -1; diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index 79edc1cd13c0..77cad4118202 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -16,7 +16,6 @@ #include <linux/gpio/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/spi/spi.h> @@ -496,14 +495,13 @@ static int at91_usart_spi_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - ret = -ENOMEM; - controller = spi_alloc_host(&pdev->dev, sizeof(*aus)); + controller = devm_spi_alloc_host(&pdev->dev, sizeof(*aus)); if (!controller) - goto at91_usart_spi_probe_fail; + return -ENOMEM; ret = at91_usart_gpio_setup(pdev); if (ret) - goto at91_usart_spi_probe_fail; + return ret; controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH; controller->dev.of_node = pdev->dev.parent->of_node; @@ -525,10 +523,8 @@ static int at91_usart_spi_probe(struct platform_device *pdev) aus->dev = &pdev->dev; aus->regs = devm_ioremap_resource(&pdev->dev, regs); - if (IS_ERR(aus->regs)) { - ret = PTR_ERR(aus->regs); - goto at91_usart_spi_probe_fail; - } + if (IS_ERR(aus->regs)) + return PTR_ERR(aus->regs); aus->irq = irq; aus->clk = clk; @@ -536,11 +532,11 @@ static int at91_usart_spi_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, at91_usart_spi_interrupt, 0, dev_name(&pdev->dev), controller); if (ret) - goto at91_usart_spi_probe_fail; + return ret; ret = clk_prepare_enable(clk); if (ret) - goto at91_usart_spi_probe_fail; + return ret; aus->spi_clk = clk_get_rate(clk); at91_usart_spi_init(aus); @@ -571,43 +567,22 @@ at91_usart_fail_register_controller: at91_usart_spi_release_dma(controller); at91_usart_fail_dma: clk_disable_unprepare(clk); -at91_usart_spi_probe_fail: - spi_controller_put(controller); - return ret; -} - -__maybe_unused static int at91_usart_spi_runtime_suspend(struct device *dev) -{ - struct spi_controller *ctlr = dev_get_drvdata(dev); - struct at91_usart_spi *aus = spi_controller_get_devdata(ctlr); - - clk_disable_unprepare(aus->clk); - pinctrl_pm_select_sleep_state(dev); - - return 0; -} - -__maybe_unused static int at91_usart_spi_runtime_resume(struct device *dev) -{ - struct spi_controller *ctrl = dev_get_drvdata(dev); - struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl); - - pinctrl_pm_select_default_state(dev); - return clk_prepare_enable(aus->clk); + return ret; } __maybe_unused static int at91_usart_spi_suspend(struct device *dev) { struct spi_controller *ctrl = dev_get_drvdata(dev); + struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl); int ret; ret = spi_controller_suspend(ctrl); if (ret) return ret; - if (!pm_runtime_suspended(dev)) - at91_usart_spi_runtime_suspend(dev); + clk_disable_unprepare(aus->clk); + pinctrl_pm_select_sleep_state(dev); return 0; } @@ -618,11 +593,11 @@ __maybe_unused static int at91_usart_spi_resume(struct device *dev) struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl); int ret; - if (!pm_runtime_suspended(dev)) { - ret = at91_usart_spi_runtime_resume(dev); - if (ret) - return ret; - } + pinctrl_pm_select_default_state(dev); + + ret = clk_prepare_enable(aus->clk); + if (ret) + return ret; at91_usart_spi_init(aus); @@ -634,20 +609,14 @@ static void at91_usart_spi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct at91_usart_spi *aus = spi_controller_get_devdata(ctlr); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); at91_usart_spi_release_dma(ctlr); clk_disable_unprepare(aus->clk); - - spi_controller_put(ctlr); } static const struct dev_pm_ops at91_usart_spi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume) - SET_RUNTIME_PM_OPS(at91_usart_spi_runtime_suspend, - at91_usart_spi_runtime_resume, NULL) }; static struct platform_driver at91_usart_spi_driver = { diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c index 3832d9db3cbf..6d4b6aeb3f5b 100644 --- a/drivers/spi/spi-atcspi200.c +++ b/drivers/spi/spi-atcspi200.c @@ -550,7 +550,7 @@ static int atcspi_probe(struct platform_device *pdev) struct resource *mem_res; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi)); if (!host) return -ENOMEM; @@ -559,28 +559,24 @@ static int atcspi_probe(struct platform_device *pdev) spi->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, host); - mutex_init(&spi->mutex_lock); + ret = devm_mutex_init(&pdev->dev, &spi->mutex_lock); + if (ret) + return ret; ret = atcspi_init_resources(pdev, spi, &mem_res); if (ret) - goto free_controller; + return ret; ret = atcspi_enable_clk(spi); if (ret) - goto free_controller; + return ret; atcspi_init_controller(pdev, spi, host, mem_res); ret = atcspi_setup(spi); if (ret) - goto free_controller; + return ret; - ret = devm_spi_register_controller(&pdev->dev, host); - if (ret) { - dev_err_probe(spi->dev, ret, - "Failed to register SPI controller\n"); - goto free_controller; - } spi->use_dma = false; if (ATCSPI_DMA_SUPPORT) { ret = atcspi_configure_dma(spi); @@ -591,12 +587,12 @@ static int atcspi_probe(struct platform_device *pdev) spi->use_dma = true; } - return 0; + ret = devm_spi_register_controller(&pdev->dev, host); + if (ret) + return dev_err_probe(spi->dev, ret, + "Failed to register SPI controller\n"); -free_controller: - mutex_destroy(&spi->mutex_lock); - spi_controller_put(host); - return ret; + return 0; } static int atcspi_suspend(struct device *dev) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 42db85d7ff8e..c8012c82c3a7 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -559,6 +559,38 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, u8 bits_per_word) return err; } +static void atmel_spi_release_dma(void *data) +{ + struct spi_controller *host = data; + struct atmel_spi *as = spi_controller_get_devdata(host); + struct device *dev = &as->pdev->dev; + + if (host->dma_tx) { + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; + } + + if (host->dma_rx) { + dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + } + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + if (as->addr_tx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_tx_bbuf, + as->dma_addr_tx_bbuf); + as->addr_tx_bbuf = NULL; + } + if (as->addr_rx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_rx_bbuf, + as->dma_addr_rx_bbuf); + as->addr_rx_bbuf = NULL; + } + } +} + static int atmel_spi_configure_dma(struct spi_controller *host, struct atmel_spi *as) { @@ -569,7 +601,8 @@ static int atmel_spi_configure_dma(struct spi_controller *host, if (IS_ERR(host->dma_tx)) { err = PTR_ERR(host->dma_tx); dev_dbg(dev, "No TX DMA channel, DMA is disabled\n"); - goto error_clear; + host->dma_tx = NULL; + return err; } host->dma_rx = dma_request_chan(dev, "rx"); @@ -580,26 +613,45 @@ static int atmel_spi_configure_dma(struct spi_controller *host, * requested tx channel. */ dev_dbg(dev, "No RX DMA channel, DMA is disabled\n"); - goto error; + host->dma_rx = NULL; + goto err_release_dma; } err = atmel_spi_dma_slave_config(as, 8); if (err) - goto error; + goto err_release_dma; + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + as->addr_tx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_tx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_tx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + + as->addr_rx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_rx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_rx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + } + + err = devm_add_action_or_reset(dev, atmel_spi_release_dma, host); + if (err) + return err; dev_info(&as->pdev->dev, - "Using %s (tx) and %s (rx) for DMA transfers\n", - dma_chan_name(host->dma_tx), - dma_chan_name(host->dma_rx)); + "Using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(host->dma_tx), dma_chan_name(host->dma_rx)); return 0; -error: - if (!IS_ERR(host->dma_rx)) - dma_release_channel(host->dma_rx); - if (!IS_ERR(host->dma_tx)) - dma_release_channel(host->dma_tx); -error_clear: - host->dma_tx = host->dma_rx = NULL; + +err_release_dma: + atmel_spi_release_dma(host); + return err; } @@ -611,18 +663,6 @@ static void atmel_spi_stop_dma(struct spi_controller *host) dmaengine_terminate_all(host->dma_tx); } -static void atmel_spi_release_dma(struct spi_controller *host) -{ - if (host->dma_rx) { - dma_release_channel(host->dma_rx); - host->dma_rx = NULL; - } - if (host->dma_tx) { - dma_release_channel(host->dma_tx); - host->dma_tx = NULL; - } -} - /* This function is called by the DMA driver from tasklet context */ static void dma_callback(void *data) { @@ -1528,7 +1568,7 @@ static int atmel_spi_probe(struct platform_device *pdev) return PTR_ERR(clk); /* setup spi core then atmel-specific driver state */ - host = spi_alloc_host(&pdev->dev, sizeof(*as)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*as)); if (!host) return -ENOMEM; @@ -1555,18 +1595,15 @@ static int atmel_spi_probe(struct platform_device *pdev) as->pdev = pdev; as->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); - if (IS_ERR(as->regs)) { - ret = PTR_ERR(as->regs); - goto out_unmap_regs; - } + if (IS_ERR(as->regs)) + return PTR_ERR(as->regs); + as->phybase = regs->start; as->irq = irq; as->clk = clk; as->gclk = devm_clk_get_optional(&pdev->dev, "spi_gclk"); - if (IS_ERR(as->gclk)) { - ret = PTR_ERR(as->gclk); - goto out_unmap_regs; - } + if (IS_ERR(as->gclk)) + return PTR_ERR(as->gclk); init_completion(&as->xfer_completion); @@ -1576,39 +1613,14 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_pdc = false; if (as->caps.has_dma_support) { ret = atmel_spi_configure_dma(host, as); - if (ret == 0) { + if (ret == 0) as->use_dma = true; - } else if (ret == -EPROBE_DEFER) { - goto out_unmap_regs; - } + else if (ret == -EPROBE_DEFER) + return ret; } else if (as->caps.has_pdc_support) { as->use_pdc = true; } - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_rx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_rx_bbuf) { - as->use_dma = false; - } else { - as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_tx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_tx_bbuf) { - as->use_dma = false; - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } - if (!as->use_dma) - dev_info(host->dev.parent, - " can not allocate dma coherent memory\n"); - } - if (as->caps.has_dma_support && !as->use_dma) dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); @@ -1620,12 +1632,12 @@ static int atmel_spi_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), host); } if (ret) - goto out_unmap_regs; + return ret; /* Initialize the hardware */ ret = clk_prepare_enable(clk); if (ret) - goto out_free_irq; + return ret; /* * In cases where the peripheral clock is higher,the FLEX_SPI_CSRx.SCBR @@ -1668,18 +1680,13 @@ static int atmel_spi_probe(struct platform_device *pdev) out_free_dma: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - - if (as->use_dma) - atmel_spi_release_dma(host); - spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - clk_disable_unprepare(as->gclk); + if (as->gclk) + clk_disable_unprepare(as->gclk); out_disable_clk: clk_disable_unprepare(clk); -out_free_irq: -out_unmap_regs: - spi_controller_put(host); + return ret; } @@ -1688,25 +1695,13 @@ static void atmel_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct atmel_spi *as = spi_controller_get_devdata(host); - spi_controller_get(host); - pm_runtime_get_sync(&pdev->dev); spi_unregister_controller(host); /* reset the hardware and block queue progress */ - if (as->use_dma) { + if (as->use_dma) atmel_spi_stop_dma(host); - atmel_spi_release_dma(host); - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_tx_bbuf, - as->dma_addr_tx_bbuf); - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } spin_lock_irq(&as->lock); spi_writel(as, CR, SPI_BIT(SWRST)); @@ -1720,8 +1715,6 @@ static void atmel_spi_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(host); } static int atmel_spi_runtime_suspend(struct device *dev) diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index e935e8ab9cfd..58012e1b5ae7 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -783,7 +783,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) "failed get pll clk rate\n"); } - host = spi_alloc_host(&pdev->dev, sizeof(*bs)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*bs)); if (!host) return dev_err_probe(dev, -ENOMEM, "alloc host no mem\n"); @@ -796,10 +796,8 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0)); bs->wait_mode = HSSPI_WAIT_MODE_POLLING; bs->prepend_buf = devm_kzalloc(dev, HSSPI_BUFFER_LEN, GFP_KERNEL); - if (!bs->prepend_buf) { - ret = -ENOMEM; - goto out_put_host; - } + if (!bs->prepend_buf) + return -ENOMEM; mutex_init(&bs->bus_mutex); mutex_init(&bs->msg_mutex); @@ -845,7 +843,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) pdev->name, bs); if (ret) - goto out_put_host; + return ret; } pm_runtime_enable(&pdev->dev); @@ -869,8 +867,7 @@ out_sysgroup_disable: sysfs_remove_group(&pdev->dev.kobj, &bcm63xx_hsspi_group); out_pm_disable: pm_runtime_disable(&pdev->dev); -out_put_host: - spi_controller_put(host); + return ret; } @@ -880,15 +877,11 @@ static void bcm63xx_hsspi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct bcm63xx_hsspi *bs = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* reset the hardware and block queue progress */ __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); sysfs_remove_group(&pdev->dev.kobj, &bcm63xx_hsspi_group); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 40cd7efc4b54..43d7b54e3ae8 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -477,8 +477,7 @@ static const struct platform_device_id bcm63xx_spi_dev_match[] = { .name = "bcm6358-spi", .driver_data = (unsigned long)bcm6358_spi_reg_offsets, }, - { - }, + { } }; MODULE_DEVICE_TABLE(platform, bcm63xx_spi_dev_match); @@ -541,11 +540,9 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) if (IS_ERR(reset)) return PTR_ERR(reset); - host = spi_alloc_host(dev, sizeof(*bs)); - if (!host) { - dev_err(dev, "out of memory\n"); + host = devm_spi_alloc_host(dev, sizeof(*bs)); + if (!host) return -ENOMEM; - } bs = spi_controller_get_devdata(host); init_completion(&bs->done); @@ -554,10 +551,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) bs->pdev = pdev; bs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(bs->regs)) { - ret = PTR_ERR(bs->regs); - goto out_err; - } + if (IS_ERR(bs->regs)) + return PTR_ERR(bs->regs); bs->irq = irq; bs->clk = clk; @@ -568,7 +563,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) pdev->name, host); if (ret) { dev_err(dev, "unable to request irq\n"); - goto out_err; + return ret; } host->bus_num = bus_num; @@ -587,7 +582,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) /* Initialize hardware */ ret = clk_prepare_enable(bs->clk); if (ret) - goto out_err; + return ret; ret = reset_control_reset(reset); if (ret) { @@ -615,8 +610,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) out_clk_disable: clk_disable_unprepare(clk); -out_err: - spi_controller_put(host); + return ret; } @@ -625,8 +619,6 @@ static void bcm63xx_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct bcm63xx_spi *bs = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* reset spi block */ @@ -634,8 +626,6 @@ static void bcm63xx_spi_remove(struct platform_device *pdev) /* HW shutdown */ clk_disable_unprepare(bs->clk); - - spi_controller_put(host); } static int bcm63xx_spi_suspend(struct device *dev) diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 9f03ac217319..fb288ed56d94 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -420,11 +420,6 @@ EXPORT_SYMBOL_GPL(spi_bitbang_init); * This routine registers the spi_controller, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop * processing those requests, call spi_bitbang_stop(). - * - * On success, this routine will take a reference to the controller. The caller - * is responsible for calling spi_bitbang_stop() to decrement the reference and - * spi_controller_put() as counterpart of spi_alloc_host() to prevent a memory - * leak. */ int spi_bitbang_start(struct spi_bitbang *bitbang) { @@ -438,11 +433,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - ret = spi_register_controller(spi_controller_get(ctlr)); - if (ret) - spi_controller_put(ctlr); - - return ret; + return spi_register_controller(ctlr); } EXPORT_SYMBOL_GPL(spi_bitbang_start); diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 057381e56a7f..aaba1a3ad577 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1478,7 +1478,6 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) int ret; struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller); struct device *dev = &cqspi->pdev->dev; - const struct cqspi_driver_platdata *ddata = of_device_get_match_data(dev); if (refcount_read(&cqspi->inflight_ops) == 0) return -ENODEV; @@ -1494,18 +1493,15 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) return -EBUSY; } - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(&mem->spi->dev, "resume failed with %d\n", ret); - goto dec_inflight_refcount; - } + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(&mem->spi->dev, "resume failed with %d\n", ret); + goto dec_inflight_refcount; } ret = cqspi_mem_process(mem, op); - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) - pm_runtime_put_autosuspend(dev); + pm_runtime_put_autosuspend(dev); if (ret) dev_err(&mem->spi->dev, "operation failed with %d\n", ret); @@ -1957,13 +1953,11 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->current_cs = -1; cqspi->sclk = 0; - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT); - pm_runtime_use_autosuspend(dev); - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - } + pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(dev); + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); host->num_chipselect = cqspi->num_chipselect; @@ -1993,12 +1987,11 @@ release_dma_chan: if (cqspi->rx_chan) dma_release_channel(cqspi->rx_chan); disable_rpm: - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); - pm_runtime_dont_use_autosuspend(dev); - } + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + cqspi_controller_enable(cqspi, 0); disable_clks: clk_bulk_disable_unprepare(CLK_QSPI_NUM, cqspi->clks); @@ -2008,13 +2001,10 @@ disable_clks: static void cqspi_remove(struct platform_device *pdev) { - const struct cqspi_driver_platdata *ddata; struct cqspi_st *cqspi = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; + const struct cqspi_driver_platdata *ddata = cqspi->ddata; int ret = 0; - ddata = of_device_get_match_data(dev); - spi_unregister_controller(cqspi->host); refcount_set(&cqspi->refcount, 0); @@ -2033,12 +2023,10 @@ static void cqspi_remove(struct platform_device *pdev) clk_bulk_disable_unprepare(CLK_QSPI_NUM, cqspi->clks); } - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - } + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); } static int cqspi_runtime_suspend(struct device *dev) diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 895b4b3276a5..e1b337789fce 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -369,6 +369,8 @@ struct cdns_xspi_dev { void *in_buffer; const void *out_buffer; + /* Slave DMA data width in bytes (4 or 8). */ + u8 dma_data_width; u8 hw_num_banks; @@ -453,8 +455,7 @@ static bool cdns_mrvl_xspi_setup_clock(struct cdns_xspi_dev *cdns_xspi, writel(clk_reg, cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG); clk_reg = FIELD_PREP(MRVL_XSPI_CLK_DIV, i); - clk_reg &= ~MRVL_XSPI_CLK_DIV; - clk_reg |= FIELD_PREP(MRVL_XSPI_CLK_DIV, i); + FIELD_MODIFY(MRVL_XSPI_CLK_DIV, &clk_reg, i); clk_reg |= MRVL_XSPI_CLK_ENABLE; clk_reg |= MRVL_XSPI_IRQ_ENABLE; update_clk = true; @@ -573,11 +574,56 @@ static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi) ctrl_features = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_FEATURES_REG); cdns_xspi->hw_num_banks = FIELD_GET(CDNS_XSPI_NUM_BANKS, ctrl_features); + cdns_xspi->dma_data_width = (ctrl_features & CDNS_XSPI_DMA_DATA_WIDTH) ? 8 : 4; cdns_xspi->set_interrupts_handler(cdns_xspi, false); return 0; } +static inline void cdns_xspi_sdma_read(struct cdns_xspi_dev *cdns_xspi, size_t len) +{ + void __iomem *src = cdns_xspi->sdmabase; + void *buf = cdns_xspi->in_buffer; + size_t offset = 0; + + if (cdns_xspi->dma_data_width == 4) { + if (IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED((uintptr_t)buf, 4)) { + ioread32_rep(src, buf, len >> 2); + offset = len & ~0x3; + len -= offset; + } + } else { + if (IS_ALIGNED((uintptr_t)src, 8) && IS_ALIGNED((uintptr_t)buf, 8)) { + readsq(src, buf, len >> 3); + offset = len & ~0x7; + len -= offset; + } + } + ioread8_rep(src, (u8 *)buf + offset, len); +} + +static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t len) +{ + void __iomem *dst = cdns_xspi->sdmabase; + const void *buf = cdns_xspi->out_buffer; + size_t offset = 0; + + if (cdns_xspi->dma_data_width == 4) { + if (IS_ALIGNED((uintptr_t)dst, 4) && IS_ALIGNED((uintptr_t)buf, 4)) { + iowrite32_rep(dst, buf, len >> 2); + offset = len & ~0x3; + len -= offset; + } + } else { + if (IS_ALIGNED((uintptr_t)dst, 8) && IS_ALIGNED((uintptr_t)buf, 8)) { + writesq(dst, buf, len >> 3); + offset = len & ~0x7; + len -= offset; + } + } + iowrite8_rep(dst, (const u8 *)buf + offset, len); +} + static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) { u32 sdma_size, sdma_trd_info; @@ -589,13 +635,11 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) switch (sdma_dir) { case CDNS_XSPI_SDMA_DIR_READ: - ioread8_rep(cdns_xspi->sdmabase, - cdns_xspi->in_buffer, sdma_size); + cdns_xspi_sdma_read(cdns_xspi, sdma_size); break; case CDNS_XSPI_SDMA_DIR_WRITE: - iowrite8_rep(cdns_xspi->sdmabase, - cdns_xspi->out_buffer, sdma_size); + cdns_xspi_sdma_write(cdns_xspi, sdma_size); break; } } diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 891e2ba36958..9b4e5b7013ae 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -635,7 +635,7 @@ static int cdns_target_abort(struct spi_controller *ctlr) */ static int cdns_spi_probe(struct platform_device *pdev) { - int ret = 0, irq; + int ret, irq; struct spi_controller *ctlr; struct cdns_spi *xspi; u32 num_cs; @@ -643,9 +643,9 @@ static int cdns_spi_probe(struct platform_device *pdev) target = of_property_read_bool(pdev->dev.of_node, "spi-slave"); if (target) - ctlr = spi_alloc_target(&pdev->dev, sizeof(*xspi)); + ctlr = devm_spi_alloc_target(&pdev->dev, sizeof(*xspi)); else - ctlr = spi_alloc_host(&pdev->dev, sizeof(*xspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xspi)); if (!ctlr) return -ENOMEM; @@ -654,23 +654,19 @@ static int cdns_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ctlr); xspi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(xspi->regs)) { - ret = PTR_ERR(xspi->regs); - goto remove_ctlr; - } + if (IS_ERR(xspi->regs)) + return PTR_ERR(xspi->regs); xspi->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(xspi->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); - ret = PTR_ERR(xspi->pclk); - goto remove_ctlr; + return PTR_ERR(xspi->pclk); } xspi->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi"); if (IS_ERR(xspi->rstc)) { - ret = dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc), - "Cannot get SPI reset.\n"); - goto remove_ctlr; + return dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc), + "Cannot get SPI reset.\n"); } reset_control_assert(xspi->rstc); @@ -679,8 +675,7 @@ static int cdns_spi_probe(struct platform_device *pdev) xspi->ref_clk = devm_clk_get_enabled(&pdev->dev, "ref_clk"); if (IS_ERR(xspi->ref_clk)) { dev_err(&pdev->dev, "ref_clk clock not found.\n"); - ret = PTR_ERR(xspi->ref_clk); - goto remove_ctlr; + return PTR_ERR(xspi->ref_clk); } if (!spi_controller_is_target(ctlr)) { @@ -710,7 +705,7 @@ static int cdns_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; - goto clk_dis_all; + goto err_disable_rpm; } ret = devm_request_irq(&pdev->dev, irq, cdns_spi_irq, @@ -718,7 +713,7 @@ static int cdns_spi_probe(struct platform_device *pdev) if (ret != 0) { ret = -ENXIO; dev_err(&pdev->dev, "request_irq failed\n"); - goto clk_dis_all; + goto err_disable_rpm; } ctlr->use_gpio_descriptors = true; @@ -748,23 +743,22 @@ static int cdns_spi_probe(struct platform_device *pdev) ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_controller failed\n"); - goto clk_dis_all; + goto err_disable_rpm; } if (!spi_controller_is_target(ctlr)) pm_runtime_put_autosuspend(&pdev->dev); - return ret; + return 0; -clk_dis_all: +err_disable_rpm: if (!spi_controller_is_target(ctlr)) { pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); } -remove_ctlr: - spi_controller_put(ctlr); + return ret; } @@ -785,8 +779,6 @@ static void cdns_spi_remove(struct platform_device *pdev) if (!spi_controller_is_target(ctlr)) ret = pm_runtime_get_sync(&pdev->dev); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); if (ret >= 0) @@ -798,8 +790,6 @@ static void cdns_spi_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); } - - spi_controller_put(ctlr); } /** diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c index b95bfa6a3013..28c922c72068 100644 --- a/drivers/spi/spi-cavium-octeon.c +++ b/drivers/spi/spi-cavium-octeon.c @@ -23,17 +23,15 @@ static int octeon_spi_probe(struct platform_device *pdev) struct octeon_spi *p; int err = -ENOENT; - host = spi_alloc_host(&pdev->dev, sizeof(struct octeon_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct octeon_spi)); if (!host) return -ENOMEM; p = spi_controller_get_devdata(host); platform_set_drvdata(pdev, host); reg_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(reg_base)) { - err = PTR_ERR(reg_base); - goto fail; - } + if (IS_ERR(reg_base)) + return PTR_ERR(reg_base); p->register_base = reg_base; p->sys_freq = octeon_get_io_clock_rate(); @@ -57,15 +55,12 @@ static int octeon_spi_probe(struct platform_device *pdev) err = spi_register_controller(host); if (err) { dev_err(&pdev->dev, "register host failed: %d\n", err); - goto fail; + return err; } dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); return 0; -fail: - spi_controller_put(host); - return err; } static void octeon_spi_remove(struct platform_device *pdev) @@ -73,14 +68,10 @@ static void octeon_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct octeon_spi *p = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* Clear the CSENA* and put everything in a known state. */ writeq(0, p->register_base + OCTEON_SPI_CFG(p)); - - spi_controller_put(host); } static const struct of_device_id octeon_spi_match[] = { diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c index f1a9aa696c87..529b4066c922 100644 --- a/drivers/spi/spi-cavium-thunderx.c +++ b/drivers/spi/spi-cavium-thunderx.c @@ -24,7 +24,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev, struct octeon_spi *p; int ret; - host = spi_alloc_host(dev, sizeof(struct octeon_spi)); + host = devm_spi_alloc_host(dev, sizeof(struct octeon_spi)); if (!host) return -ENOMEM; @@ -32,17 +32,15 @@ static int thunderx_spi_probe(struct pci_dev *pdev, ret = pcim_enable_device(pdev); if (ret) - goto error; + return ret; ret = pcim_request_all_regions(pdev, DRV_NAME); if (ret) - goto error; + return ret; p->register_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); - if (!p->register_base) { - ret = -EINVAL; - goto error; - } + if (!p->register_base) + return -EINVAL; p->regs.config = 0x1000; p->regs.status = 0x1008; @@ -50,10 +48,8 @@ static int thunderx_spi_probe(struct pci_dev *pdev, p->regs.data = 0x1080; p->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(p->clk)) { - ret = PTR_ERR(p->clk); - goto error; - } + if (IS_ERR(p->clk)) + return PTR_ERR(p->clk); p->sys_freq = clk_get_rate(p->clk); if (!p->sys_freq) @@ -70,15 +66,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, host); - ret = spi_register_controller(host); - if (ret) - goto error; - - return 0; - -error: - spi_controller_put(host); - return ret; + return spi_register_controller(host); } static void thunderx_spi_remove(struct pci_dev *pdev) @@ -90,14 +78,10 @@ static void thunderx_spi_remove(struct pci_dev *pdev) if (!p) return; - spi_controller_get(host); - spi_unregister_controller(host); /* Put everything in a known state. */ writeq(0, p->register_base + OCTEON_SPI_CFG(p)); - - spi_controller_put(host); } static const struct pci_device_id thunderx_spi_pci_id_table[] = { diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index d6458e59d41b..382c3c81c4b9 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -99,7 +99,7 @@ static int spi_clps711x_probe(struct platform_device *pdev) if (irq < 0) return irq; - host = spi_alloc_host(&pdev->dev, sizeof(*hw)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*hw)); if (!host) return -ENOMEM; @@ -113,22 +113,16 @@ static int spi_clps711x_probe(struct platform_device *pdev) hw = spi_controller_get_devdata(host); hw->spi_clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(hw->spi_clk)) { - ret = PTR_ERR(hw->spi_clk); - goto err_out; - } + if (IS_ERR(hw->spi_clk)) + return PTR_ERR(hw->spi_clk); hw->syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); - if (IS_ERR(hw->syscon)) { - ret = PTR_ERR(hw->syscon); - goto err_out; - } + if (IS_ERR(hw->syscon)) + return PTR_ERR(hw->syscon); hw->syncio = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(hw->syncio)) { - ret = PTR_ERR(hw->syncio); - goto err_out; - } + if (IS_ERR(hw->syncio)) + return PTR_ERR(hw->syncio); /* Disable extended mode due hardware problems */ regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0); @@ -139,16 +133,9 @@ static int spi_clps711x_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0, dev_name(&pdev->dev), host); if (ret) - goto err_out; + return ret; - ret = devm_spi_register_controller(&pdev->dev, host); - if (!ret) - return 0; - -err_out: - spi_controller_put(host); - - return ret; + return devm_spi_register_controller(&pdev->dev, host); } static const struct of_device_id clps711x_spi_dt_ids[] = { diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index b45f44de85dc..3b175c1da36b 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -353,39 +353,33 @@ static int mcfqspi_probe(struct platform_device *pdev) return -EINVAL; } - host = spi_alloc_host(&pdev->dev, sizeof(*mcfqspi)); - if (host == NULL) { - dev_dbg(&pdev->dev, "spi_alloc_host failed\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*mcfqspi)); + if (host == NULL) return -ENOMEM; - } mcfqspi = spi_controller_get_devdata(host); mcfqspi->iobase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(mcfqspi->iobase)) { - status = PTR_ERR(mcfqspi->iobase); - goto fail0; - } + if (IS_ERR(mcfqspi->iobase)) + return PTR_ERR(mcfqspi->iobase); mcfqspi->irq = platform_get_irq(pdev, 0); if (mcfqspi->irq < 0) { dev_dbg(&pdev->dev, "platform_get_irq failed\n"); - status = -ENXIO; - goto fail0; + return -ENXIO; } status = devm_request_irq(&pdev->dev, mcfqspi->irq, mcfqspi_irq_handler, 0, pdev->name, mcfqspi); if (status) { dev_dbg(&pdev->dev, "request_irq failed\n"); - goto fail0; + return status; } mcfqspi->clk = devm_clk_get_enabled(&pdev->dev, "qspi_clk"); if (IS_ERR(mcfqspi->clk)) { dev_dbg(&pdev->dev, "clk_get failed\n"); - status = PTR_ERR(mcfqspi->clk); - goto fail0; + return PTR_ERR(mcfqspi->clk); } host->bus_num = pdata->bus_num; @@ -395,7 +389,7 @@ static int mcfqspi_probe(struct platform_device *pdev) status = mcfqspi_cs_setup(mcfqspi); if (status) { dev_dbg(&pdev->dev, "error initializing cs_control\n"); - goto fail0; + return status; } init_waitqueue_head(&mcfqspi->waitq); @@ -423,8 +417,6 @@ static int mcfqspi_probe(struct platform_device *pdev) fail1: pm_runtime_disable(&pdev->dev); mcfqspi_cs_teardown(mcfqspi); -fail0: - spi_controller_put(host); dev_dbg(&pdev->dev, "Coldfire QSPI probe failed\n"); @@ -436,8 +428,6 @@ static void mcfqspi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct mcfqspi *mcfqspi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); @@ -445,8 +435,6 @@ static void mcfqspi_remove(struct platform_device *pdev) mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); mcfqspi_cs_teardown(mcfqspi); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index 68f208ef1e01..6961e36b89d1 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -438,8 +438,8 @@ static int cs42l43_spi_probe(struct platform_device *pdev) } static const struct platform_device_id cs42l43_spi_id_table[] = { - { "cs42l43-spi", }, - {} + { .name = "cs42l43-spi" }, + { } }; MODULE_DEVICE_TABLE(platform, cs42l43_spi_id_table); diff --git a/drivers/spi/spi-dln2.c b/drivers/spi/spi-dln2.c index 392f0d05f508..8333dda7d1e8 100644 --- a/drivers/spi/spi-dln2.c +++ b/drivers/spi/spi-dln2.c @@ -684,7 +684,7 @@ static int dln2_spi_probe(struct platform_device *pdev) struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*dln2)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*dln2)); if (!host) return -ENOMEM; @@ -693,10 +693,8 @@ static int dln2_spi_probe(struct platform_device *pdev) dln2 = spi_controller_get_devdata(host); dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL); - if (!dln2->buf) { - ret = -ENOMEM; - goto exit_free_host; - } + if (!dln2->buf) + return -ENOMEM; dln2->host = host; dln2->pdev = pdev; @@ -709,13 +707,13 @@ static int dln2_spi_probe(struct platform_device *pdev) ret = dln2_spi_enable(dln2, false); if (ret < 0) { dev_err(&pdev->dev, "Failed to disable SPI module\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_get_cs_num(dln2, &host->num_chipselect); if (ret < 0) { dev_err(&pdev->dev, "Failed to get number of CS pins\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_get_speed_range(dln2, @@ -723,20 +721,20 @@ static int dln2_spi_probe(struct platform_device *pdev) &host->max_speed_hz); if (ret < 0) { dev_err(&pdev->dev, "Failed to read bus min/max freqs\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_get_supported_frame_sizes(dln2, &host->bits_per_word_mask); if (ret < 0) { dev_err(&pdev->dev, "Failed to read supported frame sizes\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_cs_enable_all(dln2, true); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable CS pins\n"); - goto exit_free_host; + return ret; } host->bus_num = -1; @@ -749,7 +747,7 @@ static int dln2_spi_probe(struct platform_device *pdev) ret = dln2_spi_enable(dln2, true); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable SPI module\n"); - goto exit_free_host; + return ret; } pm_runtime_set_autosuspend_delay(&pdev->dev, @@ -772,8 +770,6 @@ exit_register: if (dln2_spi_enable(dln2, false) < 0) dev_err(&pdev->dev, "Failed to disable SPI module\n"); -exit_free_host: - spi_controller_put(host); return ret; } @@ -783,16 +779,12 @@ static void dln2_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct dln2_spi *dln2 = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); if (dln2_spi_enable(dln2, false) < 0) dev_err(&pdev->dev, "Failed to disable SPI module\n"); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index b47637888c5c..eff08461c2f5 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -472,7 +472,9 @@ static inline void dw_spi_abort(struct spi_controller *ctlr) if (dws->dma_mapped) dws->dma_ops->dma_stop(dws); + disable_irq(dws->irq); dw_spi_reset_chip(dws); + enable_irq(dws->irq); } static void dw_spi_handle_err(struct spi_controller *ctlr, @@ -1032,6 +1034,8 @@ void dw_spi_remove_controller(struct dw_spi *dws) dw_spi_shutdown_chip(dws); free_irq(dws->irq, dws->ctlr); + + spi_controller_put(dws->ctlr); } EXPORT_SYMBOL_NS_GPL(dw_spi_remove_controller, "SPI_DW_CORE"); diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 1bfdf24c3227..4fc864d38cff 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -226,8 +226,8 @@ static int dw_spi_hssi_init(struct platform_device *pdev, return 0; } -static int dw_spi_intel_init(struct platform_device *pdev, - struct dw_spi_mmio *dwsmmio) +static int dw_spi_hssi_no_dma_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) { dwsmmio->dws.ip = DW_HSSI_ID; @@ -438,7 +438,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, { .compatible = "renesas,rzn1-spi", .data = dw_spi_pssi_init}, { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init}, - { .compatible = "intel,keembay-ssi", .data = dw_spi_intel_init}, + { .compatible = "intel,keembay-ssi", .data = dw_spi_hssi_no_dma_init}, { .compatible = "intel,mountevans-imc-ssi", .data = dw_spi_mountevans_imc_init, @@ -453,6 +453,7 @@ MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id dw_spi_mmio_acpi_match[] = { {"HISI0173", (kernel_ulong_t)dw_spi_pssi_init}, + {"LECA0002", (kernel_ulong_t)dw_spi_hssi_no_dma_init}, {}, }; MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match); diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 72d9f5bc87f7..bfb874f96a26 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -120,16 +120,15 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en if (desc->setup) { ret = desc->setup(dws); if (ret) - goto err_free_irq_vectors; + return ret; } } else { - ret = -ENODEV; - goto err_free_irq_vectors; + return -ENODEV; } ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) - goto err_free_irq_vectors; + return ret; /* PCI hook and SPI hook use the same drv data */ pci_set_drvdata(pdev, dws); @@ -143,10 +142,6 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en pm_runtime_allow(&pdev->dev); return 0; - -err_free_irq_vectors: - pci_free_irq_vectors(pdev); - return ret; } static void dw_spi_pci_remove(struct pci_dev *pdev) @@ -157,7 +152,6 @@ static void dw_spi_pci_remove(struct pci_dev *pdev) pm_runtime_get_noresume(&pdev->dev); dw_spi_remove_controller(dws); - pci_free_irq_vectors(pdev); } #ifdef CONFIG_PM_SLEEP @@ -185,15 +179,15 @@ static const struct pci_device_id dw_spi_pci_ids[] = { * exclusively used by SCU to communicate with MSIC. */ /* Intel MID platform SPI controller 1 */ - { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&dw_spi_pci_mid_desc_1}, + { PCI_VDEVICE(INTEL, 0x0800), .driver_data = (kernel_ulong_t)&dw_spi_pci_mid_desc_1 }, /* Intel MID platform SPI controller 2 */ - { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&dw_spi_pci_mid_desc_2}, + { PCI_VDEVICE(INTEL, 0x0812), .driver_data = (kernel_ulong_t)&dw_spi_pci_mid_desc_2 }, /* Intel Elkhart Lake PSE SPI controllers */ - { PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - { PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - { PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - { PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - {}, + { PCI_VDEVICE(INTEL, 0x4b84), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { PCI_VDEVICE(INTEL, 0x4b85), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { PCI_VDEVICE(INTEL, 0x4b86), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { PCI_VDEVICE(INTEL, 0x4b87), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { }, }; MODULE_DEVICE_TABLE(pci, dw_spi_pci_ids); diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index f716c9607be4..bf389d7590d3 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -600,6 +600,7 @@ fail_release_rx: espi->dma_rx = NULL; fail_free_page: free_page((unsigned long)espi->zeropage); + espi->zeropage = NULL; return ret; } @@ -631,7 +632,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) if (irq < 0) return irq; - host = spi_alloc_host(&pdev->dev, sizeof(*espi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*espi)); if (!host) return -ENOMEM; @@ -656,8 +657,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) espi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(espi->clk)) { dev_err(&pdev->dev, "unable to get spi clock\n"); - error = PTR_ERR(espi->clk); - goto fail_release_host; + return PTR_ERR(espi->clk); } /* @@ -668,22 +668,21 @@ static int ep93xx_spi_probe(struct platform_device *pdev) host->min_speed_hz = clk_get_rate(espi->clk) / (254 * 256); espi->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(espi->mmio)) { - error = PTR_ERR(espi->mmio); - goto fail_release_host; - } + if (IS_ERR(espi->mmio)) + return PTR_ERR(espi->mmio); + espi->sspdr_phys = res->start + SSPDR; error = devm_request_irq(&pdev->dev, irq, ep93xx_spi_interrupt, 0, "ep93xx-spi", host); if (error) { dev_err(&pdev->dev, "failed to request irq\n"); - goto fail_release_host; + return error; } error = ep93xx_spi_setup_dma(&pdev->dev, espi); if (error == -EPROBE_DEFER) - goto fail_release_host; + return error; if (error) dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n"); @@ -704,8 +703,6 @@ static int ep93xx_spi_probe(struct platform_device *pdev) fail_free_dma: ep93xx_spi_release_dma(espi); -fail_release_host: - spi_controller_put(host); return error; } @@ -715,13 +712,9 @@ static void ep93xx_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct ep93xx_spi *espi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); ep93xx_spi_release_dma(espi); - - spi_controller_put(host); } static const struct of_device_id ep93xx_spi_of_ids[] = { diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index cb15faabd88f..e00e808eafee 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -392,9 +392,8 @@ static int falcon_sflash_probe(struct platform_device *pdev) { struct falcon_sflash *priv; struct spi_controller *host; - int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*priv)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*priv)); if (!host) return -ENOMEM; @@ -406,10 +405,7 @@ static int falcon_sflash_probe(struct platform_device *pdev) host->setup = falcon_sflash_setup; host->transfer_one_message = falcon_sflash_xfer_one; - ret = devm_spi_register_controller(&pdev->dev, host); - if (ret) - spi_controller_put(host); - return ret; + return devm_spi_register_controller(&pdev->dev, host); } static const struct of_device_id falcon_sflash_match[] = { diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index f6a75f0184c4..451cb4cfdb9c 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -554,7 +554,7 @@ static int fsi_spi_probe(struct fsi_device *fsi) if (of_property_read_u32(np, "reg", &base)) continue; - ctlr = spi_alloc_host(dev, sizeof(*ctx)); + ctlr = devm_spi_alloc_host(dev, sizeof(*ctx)); if (!ctlr) break; @@ -571,9 +571,9 @@ static int fsi_spi_probe(struct fsi_device *fsi) rc = devm_spi_register_controller(dev, ctlr); if (rc) - spi_controller_put(ctlr); - else - num_controllers_registered++; + continue; + + num_controllers_registered++; } if (!num_controllers_registered) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 9f2a7b8163b1..019d05cdefe6 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1715,6 +1715,8 @@ static void dspi_remove(struct platform_device *pdev) dspi_release_dma(dspi); if (dspi->irq) free_irq(dspi->irq, dspi); + + spi_controller_put(dspi->ctlr); } static void dspi_shutdown(struct platform_device *pdev) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 45b9974ae911..1341bdd7db75 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -667,7 +667,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, struct fsl_espi *espi; int ret; - host = spi_alloc_host(dev, sizeof(struct fsl_espi)); + host = devm_spi_alloc_host(dev, sizeof(struct fsl_espi)); if (!host) return -ENOMEM; @@ -690,8 +690,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, espi->spibrg = fsl_get_sys_freq(); if (espi->spibrg == -1) { dev_err(dev, "Can't get sys frequency!\n"); - ret = -EINVAL; - goto err_probe; + return -EINVAL; } /* determined by clock divider fields DIV16/PM in register SPMODEx */ host->min_speed_hz = DIV_ROUND_UP(espi->spibrg, 4 * 16 * 16); @@ -700,15 +699,13 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, init_completion(&espi->done); espi->reg_base = devm_ioremap_resource(dev, mem); - if (IS_ERR(espi->reg_base)) { - ret = PTR_ERR(espi->reg_base); - goto err_probe; - } + if (IS_ERR(espi->reg_base)) + return PTR_ERR(espi->reg_base); /* Register for SPI Interrupt */ ret = devm_request_irq(dev, irq, fsl_espi_irq, 0, "fsl_espi", espi); if (ret) - goto err_probe; + return ret; fsl_espi_init_regs(dev, true); @@ -732,8 +729,7 @@ err_pm: pm_runtime_put_noidle(dev); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); -err_probe: - spi_controller_put(host); + return ret; } @@ -760,7 +756,7 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) unsigned int irq, num_cs; int ret; - if (of_property_read_bool(np, "mode")) { + if (of_property_present(np, "mode")) { dev_err(dev, "mode property is not supported on ESPI!\n"); return -EINVAL; } @@ -784,13 +780,9 @@ static void of_fsl_espi_remove(struct platform_device *dev) { struct spi_controller *host = platform_get_drvdata(dev); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&dev->dev); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index e201309f8aae..e14753144e19 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -647,7 +647,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { - dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -EINVAL; } @@ -668,8 +668,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -678,8 +678,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -688,8 +688,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA TX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } @@ -698,8 +698,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA RX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 1252c41c206f..e45816ef7b65 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -535,7 +535,7 @@ static struct spi_controller *fsl_spi_probe(struct device *dev, u32 regval; int ret = 0; - host = spi_alloc_host(dev, sizeof(struct mpc8xxx_spi)); + host = devm_spi_alloc_host(dev, sizeof(struct mpc8xxx_spi)); if (host == NULL) { ret = -ENOMEM; goto err; @@ -559,7 +559,7 @@ static struct spi_controller *fsl_spi_probe(struct device *dev, ret = fsl_spi_cpm_init(mpc8xxx_spi); if (ret) - goto err_cpm_init; + goto err; mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem); if (IS_ERR(mpc8xxx_spi->reg_base)) { @@ -625,8 +625,6 @@ static struct spi_controller *fsl_spi_probe(struct device *dev, err_probe: fsl_spi_cpm_free(mpc8xxx_spi); -err_cpm_init: - spi_controller_put(host); err: return ERR_PTR(ret); } @@ -705,13 +703,9 @@ static void of_fsl_spi_remove(struct platform_device *ofdev) struct spi_controller *host = platform_get_drvdata(ofdev); struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); fsl_spi_cpm_free(mpc8xxx_spi); - - spi_controller_put(host); } static struct platform_driver of_fsl_spi_driver = { @@ -757,13 +751,9 @@ static void plat_mpc8xxx_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); fsl_spi_cpm_free(mpc8xxx_spi); - - spi_controller_put(host); } MODULE_ALIAS("platform:mpc8xxx_spi"); diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index d5fb0edc8e0c..26e723cfea61 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2017-2018, The Linux foundation. All rights reserved. +#define CREATE_TRACE_POINTS +#include <trace/events/qcom_geni_spi.h> + #include <linux/clk.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> @@ -332,6 +335,9 @@ static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas, writel(clk_sel, se->base + SE_GENI_CLK_SEL); writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG); + trace_geni_spi_clk_cfg(mas->dev, clk_hz, mas->cur_sclk_hz, idx, div, + mas->cur_bits_per_word); + /* Set BW quota for CPU as driver supports FIFO mode only. */ se->icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(mas->cur_speed_hz); ret = geni_icc_set_bw(se); @@ -366,6 +372,9 @@ static int setup_fifo_params(struct spi_device *spi_slv, if ((mode_changed & SPI_CS_HIGH) || (cs_changed && (spi_slv->mode & SPI_CS_HIGH))) writel((spi_slv->mode & SPI_CS_HIGH) ? BIT(chipselect) : 0, se->base + SE_SPI_DEMUX_OUTPUT_INV); + trace_geni_spi_setup_params(mas->dev, chipselect, spi_slv->mode, + mode_changed, cs_changed); + return 0; } @@ -440,10 +449,15 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas return ret; } - if (!xfer->cs_change) { - if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers)) - peripheral.fragmentation = FRAGMENTATION; - } + /* + * Set fragmentation to keep CS asserted after this transfer when: + * - non-last transfer with cs_change=0: keep CS asserted between chained transfers + * - last transfer with cs_change=1: keep CS asserted after the message + * (e.g. TPM TIS SPI uses cs_change=1 on single-transfer messages to + * keep CS asserted across header, wait-state and data phases) + */ + peripheral.fragmentation = list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers) ? + xfer->cs_change : !xfer->cs_change; if (peripheral.cmd & SPI_RX) { dmaengine_slave_config(mas->rx, &config); @@ -849,10 +863,16 @@ static int setup_se_xfer(struct spi_transfer *xfer, mas->cur_xfer_mode = GENI_SE_DMA; geni_se_select_mode(se, mas->cur_xfer_mode); - if (!xfer->cs_change) { - if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers)) - m_params = FRAGMENTATION; - } + /* + * Set FRAGMENTATION to keep CS asserted after this transfer when: + * - non-last transfer with cs_change=0: keep CS asserted between chained transfers + * - last transfer with cs_change=1: keep CS asserted after the message + * (e.g. TPM TIS SPI uses cs_change=1 on single-transfer messages to + * keep CS asserted across header, wait-state and data phases) + */ + if (list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers) ? + xfer->cs_change : !xfer->cs_change) + m_params = FRAGMENTATION; /* * Lock around right before we start the transfer since our @@ -861,6 +881,8 @@ static int setup_se_xfer(struct spi_transfer *xfer, spin_lock_irq(&mas->lock); geni_se_setup_m_cmd(se, m_cmd, m_params); + trace_geni_spi_transfer(mas->dev, len, m_cmd); + if (mas->cur_xfer_mode == GENI_SE_DMA) { if (m_cmd & SPI_RX_ONLY) geni_se_rx_init_dma(se, sg_dma_address(xfer->rx_sg.sgl), @@ -915,6 +937,8 @@ static irqreturn_t geni_spi_isr(int irq, void *data) if (!m_irq && !dma_tx_status && !dma_rx_status) return IRQ_NONE; + trace_geni_spi_irq(mas->dev, m_irq, dma_tx_status, dma_rx_status); + if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN | M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN)) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 046bd894040b..30fcdb5028dc 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -463,6 +463,7 @@ static int hisi_spi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct spi_controller *host; struct hisi_spi *hs; + u32 num_cs; int ret, irq; irq = platform_get_irq(pdev, 0); @@ -495,10 +496,11 @@ static int hisi_spi_probe(struct platform_device *pdev) if (host->max_speed_hz == 0) return dev_err_probe(dev, -EINVAL, "spi-max-frequency can't be 0\n"); - ret = device_property_read_u16(dev, "num-cs", - &host->num_chipselect); + ret = device_property_read_u32(dev, "num-cs", &num_cs); if (ret) host->num_chipselect = DEFAULT_NUM_CS; + else + host->num_chipselect = num_cs; host->use_gpio_descriptors = true; host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; @@ -520,10 +522,8 @@ static int hisi_spi_probe(struct platform_device *pdev) } ret = spi_register_controller(host); - if (ret) { - dev_err(dev, "failed to register spi host, ret=%d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to register spi host\n"); if (hisi_spi_debugfs_init(hs)) dev_info(dev, "failed to create debugfs dir\n"); diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index b2af2eed197f..eeeb86381862 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -436,7 +436,7 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) u32 version, glb_config; int ret; - ctlr = spi_alloc_host(&pdev->dev, sizeof(*host)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*host)); if (!ctlr) return -ENOMEM; @@ -451,16 +451,12 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); host->regbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host->regbase)) { - ret = PTR_ERR(host->regbase); - goto err_put_host; - } + if (IS_ERR(host->regbase)) + return PTR_ERR(host->regbase); host->irq = platform_get_irq_optional(pdev, 0); - if (host->irq == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto err_put_host; - } + if (host->irq == -EPROBE_DEFER) + return -EPROBE_DEFER; hisi_sfc_v3xx_disable_int(host); @@ -501,16 +497,12 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) ret = devm_spi_register_controller(dev, ctlr); if (ret) - goto err_put_host; + return ret; dev_info(&pdev->dev, "hw version 0x%x, %s mode.\n", version, host->irq ? "irq" : "polling"); return 0; - -err_put_host: - spi_controller_put(ctlr); - return ret; } static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = { diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 57625a3ce2f2..aec724e3f824 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -530,7 +530,7 @@ static int img_spfi_probe(struct platform_device *pdev) int ret; u32 max_speed_hz; - host = spi_alloc_host(&pdev->dev, sizeof(*spfi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spfi)); if (!host) return -ENOMEM; platform_set_drvdata(pdev, host); @@ -541,36 +541,32 @@ static int img_spfi_probe(struct platform_device *pdev) spin_lock_init(&spfi->lock); spfi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(spfi->regs)) { - ret = PTR_ERR(spfi->regs); - goto put_spi; - } + if (IS_ERR(spfi->regs)) + return PTR_ERR(spfi->regs); + spfi->phys = res->start; spfi->irq = platform_get_irq(pdev, 0); - if (spfi->irq < 0) { - ret = spfi->irq; - goto put_spi; - } + if (spfi->irq < 0) + return spfi->irq; + ret = devm_request_irq(spfi->dev, spfi->irq, img_spfi_irq, IRQ_TYPE_LEVEL_HIGH, dev_name(spfi->dev), spfi); if (ret) - goto put_spi; + return ret; spfi->sys_clk = devm_clk_get(spfi->dev, "sys"); - if (IS_ERR(spfi->sys_clk)) { - ret = PTR_ERR(spfi->sys_clk); - goto put_spi; - } + if (IS_ERR(spfi->sys_clk)) + return PTR_ERR(spfi->sys_clk); + spfi->spfi_clk = devm_clk_get(spfi->dev, "spfi"); - if (IS_ERR(spfi->spfi_clk)) { - ret = PTR_ERR(spfi->spfi_clk); - goto put_spi; - } + if (IS_ERR(spfi->spfi_clk)) + return PTR_ERR(spfi->spfi_clk); ret = clk_prepare_enable(spfi->sys_clk); if (ret) - goto put_spi; + return ret; + ret = clk_prepare_enable(spfi->spfi_clk); if (ret) goto disable_pclk; @@ -658,8 +654,6 @@ disable_pm: clk_disable_unprepare(spfi->spfi_clk); disable_pclk: clk_disable_unprepare(spfi->sys_clk); -put_spi: - spi_controller_put(host); return ret; } @@ -669,8 +663,6 @@ static void img_spfi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct img_spfi *spfi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); if (spfi->tx_ch) @@ -683,8 +675,6 @@ static void img_spfi_remove(struct platform_device *pdev) clk_disable_unprepare(spfi->spfi_clk); clk_disable_unprepare(spfi->sys_clk); } - - spi_controller_put(host); } #ifdef CONFIG_PM diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 480d1e8b281f..ae9912905c67 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1774,8 +1774,8 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, transfer_timeout); if (!time_left) { dev_err(spi_imx->dev, "I/O Error in DMA TX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -ETIMEDOUT; } @@ -1784,7 +1784,7 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, if (!time_left) { dev_err(&controller->dev, "I/O Error in DMA RX\n"); spi_imx->devtype_data->reset(spi_imx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -ETIMEDOUT; } } else { @@ -1793,15 +1793,15 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, if (wait_for_completion_interruptible(&spi_imx->dma_tx_completion) || READ_ONCE(spi_imx->target_aborted)) { dev_dbg(spi_imx->dev, "I/O Error in DMA TX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -EINTR; } if (wait_for_completion_interruptible(&spi_imx->dma_rx_completion) || READ_ONCE(spi_imx->target_aborted)) { dev_dbg(spi_imx->dev, "I/O Error in DMA RX interrupted\n"); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -EINTR; } @@ -1818,9 +1818,9 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, return 0; dmaengine_terminate_tx: - dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_tx); dmaengine_terminate_rx: - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -EINVAL; } diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c index d8ef8f89330a..8c429c832ddd 100644 --- a/drivers/spi/spi-intel-pci.c +++ b/drivers/spi/spi-intel-pci.c @@ -66,39 +66,39 @@ static int intel_spi_pci_probe(struct pci_dev *pdev, } static const struct pci_device_id intel_spi_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x02a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x38a4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x4d23), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x5794), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x5825), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x6e24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7723), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7e23), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7f24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x9d24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x9da4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0xa2a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa3a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa823), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xd323), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xe323), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xe423), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x02a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x06a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x18e0), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x19e0), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x1bca), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x34a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x38a4), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x43a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x4b24), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x4d23), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x4da4), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x51a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x54a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x5794), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x5825), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x6e24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7723), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7a24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7aa4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7e23), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7f24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x9d24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x9da4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa0a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa1a4), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa224), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa2a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa324), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa3a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa823), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xd323), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xe323), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xe423), .driver_data = (unsigned long)&cnl_info }, { }, }; MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 1775ad39e633..7494b921a743 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -814,7 +814,7 @@ static int intel_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller); const struct intel_spi_mem_op *iop; - iop = intel_spi_match_mem_op(ispi, &desc->info.op_tmpl); + iop = intel_spi_match_mem_op(ispi, desc->info.op_tmpl); if (!iop) return -EOPNOTSUPP; @@ -827,7 +827,7 @@ static ssize_t intel_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, { struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller); const struct intel_spi_mem_op *iop = desc->priv; - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; /* Fill in the gaps */ @@ -844,7 +844,7 @@ static ssize_t intel_spi_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs { struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller); const struct intel_spi_mem_op *iop = desc->priv; - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; op.addr.val = offs; diff --git a/drivers/spi/spi-jcore.c b/drivers/spi/spi-jcore.c index e37ca22e04ba..a75cd61ec7a3 100644 --- a/drivers/spi/spi-jcore.c +++ b/drivers/spi/spi-jcore.c @@ -146,11 +146,10 @@ static int jcore_spi_probe(struct platform_device *pdev) struct resource *res; u32 clock_freq; struct clk *clk; - int err = -ENODEV; - host = spi_alloc_host(&pdev->dev, sizeof(struct jcore_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct jcore_spi)); if (!host) - return err; + return -ENOMEM; /* Setup the host state. */ host->num_chipselect = 3; @@ -167,14 +166,14 @@ static int jcore_spi_probe(struct platform_device *pdev) /* Find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) - goto exit_busy; + return -EBUSY; if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), pdev->name)) - goto exit_busy; + return -EBUSY; hw->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!hw->base) - goto exit_busy; + return -EBUSY; /* * The SPI clock rate controlled via a configurable clock divider @@ -200,17 +199,7 @@ static int jcore_spi_probe(struct platform_device *pdev) jcore_spi_baudrate(hw, 400000); /* Register our spi controller */ - err = devm_spi_register_controller(&pdev->dev, host); - if (err) - goto exit; - - return 0; - -exit_busy: - err = -EBUSY; -exit: - spi_controller_put(host); - return err; + return devm_spi_register_controller(&pdev->dev, host); } static const struct of_device_id jcore_spi_of_match[] = { diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c index 75b9af8cb5db..ecae50a50b14 100644 --- a/drivers/spi/spi-lantiq-ssc.c +++ b/drivers/spi/spi-lantiq-ssc.c @@ -913,7 +913,7 @@ static int lantiq_ssc_probe(struct platform_device *pdev) hwcfg = of_device_get_match_data(dev); - host = spi_alloc_host(dev, sizeof(struct lantiq_ssc_spi)); + host = devm_spi_alloc_host(dev, sizeof(struct lantiq_ssc_spi)); if (!host) return -ENOMEM; @@ -923,20 +923,16 @@ static int lantiq_ssc_probe(struct platform_device *pdev) spi->hwcfg = hwcfg; platform_set_drvdata(pdev, spi); spi->regbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(spi->regbase)) { - err = PTR_ERR(spi->regbase); - goto err_host_put; - } + if (IS_ERR(spi->regbase)) + return PTR_ERR(spi->regbase); err = hwcfg->cfg_irq(pdev, spi); if (err) - goto err_host_put; + return err; spi->spi_clk = devm_clk_get_enabled(dev, "gate"); - if (IS_ERR(spi->spi_clk)) { - err = PTR_ERR(spi->spi_clk); - goto err_host_put; - } + if (IS_ERR(spi->spi_clk)) + return PTR_ERR(spi->spi_clk); /* * Use the old clk_get_fpi() function on Lantiq platform, till it @@ -947,10 +943,8 @@ static int lantiq_ssc_probe(struct platform_device *pdev) #else spi->fpi_clk = clk_get(dev, "freq"); #endif - if (IS_ERR(spi->fpi_clk)) { - err = PTR_ERR(spi->fpi_clk); - goto err_host_put; - } + if (IS_ERR(spi->fpi_clk)) + return PTR_ERR(spi->fpi_clk); num_cs = 8; of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); @@ -1006,8 +1000,6 @@ err_wq_destroy: destroy_workqueue(spi->wq); err_clk_put: clk_put(spi->fpi_clk); -err_host_put: - spi_controller_put(host); return err; } @@ -1016,8 +1008,6 @@ static void lantiq_ssc_remove(struct platform_device *pdev) { struct lantiq_ssc_spi *spi = platform_get_drvdata(pdev); - spi_controller_get(spi->host); - spi_unregister_controller(spi->host); lantiq_ssc_writel(spi, 0, LTQ_SPI_IRNEN); @@ -1028,8 +1018,6 @@ static void lantiq_ssc_remove(struct platform_device *pdev) destroy_workqueue(spi->wq); clk_put(spi->fpi_clk); - - spi_controller_put(spi->host); } static struct platform_driver lantiq_ssc_driver = { diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c index e466866d5e80..b2546a3a9eaa 100644 --- a/drivers/spi/spi-lp8841-rtc.c +++ b/drivers/spi/spi-lp8841-rtc.c @@ -185,7 +185,7 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) struct spi_controller *host; struct spi_lp8841_rtc *data; - host = spi_alloc_host(&pdev->dev, sizeof(*data)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*data)); if (!host) return -ENOMEM; platform_set_drvdata(pdev, host); @@ -199,8 +199,6 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) host->set_cs = spi_lp8841_rtc_set_cs; host->transfer_one = spi_lp8841_rtc_transfer_one; host->bits_per_word_mask = SPI_BPW_MASK(8); -#ifdef CONFIG_OF -#endif data = spi_controller_get_devdata(host); @@ -208,23 +206,17 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) ret = PTR_ERR_OR_ZERO(data->iomem); if (ret) { dev_err(&pdev->dev, "failed to get IO address\n"); - goto err_put_host; + return ret; } /* register with the SPI framework */ ret = devm_spi_register_controller(&pdev->dev, host); if (ret) { dev_err(&pdev->dev, "cannot register spi host\n"); - goto err_put_host; + return ret; } - return ret; - - -err_put_host: - spi_controller_put(host); - - return ret; + return 0; } MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index a09371a075d2..a88b9f038356 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -279,13 +279,20 @@ static bool spi_mem_internal_supports_op(struct spi_mem *mem, */ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { - /* Make sure the operation frequency is correct before going futher */ - spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op); + struct spi_mem_op eval_op = *op; + + /* + * Work on a local copy; this is a pure capability check and must + * not modify the caller's op. Stored templates with max_freq == 0 + * must remain unset so their frequency is always re-capped to the + * current device maximum at execution time. + */ + spi_mem_adjust_op_freq(mem, &eval_op); - if (spi_mem_check_op(op)) + if (spi_mem_check_op(&eval_op)) return false; - return spi_mem_internal_supports_op(mem, op); + return spi_mem_internal_supports_op(mem, &eval_op); } EXPORT_SYMBOL_GPL(spi_mem_supports_op); @@ -647,7 +654,7 @@ EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration); static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, void *buf) { - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; op.addr.val = desc->info.offset + offs; @@ -667,7 +674,7 @@ static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc, static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, const void *buf) { - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; op.addr.val = desc->info.offset + offs; @@ -706,19 +713,37 @@ spi_mem_dirmap_create(struct spi_mem *mem, int ret = -ENOTSUPP; /* Make sure the number of address cycles is between 1 and 8 bytes. */ - if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8) + if (!info->primary_op_tmpl.addr.nbytes || info->primary_op_tmpl.addr.nbytes > 8) return ERR_PTR(-EINVAL); /* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */ - if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA) + if (info->primary_op_tmpl.data.dir == SPI_MEM_NO_DATA) return ERR_PTR(-EINVAL); + /* Apply similar constraints to the secondary template */ + if (info->secondary_op_tmpl.cmd.opcode) { + if (!info->secondary_op_tmpl.addr.nbytes || + info->secondary_op_tmpl.addr.nbytes > 8) + return ERR_PTR(-EINVAL); + + if (info->secondary_op_tmpl.data.dir == SPI_MEM_NO_DATA) + return ERR_PTR(-EINVAL); + + if (!spi_mem_supports_op(mem, &info->secondary_op_tmpl)) + return ERR_PTR(-EOPNOTSUPP); + + if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create && + !spi_mem_controller_is_capable(ctlr, secondary_op_tmpl)) + return ERR_PTR(-EOPNOTSUPP); + } + desc = kzalloc_obj(*desc); if (!desc) return ERR_PTR(-ENOMEM); desc->mem = mem; desc->info = *info; + desc->info.op_tmpl = &desc->info.primary_op_tmpl; if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create) { ret = spi_mem_access_start(mem); if (ret) { @@ -733,7 +758,7 @@ spi_mem_dirmap_create(struct spi_mem *mem, if (ret) { desc->nodirmap = true; - if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!spi_mem_supports_op(desc->mem, &desc->info.primary_op_tmpl)) ret = -EOPNOTSUPP; else ret = 0; @@ -857,7 +882,7 @@ ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, struct spi_controller *ctlr = desc->mem->spi->controller; ssize_t ret; - if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN) return -EINVAL; if (!len) @@ -903,7 +928,7 @@ ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, struct spi_controller *ctlr = desc->mem->spi->controller; ssize_t ret; - if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT) + if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_OUT) return -EINVAL; if (!len) diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index b80f9f457b66..fa2d2db2d6e7 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -539,9 +539,8 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc, conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG); /* Setup word width */ - conf &= ~SPICC_BITLENGTH_MASK; - conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, - (spicc->bytes_per_word << 3) - 1); + FIELD_MODIFY(SPICC_BITLENGTH_MASK, &conf, + (spicc->bytes_per_word << 3) - 1); /* Ignore if unchanged */ if (conf != conf_orig) @@ -982,7 +981,7 @@ static int meson_spicc_probe(struct platform_device *pdev) struct meson_spicc_device *spicc; int ret, irq; - host = spi_alloc_host(&pdev->dev, sizeof(*spicc)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spicc)); if (!host) { dev_err(&pdev->dev, "host allocation failed\n"); return -ENOMEM; @@ -993,8 +992,7 @@ static int meson_spicc_probe(struct platform_device *pdev) spicc->data = of_device_get_match_data(&pdev->dev); if (!spicc->data) { dev_err(&pdev->dev, "failed to get match data\n"); - ret = -EINVAL; - goto out_host; + return -EINVAL; } spicc->pdev = pdev; @@ -1005,8 +1003,7 @@ static int meson_spicc_probe(struct platform_device *pdev) spicc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spicc->base)) { dev_err(&pdev->dev, "io resource mapping failed\n"); - ret = PTR_ERR(spicc->base); - goto out_host; + return PTR_ERR(spicc->base); } /* Set master mode and enable controller */ @@ -1017,39 +1014,33 @@ static int meson_spicc_probe(struct platform_device *pdev) writel_relaxed(0, spicc->base + SPICC_INTREG); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto out_host; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, meson_spicc_irq, 0, NULL, spicc); if (ret) { dev_err(&pdev->dev, "irq request failed\n"); - goto out_host; + return ret; } spicc->core = devm_clk_get_enabled(&pdev->dev, "core"); if (IS_ERR(spicc->core)) { dev_err(&pdev->dev, "core clock request failed\n"); - ret = PTR_ERR(spicc->core); - goto out_host; + return PTR_ERR(spicc->core); } if (spicc->data->has_pclk) { spicc->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(spicc->pclk)) { dev_err(&pdev->dev, "pclk clock request failed\n"); - ret = PTR_ERR(spicc->pclk); - goto out_host; + return PTR_ERR(spicc->pclk); } } spicc->pinctrl = devm_pinctrl_get(&pdev->dev); - if (IS_ERR(spicc->pinctrl)) { - ret = PTR_ERR(spicc->pinctrl); - goto out_host; - } + if (IS_ERR(spicc->pinctrl)) + return PTR_ERR(spicc->pinctrl); device_reset_optional(&pdev->dev); @@ -1070,43 +1061,34 @@ static int meson_spicc_probe(struct platform_device *pdev) ret = meson_spicc_pow2_clk_init(spicc); if (ret) { dev_err(&pdev->dev, "pow2 clock registration failed\n"); - goto out_host; + return ret; } if (spicc->data->has_enhance_clk_div) { ret = meson_spicc_enh_clk_init(spicc); if (ret) { dev_err(&pdev->dev, "clock registration failed\n"); - goto out_host; + return ret; } } ret = spi_register_controller(host); if (ret) { dev_err(&pdev->dev, "spi registration failed\n"); - goto out_host; + return ret; } return 0; - -out_host: - spi_controller_put(host); - - return ret; } static void meson_spicc_remove(struct platform_device *pdev) { struct meson_spicc_device *spicc = platform_get_drvdata(pdev); - spi_controller_get(spicc->host); - spi_unregister_controller(spicc->host); /* Disable SPI */ writel(0, spicc->base + SPICC_CONREG); - - spi_controller_put(spicc->host); } static const struct meson_spicc_data meson_spicc_gx_data = { diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index b818950a8cb7..e36aa94bbdae 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -288,9 +288,9 @@ static int meson_spifc_probe(struct platform_device *pdev) struct meson_spifc *spifc; void __iomem *base; unsigned int rate; - int ret = 0; + int ret; - host = spi_alloc_host(&pdev->dev, sizeof(struct meson_spifc)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct meson_spifc)); if (!host) return -ENOMEM; @@ -300,23 +300,18 @@ static int meson_spifc_probe(struct platform_device *pdev) spifc->dev = &pdev->dev; base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto out_err; - } + if (IS_ERR(base)) + return PTR_ERR(base); spifc->regmap = devm_regmap_init_mmio(spifc->dev, base, &spifc_regmap_config); - if (IS_ERR(spifc->regmap)) { - ret = PTR_ERR(spifc->regmap); - goto out_err; - } + if (IS_ERR(spifc->regmap)) + return PTR_ERR(spifc->regmap); spifc->clk = devm_clk_get_enabled(spifc->dev, NULL); if (IS_ERR(spifc->clk)) { dev_err(spifc->dev, "missing clock\n"); - ret = PTR_ERR(spifc->clk); - goto out_err; + return PTR_ERR(spifc->clk); } rate = clk_get_rate(spifc->clk); @@ -342,8 +337,7 @@ static int meson_spifc_probe(struct platform_device *pdev) return 0; out_pm: pm_runtime_disable(spifc->dev); -out_err: - spi_controller_put(host); + return ret; } @@ -351,6 +345,7 @@ static void meson_spifc_remove(struct platform_device *pdev) { pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index 4dee0fea1df8..c0ea65014d2e 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -99,7 +99,6 @@ #define REG_IEN (0x0c) #define REG_STATUS (0x10) #define REG_DIRECT_ACCESS (0x14) -#define REG_UPPER_ACCESS (0x18) #define REG_RX_DATA (0x40) #define REG_TX_DATA (0x44) #define REG_X4_RX_DATA (0x48) @@ -632,7 +631,7 @@ static int mchp_coreqspi_prepare_message(struct spi_controller *ctlr, struct spi ret = mchp_coreqspi_wait_for_ready(qspi); if (ret) { mutex_unlock(&qspi->op_lock); - dev_err(&ctlr->dev, "Timeout waiting on QSPI ready.\n"); + dev_err(&m->spi->dev, "Timeout waiting on QSPI ready.\n"); return ret; } diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 924d820448fb..70d8e17e8c43 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -472,13 +472,15 @@ static int mpc52xx_spi_probe(struct platform_device *op) if (ms->irq0 && ms->irq1) { rc = request_irq(ms->irq0, mpc52xx_spi_irq, 0, "mpc5200-spi-modf", ms); - rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0, - "mpc5200-spi-spif", ms); - if (rc) { - free_irq(ms->irq0, ms); - free_irq(ms->irq1, ms); - ms->irq0 = ms->irq1 = 0; + if (rc == 0) { + rc = request_irq(ms->irq1, mpc52xx_spi_irq, 0, + "mpc5200-spi-spif", ms); + if (rc) + free_irq(ms->irq0, ms); } + + if (rc) + ms->irq0 = ms->irq1 = 0; } else { /* operate in polled mode */ ms->irq0 = ms->irq1 = 0; @@ -498,8 +500,10 @@ static int mpc52xx_spi_probe(struct platform_device *op) err_register: dev_err(&ms->host->dev, "initialization failed\n"); - free_irq(ms->irq0, ms); - free_irq(ms->irq1, ms); + if (ms->irq0) { + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + } cancel_work_sync(&ms->work); err_gpio: while (i-- > 0) @@ -516,14 +520,16 @@ static int mpc52xx_spi_probe(struct platform_device *op) static void mpc52xx_spi_remove(struct platform_device *op) { - struct spi_controller *host = spi_controller_get(platform_get_drvdata(op)); + struct spi_controller *host = platform_get_drvdata(op); struct mpc52xx_spi *ms = spi_controller_get_devdata(host); int i; spi_unregister_controller(host); - free_irq(ms->irq0, ms); - free_irq(ms->irq1, ms); + if (ms->irq0) { + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + } cancel_work_sync(&ms->work); diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c index bd122de152c0..08fe1fa32dea 100644 --- a/drivers/spi/spi-mux.c +++ b/drivers/spi/spi-mux.c @@ -127,9 +127,8 @@ static int spi_mux_probe(struct spi_device *spi) { struct spi_controller *ctlr; struct spi_mux_priv *priv; - int ret; - ctlr = spi_alloc_host(&spi->dev, sizeof(*priv)); + ctlr = devm_spi_alloc_host(&spi->dev, sizeof(*priv)); if (!ctlr) return -ENOMEM; @@ -146,9 +145,8 @@ static int spi_mux_probe(struct spi_device *spi) priv->mux = devm_mux_control_get(&spi->dev, NULL); if (IS_ERR(priv->mux)) { - ret = dev_err_probe(&spi->dev, PTR_ERR(priv->mux), - "failed to get control-mux\n"); - goto err_put_ctlr; + return dev_err_probe(&spi->dev, PTR_ERR(priv->mux), + "failed to get control-mux\n"); } priv->current_cs = SPI_MUX_NO_CS; @@ -164,16 +162,7 @@ static int spi_mux_probe(struct spi_device *spi) ctlr->must_async = true; ctlr->defer_optimize_message = true; - ret = devm_spi_register_controller(&spi->dev, ctlr); - if (ret) - goto err_put_ctlr; - - return 0; - -err_put_ctlr: - spi_controller_put(ctlr); - - return ret; + return devm_spi_register_controller(&spi->dev, ctlr); } static const struct spi_device_id spi_mux_id[] = { diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index b0e7fc828a50..83b688e65284 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -403,20 +403,20 @@ static ssize_t mxic_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, if (WARN_ON(offs + desc->info.offset + len > U32_MAX)) return -EINVAL; - writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl.data.swap16), + writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl->data.swap16), mxic->regs + HC_CFG); - writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len), + writel(mxic_spi_mem_prep_op_cfg(desc->info.op_tmpl, len), mxic->regs + LRD_CFG); writel(desc->info.offset + offs, mxic->regs + LRD_ADDR); len = min_t(size_t, len, mxic->linear.size); writel(len, mxic->regs + LRD_RANGE); - writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) | + writel(LMODE_CMD0(desc->info.op_tmpl->cmd.opcode) | LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) | LMODE_EN, mxic->regs + LRD_CTRL); - if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) { + if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl->data.ecc) { ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine, NAND_PAGE_READ, mxic->linear.dma + offs); @@ -448,20 +448,20 @@ static ssize_t mxic_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, if (WARN_ON(offs + desc->info.offset + len > U32_MAX)) return -EINVAL; - writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl.data.swap16), + writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl->data.swap16), mxic->regs + HC_CFG); - writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len), + writel(mxic_spi_mem_prep_op_cfg(desc->info.op_tmpl, len), mxic->regs + LWR_CFG); writel(desc->info.offset + offs, mxic->regs + LWR_ADDR); len = min_t(size_t, len, mxic->linear.size); writel(len, mxic->regs + LWR_RANGE); - writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) | + writel(LMODE_CMD0(desc->info.op_tmpl->cmd.opcode) | LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) | LMODE_EN, mxic->regs + LWR_CTRL); - if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) { + if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl->data.ecc) { ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine, NAND_PAGE_WRITE, mxic->linear.dma + offs); @@ -509,7 +509,7 @@ static int mxic_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc) if (desc->info.offset + desc->info.length > U32_MAX) return -EINVAL; - if (!mxic_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!mxic_spi_mem_supports_op(desc->mem, desc->info.op_tmpl)) return -EOPNOTSUPP; return 0; diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 0164e04d59a1..fda1d260b296 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -563,7 +563,7 @@ static int mxs_spi_probe(struct platform_device *pdev) if (ret) clk_freq = clk_freq_default; - host = spi_alloc_host(&pdev->dev, sizeof(*spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi)); if (!host) return -ENOMEM; @@ -589,13 +589,12 @@ static int mxs_spi_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, dev_name(&pdev->dev), ssp); if (ret) - goto out_host_free; + return ret; ssp->dmach = dma_request_chan(&pdev->dev, "rx-tx"); if (IS_ERR(ssp->dmach)) { dev_err(ssp->dev, "Failed to request DMA\n"); - ret = PTR_ERR(ssp->dmach); - goto out_host_free; + return PTR_ERR(ssp->dmach); } pm_runtime_enable(ssp->dev); @@ -635,8 +634,7 @@ out_pm_runtime_disable: pm_runtime_disable(ssp->dev); out_dma_release: dma_release_channel(ssp->dmach); -out_host_free: - spi_controller_put(host); + return ret; } @@ -650,8 +648,6 @@ static void mxs_spi_remove(struct platform_device *pdev) spi = spi_controller_get_devdata(host); ssp = &spi->ssp; - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); @@ -659,8 +655,6 @@ static void mxs_spi_remove(struct platform_device *pdev) mxs_spi_runtime_suspend(&pdev->dev); dma_release_channel(ssp->dmach); - - spi_controller_put(host); } static struct platform_driver mxs_spi_driver = { diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c index 6617751009c3..4b825044038b 100644 --- a/drivers/spi/spi-npcm-fiu.c +++ b/drivers/spi/spi-npcm-fiu.c @@ -299,11 +299,11 @@ static ssize_t npcm_fiu_direct_read(struct spi_mem_dirmap_desc *desc, for (i = 0 ; i < len ; i++) *(buf_rx + i) = ioread8(src + i); } else { - if (desc->info.op_tmpl.addr.buswidth != fiu->drd_op.addr.buswidth || - desc->info.op_tmpl.dummy.nbytes != fiu->drd_op.dummy.nbytes || - desc->info.op_tmpl.cmd.opcode != fiu->drd_op.cmd.opcode || - desc->info.op_tmpl.addr.nbytes != fiu->drd_op.addr.nbytes) - npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); + if (desc->info.op_tmpl->addr.buswidth != fiu->drd_op.addr.buswidth || + desc->info.op_tmpl->dummy.nbytes != fiu->drd_op.dummy.nbytes || + desc->info.op_tmpl->cmd.opcode != fiu->drd_op.cmd.opcode || + desc->info.op_tmpl->addr.nbytes != fiu->drd_op.addr.nbytes) + npcm_fiu_set_drd(fiu, desc->info.op_tmpl); memcpy_fromio(buf_rx, src, len); } @@ -609,7 +609,7 @@ static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) } if (!fiu->spix_mode && - desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { + desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT) { desc->nodirmap = true; return 0; } @@ -644,9 +644,9 @@ static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) NPCM_FIU_CFG_FIU_FIX); } - if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { + if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN) { if (!fiu->spix_mode) - npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); + npcm_fiu_set_drd(fiu, desc->info.op_tmpl); else npcm_fiux_set_direct_rd(fiu); diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index cffef0a5977d..a437a30d636c 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -345,7 +345,7 @@ static int npcm_pspi_probe(struct platform_device *pdev) int irq; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*priv)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*priv)); if (!host) return -ENOMEM; @@ -356,21 +356,18 @@ static int npcm_pspi_probe(struct platform_device *pdev) priv->is_save_param = false; priv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->base)) { - ret = PTR_ERR(priv->base); - goto out_host_put; - } + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); priv->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(priv->clk); - goto out_host_put; + return PTR_ERR(priv->clk); } ret = clk_prepare_enable(priv->clk); if (ret) - goto out_host_put; + return ret; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -424,8 +421,6 @@ static int npcm_pspi_probe(struct platform_device *pdev) out_disable_clk: clk_disable_unprepare(priv->clk); -out_host_put: - spi_controller_put(host); return ret; } @@ -434,14 +429,10 @@ static void npcm_pspi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct npcm_pspi *priv = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); npcm_pspi_reset_hw(priv); clk_disable_unprepare(priv->clk); - - spi_controller_put(host); } static const struct of_device_id npcm_pspi_match[] = { diff --git a/drivers/spi/spi-nxp-xspi.c b/drivers/spi/spi-nxp-xspi.c index 385302a6e62f..037eac24e6fd 100644 --- a/drivers/spi/spi-nxp-xspi.c +++ b/drivers/spi/spi-nxp-xspi.c @@ -493,9 +493,8 @@ static void nxp_xspi_disable_ddr(struct nxp_xspi *xspi) writel(reg, base + XSPI_MCR); reg &= ~XSPI_MCR_DDR_EN; - reg &= ~XSPI_MCR_DQS_FA_SEL_MASK; /* Use dummy pad loopback mode to sample data */ - reg |= FIELD_PREP(XSPI_MCR_DQS_FA_SEL_MASK, 0x01); + FIELD_MODIFY(XSPI_MCR_DQS_FA_SEL_MASK, ®, 0x01); writel(reg, base + XSPI_MCR); xspi->support_max_rate = 133000000; @@ -524,15 +523,13 @@ static void nxp_xspi_enable_ddr(struct nxp_xspi *xspi) writel(reg, base + XSPI_MCR); reg |= XSPI_MCR_DDR_EN; - reg &= ~XSPI_MCR_DQS_FA_SEL_MASK; /* Use external dqs to sample data */ - reg |= FIELD_PREP(XSPI_MCR_DQS_FA_SEL_MASK, 0x03); + FIELD_MODIFY(XSPI_MCR_DQS_FA_SEL_MASK, ®, 0x03); writel(reg, base + XSPI_MCR); xspi->support_max_rate = 200000000; reg = readl(base + XSPI_FLSHCR); - reg &= ~XSPI_FLSHCR_TDH_MASK; - reg |= FIELD_PREP(XSPI_FLSHCR_TDH_MASK, 0x01); + FIELD_MODIFY(XSPI_FLSHCR_TDH_MASK, ®, 0x01); writel(reg, base + XSPI_FLSHCR); reg = FIELD_PREP(XSPI_SMPR_DLLFSMPFA_MASK, 0x04); @@ -1096,8 +1093,7 @@ static int nxp_xspi_default_setup(struct nxp_xspi *xspi) /* Give read/write access right to EENV0 */ reg = readl(base + XSPI_FRAD0_WORD2); - reg &= ~XSPI_FRAD0_WORD2_MD0ACP_MASK; - reg |= FIELD_PREP(XSPI_FRAD0_WORD2_MD0ACP_MASK, 0x03); + FIELD_MODIFY(XSPI_FRAD0_WORD2_MD0ACP_MASK, ®, 0x03); writel(reg, base + XSPI_FRAD0_WORD2); /* Enable the FRAD check for EENV0 */ diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 56b30ff58771..9cc078acc13c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -22,7 +22,6 @@ #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/gcd.h> #include <linux/spi/spi.h> @@ -1481,12 +1480,11 @@ static int omap2_mcspi_probe(struct platform_device *pdev) int status = 0, i; u32 regs_offset = 0; struct device_node *node = pdev->dev.of_node; - const struct of_device_id *match; if (of_property_read_bool(node, "spi-slave")) - ctlr = spi_alloc_target(&pdev->dev, sizeof(*mcspi)); + ctlr = devm_spi_alloc_target(&pdev->dev, sizeof(*mcspi)); else - ctlr = spi_alloc_host(&pdev->dev, sizeof(*mcspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*mcspi)); if (!ctlr) return -ENOMEM; @@ -1509,10 +1507,9 @@ static int omap2_mcspi_probe(struct platform_device *pdev) mcspi = spi_controller_get_devdata(ctlr); mcspi->ctlr = ctlr; - match = of_match_device(omap_mcspi_of_match, &pdev->dev); - if (match) { + pdata = of_device_get_match_data(&pdev->dev); + if (pdata) { u32 num_cs = 1; /* default number of chipselect */ - pdata = match->data; of_property_read_u32(node, "ti,spi-num-cs", &num_cs); ctlr->num_chipselect = num_cs; @@ -1530,10 +1527,9 @@ static int omap2_mcspi_probe(struct platform_device *pdev) } mcspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(mcspi->base)) { - status = PTR_ERR(mcspi->base); - goto free_ctlr; - } + if (IS_ERR(mcspi->base)) + return PTR_ERR(mcspi->base); + mcspi->phys = r->start + regs_offset; mcspi->base += regs_offset; @@ -1544,10 +1540,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev) mcspi->dma_channels = devm_kcalloc(&pdev->dev, ctlr->num_chipselect, sizeof(struct omap2_mcspi_dma), GFP_KERNEL); - if (mcspi->dma_channels == NULL) { - status = -ENOMEM; - goto free_ctlr; - } + if (mcspi->dma_channels == NULL) + return -ENOMEM; for (i = 0; i < ctlr->num_chipselect; i++) { sprintf(mcspi->dma_channels[i].dma_rx_ch_name, "rx%d", i); @@ -1556,26 +1550,27 @@ static int omap2_mcspi_probe(struct platform_device *pdev) status = omap2_mcspi_request_dma(mcspi, &mcspi->dma_channels[i]); if (status == -EPROBE_DEFER) - goto free_ctlr; + goto err_release_dma; } status = platform_get_irq(pdev, 0); if (status < 0) - goto free_ctlr; + goto err_release_dma; + init_completion(&mcspi->txdone); status = devm_request_irq(&pdev->dev, status, omap2_mcspi_irq_handler, 0, pdev->name, mcspi); if (status) { dev_err(&pdev->dev, "Cannot request IRQ"); - goto free_ctlr; + goto err_release_dma; } mcspi->ref_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR(mcspi->ref_clk)) { status = PTR_ERR(mcspi->ref_clk); dev_err_probe(&pdev->dev, status, "Failed to get ref_clk"); - goto free_ctlr; + goto err_release_dma; } if (mcspi->ref_clk) mcspi->ref_clk_hz = clk_get_rate(mcspi->ref_clk); @@ -1590,21 +1585,21 @@ static int omap2_mcspi_probe(struct platform_device *pdev) status = omap2_mcspi_controller_setup(mcspi); if (status < 0) - goto disable_pm; + goto err_disable_rpm; status = spi_register_controller(ctlr); if (status < 0) - goto disable_pm; + goto err_disable_rpm; - return status; + return 0; -disable_pm: +err_disable_rpm: pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -free_ctlr: +err_release_dma: omap2_mcspi_release_dma(ctlr); - spi_controller_put(ctlr); + return status; } @@ -1613,8 +1608,6 @@ static void omap2_mcspi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); omap2_mcspi_release_dma(ctlr); @@ -1622,8 +1615,6 @@ static void omap2_mcspi_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(mcspi->dev); pm_runtime_put_sync(mcspi->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(ctlr); } /* work with hotplug and coldplug */ diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 64bf215c1804..265708a94984 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -651,11 +651,9 @@ static int orion_spi_probe(struct platform_device *pdev) struct device_node *np; int status; - host = spi_alloc_host(&pdev->dev, sizeof(*spi)); - if (host == NULL) { - dev_dbg(&pdev->dev, "host allocation failed\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi)); + if (host == NULL) return -ENOMEM; - } if (pdev->id != -1) host->bus_num = pdev->id; @@ -689,17 +687,14 @@ static int orion_spi_probe(struct platform_device *pdev) spi->devdata = devdata; spi->clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(spi->clk)) { - status = PTR_ERR(spi->clk); - goto out; - } + if (IS_ERR(spi->clk)) + return PTR_ERR(spi->clk); /* The following clock is only used by some SoCs */ spi->axi_clk = devm_clk_get(&pdev->dev, "axi"); - if (PTR_ERR(spi->axi_clk) == -EPROBE_DEFER) { - status = -EPROBE_DEFER; - goto out; - } + if (PTR_ERR(spi->axi_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!IS_ERR(spi->axi_clk)) clk_prepare_enable(spi->axi_clk); @@ -796,8 +791,7 @@ out_rel_pm: pm_runtime_dont_use_autosuspend(&pdev->dev); out_rel_axi_clk: clk_disable_unprepare(spi->axi_clk); -out: - spi_controller_put(host); + return status; } @@ -807,15 +801,11 @@ static void orion_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct orion_spi *spi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_get_sync(&pdev->dev); clk_disable_unprepare(spi->axi_clk); - spi_controller_put(host); - pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index 8577a19705de..af6ed78493e3 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -173,27 +173,27 @@ struct pci1xxxx_spi { }; static const struct pci_device_id pci1xxxx_spi_pci_id_table[] = { - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { 0, } + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { } }; MODULE_DEVICE_TABLE(pci, pci1xxxx_spi_pci_id_table); diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c index 41662992dbe5..5d3921e29461 100644 --- a/drivers/spi/spi-pic32-sqi.c +++ b/drivers/spi/spi-pic32-sqi.c @@ -572,7 +572,7 @@ static int pic32_sqi_probe(struct platform_device *pdev) struct pic32_sqi *sqi; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*sqi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*sqi)); if (!host) return -ENOMEM; @@ -580,31 +580,25 @@ static int pic32_sqi_probe(struct platform_device *pdev) sqi->host = host; sqi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(sqi->regs)) { - ret = PTR_ERR(sqi->regs); - goto err_free_host; - } + if (IS_ERR(sqi->regs)) + return PTR_ERR(sqi->regs); /* irq */ sqi->irq = platform_get_irq(pdev, 0); - if (sqi->irq < 0) { - ret = sqi->irq; - goto err_free_host; - } + if (sqi->irq < 0) + return sqi->irq; /* clocks */ sqi->sys_clk = devm_clk_get_enabled(&pdev->dev, "reg_ck"); if (IS_ERR(sqi->sys_clk)) { - ret = PTR_ERR(sqi->sys_clk); dev_err(&pdev->dev, "no sys_clk ?\n"); - goto err_free_host; + return PTR_ERR(sqi->sys_clk); } sqi->base_clk = devm_clk_get_enabled(&pdev->dev, "spi_ck"); if (IS_ERR(sqi->base_clk)) { - ret = PTR_ERR(sqi->base_clk); dev_err(&pdev->dev, "no base clk ?\n"); - goto err_free_host; + return PTR_ERR(sqi->base_clk); } init_completion(&sqi->xfer_done); @@ -616,7 +610,7 @@ static int pic32_sqi_probe(struct platform_device *pdev) ret = ring_desc_ring_alloc(sqi); if (ret) { dev_err(&pdev->dev, "ring alloc failed\n"); - goto err_free_host; + return ret; } /* install irq handlers */ @@ -656,8 +650,6 @@ static int pic32_sqi_probe(struct platform_device *pdev) err_free_ring: ring_desc_ring_free(sqi); -err_free_host: - spi_controller_put(host); return ret; } @@ -665,15 +657,11 @@ static void pic32_sqi_remove(struct platform_device *pdev) { struct pic32_sqi *sqi = platform_get_drvdata(pdev); - spi_controller_get(sqi->host); - spi_unregister_controller(sqi->host); /* release resources */ free_irq(sqi->irq, sqi); ring_desc_ring_free(sqi); - - spi_controller_put(sqi->host); } static const struct of_device_id pic32_sqi_of_ids[] = { diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 70427e529945..972128271e4b 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -752,7 +752,7 @@ static int pic32_spi_probe(struct platform_device *pdev) struct pic32_spi *pic32s; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*pic32s)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*pic32s)); if (!host) return -ENOMEM; @@ -761,7 +761,7 @@ static int pic32_spi_probe(struct platform_device *pdev) ret = pic32_spi_hw_probe(pdev, pic32s); if (ret) - goto err_host; + return ret; host->dev.of_node = pdev->dev.of_node; host->mode_bits = SPI_MODE_3 | SPI_MODE_0 | SPI_CS_HIGH; @@ -833,8 +833,7 @@ static int pic32_spi_probe(struct platform_device *pdev) err_bailout: pic32_spi_dma_unprep(pic32s); -err_host: - spi_controller_put(host); + return ret; } @@ -842,14 +841,10 @@ static void pic32_spi_remove(struct platform_device *pdev) { struct pic32_spi *pic32s = platform_get_drvdata(pdev); - spi_controller_get(pic32s->host); - spi_unregister_controller(pic32s->host); pic32_spi_disable(pic32s); pic32_spi_dma_unprep(pic32s); - - spi_controller_put(pic32s->host); } static const struct of_device_id pic32_spi_of_match[] = { diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 9c0211f94fd0..95652df5fd09 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1868,7 +1868,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) } /* Allocate host with space for data */ - host = spi_alloc_host(dev, sizeof(struct pl022)); + host = devm_spi_alloc_host(dev, sizeof(struct pl022)); if (host == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI host\n"); return -ENOMEM; @@ -1907,7 +1907,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) status = amba_request_regions(adev, NULL); if (status) - goto err_no_ioregion; + return status; pl022->phybase = adev->res.start; pl022->virtbase = devm_ioremap(dev, adev->res.start, @@ -1984,8 +1984,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) err_no_clk: err_no_ioremap: amba_release_regions(adev); - err_no_ioregion: - spi_controller_put(host); + return status; } @@ -1997,8 +1996,6 @@ pl022_remove(struct amba_device *adev) if (!pl022) return; - spi_controller_get(pl022->host); - spi_unregister_controller(pl022->host); /* @@ -2012,8 +2009,6 @@ pl022_remove(struct amba_device *adev) pl022_dma_remove(pl022); amba_release_regions(adev); - - spi_controller_put(pl022->host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index 7e39038160e0..caf55a6f70b3 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -174,6 +174,7 @@ struct qcom_qspi { void *virt_cmd_desc[QSPI_MAX_SG]; unsigned int n_cmd_desc; struct icc_path *icc_path_cpu_to_qspi; + struct icc_path *icc_path_mem; unsigned long last_speed; /* Lock to protect data accessed by IRQs */ spinlock_t lock; @@ -272,7 +273,7 @@ static void qcom_qspi_handle_err(struct spi_controller *host, static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) { int ret; - unsigned int avg_bw_cpu; + unsigned int avg_bw_cpu, avg_bw_mem; if (speed_hz == ctrl->last_speed) return 0; @@ -285,7 +286,7 @@ static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) } /* - * Set BW quota for CPU. + * Set BW quota for CPU and memory paths. * We don't have explicit peak requirement so keep it equal to avg_bw. */ avg_bw_cpu = Bps_to_icc(speed_hz); @@ -296,6 +297,13 @@ static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) return ret; } + avg_bw_mem = Bps_to_icc(speed_hz); + ret = icc_set_bw(ctrl->icc_path_mem, avg_bw_mem, avg_bw_mem); + if (ret) { + dev_err(ctrl->dev, "ICC BW voting failed for memory: %d\n", ret); + return ret; + } + ctrl->last_speed = speed_hz; return 0; @@ -729,6 +737,14 @@ static int qcom_qspi_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi), "Failed to get cpu path\n"); + ctrl->icc_path_mem = devm_of_icc_get(dev, "qspi-memory"); + if (IS_ERR(ctrl->icc_path_mem)) { + if (PTR_ERR(ctrl->icc_path_mem) != -ENODATA) + return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_mem), + "Failed to get memory path\n"); + ctrl->icc_path_mem = NULL; + } + /* Set BW vote for register access */ ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000), Bps_to_icc(1000)); @@ -818,20 +834,42 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) struct qcom_qspi *ctrl = spi_controller_get_devdata(host); int ret; - /* Drop the performance state vote */ - dev_pm_opp_set_rate(dev, 0); clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); ret = icc_disable(ctrl->icc_path_cpu_to_qspi); if (ret) { dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", __func__, ret); - return ret; + goto err_enable_clk; } - pinctrl_pm_select_sleep_state(dev); + ret = icc_disable(ctrl->icc_path_mem); + if (ret) { + dev_err_ratelimited(ctrl->dev, "ICC disable failed for memory: %d\n", ret); + goto err_enable_icc_cpu; + } + + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + goto err_enable_icc_mem; + + /* Drop the performance state vote */ + ret = dev_pm_opp_set_rate(dev, 0); + if (ret) + goto err_select_default_state; return 0; + +err_select_default_state: + pinctrl_pm_select_default_state(dev); +err_enable_icc_mem: + icc_enable(ctrl->icc_path_mem); +err_enable_icc_cpu: + icc_enable(ctrl->icc_path_cpu_to_qspi); +err_enable_clk: + if (clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks)) + dev_err_ratelimited(ctrl->dev, "Failed to re-enable clocks\n"); + return ret; } static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) @@ -840,20 +878,42 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) struct qcom_qspi *ctrl = spi_controller_get_devdata(host); int ret; - pinctrl_pm_select_default_state(dev); + ret = dev_pm_opp_set_rate(dev, ctrl->last_speed * 4); + if (ret) + return ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + goto err_opp_set_rate_zero; ret = icc_enable(ctrl->icc_path_cpu_to_qspi); if (ret) { dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n", __func__, ret); - return ret; + goto err_select_sleep_state; + } + + ret = icc_enable(ctrl->icc_path_mem); + if (ret) { + dev_err_ratelimited(ctrl->dev, "ICC enable failed for memory: %d\n", ret); + goto err_disable_icc_cpu; } ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); if (ret) - return ret; + goto err_disable_icc_mem; + + return 0; - return dev_pm_opp_set_rate(dev, ctrl->last_speed * 4); +err_disable_icc_mem: + icc_disable(ctrl->icc_path_mem); +err_disable_icc_cpu: + icc_disable(ctrl->icc_path_cpu_to_qspi); +err_select_sleep_state: + pinctrl_pm_select_sleep_state(dev); +err_opp_set_rate_zero: + dev_pm_opp_set_rate(dev, 0); + return ret; } static int __maybe_unused qcom_qspi_suspend(struct device *dev) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 50bb7701b9d5..034546b3f058 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -1074,11 +1074,9 @@ static int spi_qup_probe(struct platform_device *pdev) if (ret && ret != -ENODEV) return dev_err_probe(dev, ret, "invalid OPP table\n"); - host = spi_alloc_host(dev, sizeof(struct spi_qup)); - if (!host) { - dev_err(dev, "cannot allocate host\n"); + host = devm_spi_alloc_host(dev, sizeof(struct spi_qup)); + if (!host) return -ENOMEM; - } /* use num-cs unless not present or out of range */ if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) || @@ -1111,7 +1109,7 @@ static int spi_qup_probe(struct platform_device *pdev) ret = spi_qup_init_dma(host, res->start); if (ret == -EPROBE_DEFER) - goto error; + return ret; else if (!ret) host->can_dma = spi_qup_can_dma; @@ -1209,8 +1207,7 @@ error_clk: clk_disable_unprepare(iclk); error_dma: spi_qup_release_dma(host); -error: - spi_controller_put(host); + return ret; } @@ -1323,8 +1320,6 @@ static void spi_qup_remove(struct platform_device *pdev) struct spi_qup *controller = spi_controller_get_devdata(host); int ret; - spi_controller_get(host); - spi_unregister_controller(host); ret = pm_runtime_get_sync(&pdev->dev); @@ -1346,8 +1341,6 @@ static void spi_qup_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(host); } static const struct of_device_id spi_qup_dt_match[] = { diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index 6edc0c4db854..1ef7bd91b3b3 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -83,7 +83,7 @@ static ssize_t xspi_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, if (offs + desc->info.offset + len > U32_MAX) return -EINVAL; - rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len); + rpcif_spi_mem_prepare(desc->mem->spi, desc->info.op_tmpl, &offs, &len); return xspi_dirmap_write(rpc->dev, offs, len, buf); } @@ -97,7 +97,7 @@ static ssize_t rpcif_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, if (offs + desc->info.offset + len > U32_MAX) return -EINVAL; - rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len); + rpcif_spi_mem_prepare(desc->mem->spi, desc->info.op_tmpl, &offs, &len); return rpcif_dirmap_read(rpc->dev, offs, len, buf); } @@ -110,13 +110,13 @@ static int rpcif_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc) if (desc->info.offset + desc->info.length > U32_MAX) return -EINVAL; - if (!rpcif_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!rpcif_spi_mem_supports_op(desc->mem, desc->info.op_tmpl)) return -EOPNOTSUPP; if (!rpc->dirmap) return -EOPNOTSUPP; - if (!rpc->xspi && desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + if (!rpc->xspi && desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN) return -EOPNOTSUPP; return 0; diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index a8180dece716..38df676774ee 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1171,14 +1171,10 @@ static void rspi_remove(struct platform_device *pdev) { struct rspi_data *rspi = platform_get_drvdata(pdev); - spi_controller_get(rspi->ctlr); - spi_unregister_controller(rspi->ctlr); rspi_release_dma(rspi->ctlr); pm_runtime_disable(&pdev->dev); - - spi_controller_put(rspi->ctlr); } static const struct spi_ops rspi_ops = { @@ -1226,11 +1222,6 @@ static const struct of_device_id rspi_of_match[] __maybe_unused = { MODULE_DEVICE_TABLE(of, rspi_of_match); #ifdef CONFIG_OF -static void rspi_reset_control_assert(void *data) -{ - reset_control_assert(data); -} - static int rspi_parse_dt(struct device *dev, struct spi_controller *ctlr) { struct reset_control *rstc; @@ -1246,22 +1237,10 @@ static int rspi_parse_dt(struct device *dev, struct spi_controller *ctlr) ctlr->num_chipselect = num_cs; - rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), - "failed to get reset ctrl\n"); - - error = reset_control_deassert(rstc); - if (error) { - dev_err(dev, "failed to deassert reset %d\n", error); - return error; - } - - error = devm_add_action_or_reset(dev, rspi_reset_control_assert, rstc); - if (error) { - dev_err(dev, "failed to register assert devm action, %d\n", error); - return error; - } + "failed to get reset ctrl and deassert reset\n"); return 0; } @@ -1294,7 +1273,7 @@ static int rspi_probe(struct platform_device *pdev) const struct spi_ops *ops; unsigned long clksrc; - ctlr = spi_alloc_host(&pdev->dev, sizeof(struct rspi_data)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(struct rspi_data)); if (ctlr == NULL) return -ENOMEM; @@ -1302,7 +1281,7 @@ static int rspi_probe(struct platform_device *pdev) if (ops) { ret = rspi_parse_dt(&pdev->dev, ctlr); if (ret) - goto error1; + return ret; } else { ops = (struct spi_ops *)pdev->id_entry->driver_data; ctlr->num_chipselect = 2; /* default */ @@ -1314,16 +1293,13 @@ static int rspi_probe(struct platform_device *pdev) rspi->ctlr = ctlr; rspi->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(rspi->addr)) { - ret = PTR_ERR(rspi->addr); - goto error1; - } + if (IS_ERR(rspi->addr)) + return PTR_ERR(rspi->addr); rspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rspi->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(rspi->clk); - goto error1; + return PTR_ERR(rspi->clk); } rspi->pdev = pdev; @@ -1396,15 +1372,13 @@ error3: rspi_release_dma(ctlr); error2: pm_runtime_disable(&pdev->dev); -error1: - spi_controller_put(ctlr); return ret; } static const struct platform_device_id spi_driver_ids[] = { - { "rspi", (kernel_ulong_t)&rspi_ops }, - {}, + { .name = "rspi", .driver_data = (kernel_ulong_t)&rspi_ops }, + { } }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index 1655efda7d20..0738d448160d 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -135,8 +135,9 @@ static inline void rzv2h_rspi_rx_##type(struct rzv2h_rspi_priv *rspi, \ RZV2H_RSPI_TX(writel, u32) RZV2H_RSPI_TX(writew, u16) RZV2H_RSPI_TX(writeb, u8) +/* The read access size for RSPI_SPDR is fixed at 32 bits */ RZV2H_RSPI_RX(readl, u32) -RZV2H_RSPI_RX(readw, u16) +RZV2H_RSPI_RX(readl, u16) RZV2H_RSPI_RX(readl, u8) static void rzv2h_rspi_reg_rmw(const struct rzv2h_rspi_priv *rspi, @@ -802,6 +803,23 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) return ret; } +static int rzv2h_rspi_suspend(struct device *dev) +{ + struct rzv2h_rspi_priv *rspi = dev_get_drvdata(dev); + + return spi_controller_suspend(rspi->controller); +} + +static int rzv2h_rspi_resume(struct device *dev) +{ + struct rzv2h_rspi_priv *rspi = dev_get_drvdata(dev); + + return spi_controller_resume(rspi->controller); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rzv2h_rspi_pm_ops, rzv2h_rspi_suspend, + rzv2h_rspi_resume); + static const struct rzv2h_rspi_info rzv2h_info = { .find_tclk_rate = rzv2h_rspi_find_rate_fixed, .tclk_name = "tclk", @@ -837,6 +855,7 @@ static struct platform_driver rzv2h_rspi_drv = { .driver = { .name = "rzv2h_rspi", .of_match_table = rzv2h_rspi_match, + .pm = pm_sleep_ptr(&rzv2h_rspi_pm_ops), }, }; module_platform_driver(rzv2h_rspi_drv); diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 37176e557099..28c56b06fa99 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1613,10 +1613,10 @@ static const struct s3c64xx_spi_port_config gs101_spi_port_config = { static const struct platform_device_id s3c64xx_spi_driver_ids[] = { { - .name = "s3c6410-spi", - .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, + .name = "s3c6410-spi", + .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, }, - { }, + { } }; MODULE_DEVICE_TABLE(platform, s3c64xx_spi_driver_ids); diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 78c558e7228e..ae534ebd5e87 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -295,9 +295,9 @@ static int sc18is602_probe(struct i2c_client *client) } static const struct i2c_device_id sc18is602_id[] = { - { "sc18is602", sc18is602 }, - { "sc18is602b", sc18is602b }, - { "sc18is603", sc18is603 }, + { .name = "sc18is602", .driver_data = sc18is602 }, + { .name = "sc18is602b", .driver_data = sc18is602b }, + { .name = "sc18is603", .driver_data = sc18is603 }, { } }; MODULE_DEVICE_TABLE(i2c, sc18is602_id); diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 1e3ca718ca73..f840467cfdb2 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -224,15 +224,14 @@ static int hspi_probe(struct platform_device *pdev) return -EINVAL; } - ctlr = spi_alloc_host(&pdev->dev, sizeof(*hspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*hspi)); if (!ctlr) return -ENOMEM; clk = clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "couldn't get clock\n"); - ret = -EINVAL; - goto error0; + return PTR_ERR(clk); } hspi = spi_controller_get_devdata(ctlr); @@ -269,8 +268,6 @@ static int hspi_probe(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); error1: clk_put(clk); - error0: - spi_controller_put(ctlr); return ret; } @@ -279,15 +276,11 @@ static void hspi_remove(struct platform_device *pdev) { struct hspi_priv *hspi = platform_get_drvdata(pdev); - spi_controller_get(hspi->ctlr); - spi_unregister_controller(hspi->ctlr); pm_runtime_disable(&pdev->dev); clk_put(hspi->clk); - - spi_controller_put(hspi->ctlr); } static const struct of_device_id hspi_of_match[] = { diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index f114b6313f4f..f23db85a1889 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1215,9 +1215,9 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) info->dtdl = 200; if (info->mode == MSIOF_SPI_TARGET) - ctlr = spi_alloc_target(dev, sizeof(struct sh_msiof_spi_priv)); + ctlr = devm_spi_alloc_target(dev, sizeof(struct sh_msiof_spi_priv)); else - ctlr = spi_alloc_host(dev, sizeof(struct sh_msiof_spi_priv)); + ctlr = devm_spi_alloc_host(dev, sizeof(struct sh_msiof_spi_priv)); if (ctlr == NULL) return -ENOMEM; @@ -1234,26 +1234,21 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p->clk = devm_clk_get(dev, NULL); if (IS_ERR(p->clk)) { dev_err(dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; + return PTR_ERR(p->clk); } i = platform_get_irq(pdev, 0); - if (i < 0) { - ret = i; - goto err1; - } + if (i < 0) + return i; p->mapbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(p->mapbase)) { - ret = PTR_ERR(p->mapbase); - goto err1; - } + if (IS_ERR(p->mapbase)) + return PTR_ERR(p->mapbase); ret = devm_request_irq(dev, i, sh_msiof_spi_irq, 0, dev_name(dev), p); if (ret) { dev_err(dev, "unable to request irq\n"); - goto err1; + return ret; } p->pdev = pdev; @@ -1300,8 +1295,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) err2: sh_msiof_release_dma(p); pm_runtime_disable(dev); - err1: - spi_controller_put(ctlr); + return ret; } @@ -1309,19 +1303,15 @@ static void sh_msiof_spi_remove(struct platform_device *pdev) { struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev); - spi_controller_get(p->ctlr); - spi_unregister_controller(p->ctlr); sh_msiof_release_dma(p); pm_runtime_disable(&pdev->dev); - - spi_controller_put(p->ctlr); } static const struct platform_device_id spi_driver_ids[] = { - { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, - {}, + { .name = "spi_sh_msiof", .driver_data = (kernel_ulong_t)&sh_data }, + { } }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c index 74a3e32fd2b5..cee4d92e46f4 100644 --- a/drivers/spi/spi-sifive.c +++ b/drivers/spi/spi-sifive.c @@ -296,7 +296,7 @@ static int sifive_spi_probe(struct platform_device *pdev) u32 cs_bits, max_bits_per_word; struct spi_controller *host; - host = spi_alloc_host(&pdev->dev, sizeof(struct sifive_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct sifive_spi)); if (!host) { dev_err(&pdev->dev, "out of memory\n"); return -ENOMEM; @@ -307,24 +307,19 @@ static int sifive_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); spi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(spi->regs)) { - ret = PTR_ERR(spi->regs); - goto put_host; - } + if (IS_ERR(spi->regs)) + return PTR_ERR(spi->regs); /* Spin up the bus clock before hitting registers */ spi->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(spi->clk)) { dev_err(&pdev->dev, "Unable to find bus clock\n"); - ret = PTR_ERR(spi->clk); - goto put_host; + return PTR_ERR(spi->clk); } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto put_host; - } + if (irq < 0) + return irq; /* Optional parameters */ ret = @@ -339,8 +334,7 @@ static int sifive_spi_probe(struct platform_device *pdev) if (!ret && max_bits_per_word < 8) { dev_err(&pdev->dev, "Only 8bit SPI words supported by the driver\n"); - ret = -EINVAL; - goto put_host; + return -EINVAL; } /* probe the number of CS lines */ @@ -350,15 +344,13 @@ static int sifive_spi_probe(struct platform_device *pdev) sifive_spi_write(spi, SIFIVE_SPI_REG_CSDEF, spi->cs_inactive); if (!cs_bits) { dev_err(&pdev->dev, "Could not auto probe CS lines\n"); - ret = -EINVAL; - goto put_host; + return -EINVAL; } num_cs = ilog2(cs_bits) + 1; if (num_cs > SIFIVE_SPI_MAX_CS) { dev_err(&pdev->dev, "Invalid number of spi targets\n"); - ret = -EINVAL; - goto put_host; + return -EINVAL; } /* Define our host */ @@ -386,7 +378,7 @@ static int sifive_spi_probe(struct platform_device *pdev) dev_name(&pdev->dev), spi); if (ret) { dev_err(&pdev->dev, "Unable to bind to interrupt\n"); - goto put_host; + return ret; } dev_info(&pdev->dev, "mapped; irq=%d, cs=%d\n", @@ -395,15 +387,10 @@ static int sifive_spi_probe(struct platform_device *pdev) ret = spi_register_controller(host); if (ret < 0) { dev_err(&pdev->dev, "spi_register_host failed\n"); - goto put_host; + return ret; } return 0; - -put_host: - spi_controller_put(host); - - return ret; } static void sifive_spi_remove(struct platform_device *pdev) @@ -411,14 +398,10 @@ static void sifive_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct sifive_spi *spi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* Disable all the interrupts just in case */ sifive_spi_write(spi, SIFIVE_SPI_REG_IE, 0); - - spi_controller_put(host); } static int sifive_spi_suspend(struct device *dev) diff --git a/drivers/spi/spi-slave-mt27xx.c b/drivers/spi/spi-slave-mt27xx.c index 7aedeaa5889d..e60ab4c18bed 100644 --- a/drivers/spi/spi-slave-mt27xx.c +++ b/drivers/spi/spi-slave-mt27xx.c @@ -388,11 +388,9 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) int irq, ret; const struct of_device_id *of_id; - ctlr = spi_alloc_target(&pdev->dev, sizeof(*mdata)); - if (!ctlr) { - dev_err(&pdev->dev, "failed to alloc spi target\n"); + ctlr = devm_spi_alloc_target(&pdev->dev, sizeof(*mdata)); + if (!ctlr) return -ENOMEM; - } ctlr->auto_runtime_pm = true; ctlr->mode_bits = SPI_CPOL | SPI_CPHA; @@ -406,8 +404,7 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) of_id = of_match_node(mtk_spi_slave_of_match, pdev->dev.of_node); if (!of_id) { dev_err(&pdev->dev, "failed to probe of_node\n"); - ret = -EINVAL; - goto err_put_ctlr; + return -EINVAL; } mdata = spi_controller_get_devdata(ctlr); mdata->dev_comp = of_id->data; @@ -420,35 +417,31 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) init_completion(&mdata->xfer_done); mdata->dev = &pdev->dev; mdata->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(mdata->base)) { - ret = PTR_ERR(mdata->base); - goto err_put_ctlr; - } + if (IS_ERR(mdata->base)) + return PTR_ERR(mdata->base); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto err_put_ctlr; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, mtk_spi_slave_interrupt, IRQF_TRIGGER_NONE, dev_name(&pdev->dev), ctlr); if (ret) { dev_err(&pdev->dev, "failed to register irq (%d)\n", ret); - goto err_put_ctlr; + return ret; } mdata->spi_clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(mdata->spi_clk)) { ret = PTR_ERR(mdata->spi_clk); dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); - goto err_put_ctlr; + return ret; } ret = clk_prepare_enable(mdata->spi_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); - goto err_put_ctlr; + return ret; } pm_runtime_enable(&pdev->dev); @@ -465,8 +458,6 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) err_disable_runtime_pm: pm_runtime_disable(&pdev->dev); -err_put_ctlr: - spi_controller_put(ctlr); return ret; } @@ -475,13 +466,9 @@ static void mtk_spi_slave_remove(struct platform_device *pdev) { struct spi_controller *ctlr = platform_get_drvdata(pdev); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); pm_runtime_disable(&pdev->dev); - - spi_controller_put(ctlr); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-sn-f-ospi.c b/drivers/spi/spi-sn-f-ospi.c index b459d51cb3a8..f0320e96fe23 100644 --- a/drivers/spi/spi-sn-f-ospi.c +++ b/drivers/spi/spi-sn-f-ospi.c @@ -222,9 +222,8 @@ static void f_ospi_config_clk(struct f_ospi *ospi, u32 device_hz) */ val = readl(ospi->base + OSPI_CLK_CTL); - val &= ~(OSPI_CLK_CTL_PHA | OSPI_CLK_CTL_DIV); - val |= FIELD_PREP(OSPI_CLK_CTL_PHA, OSPI_CLK_CTL_PHA_180) - | FIELD_PREP(OSPI_CLK_CTL_DIV, div_reg); + FIELD_MODIFY(OSPI_CLK_CTL_PHA, &val, OSPI_CLK_CTL_PHA_180); + FIELD_MODIFY(OSPI_CLK_CTL_DIV, &val, div_reg); writel(val, ospi->base + OSPI_CLK_CTL); } diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c new file mode 100644 index 000000000000..215fe66d27b4 --- /dev/null +++ b/drivers/spi/spi-spacemit-k1.c @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// SpacemiT K1 SPI controller driver +// +// Copyright (C) 2026, RISCstar Solutions Corporation +// Copyright (C) 2023, SpacemiT Corporation + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/scatterlist.h> +#include <linux/sizes.h> +#include <linux/spi/spi.h> +#include <linux/units.h> + +#include "internals.h" + +/* This is the range of transfer rates supported by the K1 SoC */ +#define K1_SPI_MIN_SPEED_HZ 6250 +#define K1_SPI_MAX_SPEED_HZ 51200000 + +/* DMA constraints */ +#define K1_SPI_DMA_ALIGNMENT 64 +#define K1_SPI_MAX_DMA_LEN SZ_512K + +/* SSP Top Control Register */ +#define SSP_TOP_CTRL 0x00 +#define TOP_SSE BIT(0) /* Enable port */ +#define TOP_FRF_MASK GENMASK(2, 1) /* Frame format */ +#define TOP_FRF_MOTOROLA 0 /* Motorola SPI */ +#define TOP_DSS_MASK GENMASK(9, 5) /* Data size (1-32) */ +#define TOP_SPO BIT(10) /* Polarity: 0=low */ +#define TOP_SPH BIT(11) /* Half-cycle phase */ +#define TOP_LBM BIT(12) /* Loopback mode */ +#define TOP_TRAIL BIT(13) /* Trailing bytes */ +#define TOP_HOLD_FRAME_LOW BIT(14) /* Chip select */ + +/* SSP FIFO Control Register */ +#define SSP_FIFO_CTRL 0x04 +#define FIFO_TFT_MASK GENMASK(4, 0) /* TX FIFO threshold */ +#define FIFO_RFT_MASK GENMASK(9, 5) /* RX FIFO threshold */ +#define FIFO_TSRE BIT(10) /* TX service request */ +#define FIFO_RSRE BIT(11) /* RX service request */ + +/* SSP Interrupt Enable Register */ +#define SSP_INT_EN 0x08 +#define SSP_INT_EN_TINTE BIT(1) /* RX timeout */ +#define SSP_INT_EN_RIE BIT(2) /* RX FIFO */ +#define SSP_INT_EN_TIE BIT(3) /* TX FIFO */ +#define SSP_INT_EN_RIM BIT(4) /* RX FIFO overrun */ +#define SSP_INT_EN_TIM BIT(5) /* TX FIFO underrun */ +#define SSP_INT_EN_EBCEI BIT(6) /* Bit count error */ + +/* TX interrupts, RX interrupts, and error interrupts */ +#define SSP_INT_EN_TX SSP_INT_EN_TIE +#define SSP_INT_EN_RX \ + (SSP_INT_EN_TINTE | SSP_INT_EN_RIE) +#define SSP_INT_EN_ERROR \ + (SSP_INT_EN_RIM | SSP_INT_EN_TIM | SSP_INT_EN_EBCEI) + +/* SSP Time Out Register */ +#define SSP_TIMEOUT 0x0c +#define SSP_TIMEOUT_MASK GENMASK(23, 0) + +/* SSP Data Register */ +#define SSP_DATAR 0x10 + +/* SSP Status Register */ +#define SSP_STATUS 0x14 +#define SSP_STATUS_BSY BIT(0) /* SPI/I2S busy */ +#define SSP_STATUS_TNF BIT(6) /* TX FIFO not full */ +#define SSP_STATUS_TFL GENMASK(11, 7) /* TX FIFO level */ +#define SSP_STATUS_TUR BIT(12) /* TX FIFO underrun */ +#define SSP_STATUS_RNE BIT(14) /* RX FIFO not empty */ +#define SSP_STATUS_RFL GENMASK(19, 15) /* RX FIFO level */ +#define SSP_STATUS_ROR BIT(20) /* RX FIFO overrun */ +#define SSP_STATUS_BCE BIT(21) /* Bit count error */ + +/* Error status mask */ +#define SSP_STATUS_ERROR \ + (SSP_STATUS_TUR | SSP_STATUS_ROR | SSP_STATUS_BCE) + +/* The FIFO sizes and thresholds are the same for RX and TX */ +#define K1_SPI_FIFO_SIZE 32 +#define K1_SPI_THRESH (K1_SPI_FIFO_SIZE / 2) + +struct k1_spi_driver_data { + struct spi_controller *host; + void __iomem *base; + phys_addr_t base_addr; + unsigned long bus_rate; + struct clk *clk; + unsigned long rate; + int irq; + + /* Current transfer information; not valid if message is null */ + u32 bytes; /* Bytes used for bits_per_word */ + unsigned int rx_resid; /* RX bytes left in transfer */ + unsigned int tx_resid; /* TX bytes left in transfer */ + struct spi_transfer *transfer; /* Current transfer */ + + bool dma_enabled; +}; + +/* Set our registers to a known initial state */ +static void +k1_spi_register_reset(struct k1_spi_driver_data *drv_data, bool initial) +{ + u32 val = 0; + + writel(0, drv_data->base + SSP_TOP_CTRL); + + if (initial) { + /* + * The TX and RX FIFO thresholds are the same no matter + * what the speed or bits per word, so we can just set + * them once. The thresholds are one more than the values + * in the register. + */ + val = FIELD_PREP(FIFO_RFT_MASK, K1_SPI_THRESH - 1); + val |= FIELD_PREP(FIFO_TFT_MASK, K1_SPI_THRESH - 1); + } + writel(val, drv_data->base + SSP_FIFO_CTRL); + + writel(0, drv_data->base + SSP_INT_EN); + writel(0, drv_data->base + SSP_TIMEOUT); + + /* Clear any pending interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); +} + +/* + * The client can call the setup function multiple times, and each call + * can specify a different SPI mode (and transfer speed). Each transfer + * can specify its own speed though, and the core code ensures each + * transfer's speed is set to something nonzero and supported by both + * the controller and the device. We just set the speed for each transfer. + */ +static int k1_spi_setup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data = spi_controller_get_devdata(spi->controller); + + /* + * Configure the message format for this device. We only + * support Motorola SPI format in master mode. + */ + val = FIELD_PREP(TOP_FRF_MASK, TOP_FRF_MOTOROLA); + + /* Translate the mode into the value used to program the hardware. */ + if (spi->mode & SPI_CPHA) + val |= TOP_SPH; /* 1/2 cycle */ + if (spi->mode & SPI_CPOL) + val |= TOP_SPO; /* active low */ + if (spi->mode & SPI_LOOP) + val |= TOP_LBM; /* enable loopback */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + return 0; +} + +static void k1_spi_cleanup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + + drv_data = spi_controller_get_devdata(spi->controller); + k1_spi_register_reset(drv_data, false); +} + +static bool k1_spi_can_dma(struct spi_controller *host, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 burst_size; + + if (!drv_data->dma_enabled) + return false; + + if (transfer->len > SZ_2K) + return false; + + /* Don't bother with DMA if we can't do even a single burst */ + burst_size = K1_SPI_THRESH * spi_bpw_to_bytes(transfer->bits_per_word); + + return transfer->len >= burst_size; +} + +static void k1_spi_dma_callback(void *param) +{ + struct k1_spi_driver_data *drv_data = param; + u32 val; + + val = readl(drv_data->base + SSP_FIFO_CTRL); + val &= ~(FIFO_TSRE | FIFO_RSRE); + writel(val, drv_data->base + SSP_FIFO_CTRL); + + val = readl(drv_data->base + SSP_TOP_CTRL); + val &= ~TOP_TRAIL; + writel(val, drv_data->base + SSP_TOP_CTRL); + + /* Check for any error conditions */ + val = readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_ERROR) + drv_data->transfer->error |= SPI_TRANS_FAIL_IO; + + /* Disable the port */ + val = readl(drv_data->base + SSP_TOP_CTRL); + val &= ~TOP_SSE; + writel(val, drv_data->base + SSP_TOP_CTRL); + + drv_data->transfer = NULL; + + spi_finalize_current_transfer(drv_data->host); +} + +/* Prepare a descriptor for TX or RX DMA */ +static struct dma_async_tx_descriptor * +k1_spi_dma_prep(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer, bool tx) +{ + phys_addr_t addr = drv_data->base_addr + SSP_DATAR; + u32 burst_size = K1_SPI_THRESH * drv_data->bytes; + struct dma_slave_config cfg = { }; + enum dma_transfer_direction dir; + enum dma_slave_buswidth width; + struct dma_chan *chan; + struct sg_table *sgt; + + switch (drv_data->bytes) { + case 1: + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 2: + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: /* bytes == 4 */ + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + if (tx) { + chan = drv_data->host->dma_tx; + sgt = &transfer->tx_sg; + dir = DMA_MEM_TO_DEV; + + cfg.dst_addr = addr; + cfg.dst_addr_width = width; + cfg.dst_maxburst = burst_size; + } else { + chan = drv_data->host->dma_rx; + sgt = &transfer->rx_sg; + dir = DMA_DEV_TO_MEM; + + cfg.src_addr = addr; + cfg.src_addr_width = width; + cfg.src_maxburst = burst_size; + } + cfg.direction = dir; + + if (dmaengine_slave_config(chan, &cfg)) + return NULL; + + return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + +} + +static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + struct dma_async_tx_descriptor *desc; + u32 val; + + /* Prepare the TX descriptor and submit it */ + desc = k1_spi_dma_prep(drv_data, transfer, true); + if (!desc) + goto fallback; + dmaengine_submit(desc); + + /* Prepare the RX descriptor and submit it */ + desc = k1_spi_dma_prep(drv_data, transfer, false); + if (!desc) + goto fallback; + + /* When RX is complete we also know TX has completed */ + desc->callback = k1_spi_dma_callback; + desc->callback_param = drv_data; + + dmaengine_submit(desc); + + val = readl(drv_data->base + SSP_TOP_CTRL); + val |= TOP_TRAIL; /* Trailing bytes handled by DMA */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + val = readl(drv_data->base + SSP_FIFO_CTRL); + val |= FIFO_TSRE | FIFO_RSRE; + writel(val, drv_data->base + SSP_FIFO_CTRL); + + /* Start RX first so we're ready the instant we start transmitting */ + dma_async_issue_pending(host->dma_rx); + dma_async_issue_pending(host->dma_tx); + + return 1; +fallback: + transfer->error |= SPI_TRANS_FAIL_NO_START; + + return -EAGAIN; +} + +/* Flush the RX FIFO of any leftover data before processing a message */ +static int k1_spi_prepare_message(struct spi_controller *host, + struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 val = readl(drv_data->base + SSP_STATUS); + u32 count; + + /* If there's nothing in the FIFO, we're done */ + if (!(val & SSP_STATUS_RNE)) + return 0; + + /* Read and discard what's there (one more than what the field says) */ + count = FIELD_GET(SSP_STATUS_RFL, val) + 1; + do + (void)readl(drv_data->base + SSP_DATAR); + while (--count); + + return 0; +} + +/* Set logic level of chip select line (high=true means CS deasserted) */ +static void k1_spi_set_cs(struct spi_device *spi, bool high) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data = spi_controller_get_devdata(spi->controller); + + val = readl(drv_data->base + SSP_TOP_CTRL); + if (high) + val &= ~TOP_HOLD_FRAME_LOW; + else + val |= TOP_HOLD_FRAME_LOW; + writel(val, drv_data->base + SSP_TOP_CTRL); +} + +/* Set the transfer speed; the SPI core code ensures it is supported */ +static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer) +{ + struct clk *clk = drv_data->clk; + u64 nsec_per_word; + u64 bus_ticks; + u32 timeout; + u32 val; + int ret; + + ret = clk_set_rate(clk, transfer->speed_hz); + if (ret) + return ret; + + drv_data->rate = clk_get_rate(clk); + + /* No need for RX FIFO timeout if we're not receiving anything */ + if (!transfer->rx_buf) + return 0; + + /* + * Compute the RX FIFO inactivity timeout value that should be used. + * The inactivity timer restarts with each word that lands in the + * FIFO. If several "word transfer times" pass without any new data + * in the RX FIFO, we might as well read what's there. + * + * The rate at which words land in the FIFO is determined by the + * word size and the transfer rate. One bit is transferred per + * clock tick, and 8 (or 16 or 32) bits are transferred per word. + * + * So we can get word transfer time (in nanoseconds) from: + * nsec_per_tick = NSEC_PER_SEC / drv_data->rate; + * ticks_per_word = BITS_PER_BYTE * drv_data->bytes; + * We do the divide last for better accuracy. + */ + nsec_per_word = (u64)NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; + nsec_per_word = DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate); + + /* + * The timeout (which we'll set to three word transfer times) is + * expressed as a number of APB clock ticks. + * bus_ticks = 3 * nsec * (drv_data->bus_rate / NSEC_PER_SEC) + */ + bus_ticks = 3 * nsec_per_word * drv_data->bus_rate; + timeout = DIV_ROUND_UP_ULL(bus_ticks, NSEC_PER_SEC); + + /* Set the RX timeout period (required for both DMA and PIO) */ + val = FIELD_PREP(SSP_TIMEOUT_MASK, timeout); + writel(val, drv_data->base + SSP_TIMEOUT); + + return 0; +} + +static int k1_spi_transfer_one(struct spi_controller *host, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 ctrl; + u32 val; + int ret; + + /* Bits per word can change on a per-transfer basis */ + drv_data->bytes = spi_bpw_to_bytes(transfer->bits_per_word); + + /* Each transfer can also specify a different rate */ + ret = k1_spi_set_speed(drv_data, transfer); + if (ret) { + dev_err(&host->dev, + "failed to set transfer speed: %d\n", ret); + return ret; + } + + drv_data->rx_resid = transfer->len; + drv_data->tx_resid = transfer->len; + + drv_data->transfer = transfer; + + /* Clear any existing interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); + + /* Set the data (word) size, and enable the port */ + ctrl = readl(drv_data->base + SSP_TOP_CTRL); + ctrl &= ~TOP_DSS_MASK; + ctrl |= FIELD_PREP(TOP_DSS_MASK, transfer->bits_per_word - 1); + ctrl |= TOP_SSE; + writel(ctrl, drv_data->base + SSP_TOP_CTRL); + + if (spi_xfer_is_dma_mapped(host, spi, transfer)) + return k1_spi_dma_one(host, spi, transfer); + + /* An interrupt will initiate the transfer */ + val = SSP_INT_EN_TX | SSP_INT_EN_RX | SSP_INT_EN_ERROR; + writel(val, drv_data->base + SSP_INT_EN); + + return 1; /* We will call spi_finalize_current_transfer() */ +} + +static void +k1_spi_handle_err(struct spi_controller *host, struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + + if (drv_data->dma_enabled) { + dmaengine_terminate_sync(host->dma_rx); + dmaengine_terminate_sync(host->dma_tx); + } +} + +static void k1_spi_write_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer = drv_data->transfer; + u32 bytes = drv_data->bytes; + u32 val; + + if (transfer->tx_buf) { + const void *buf; + + buf = transfer->tx_buf + (transfer->len - drv_data->tx_resid); + if (bytes == 1) + val = *(u8 *)buf; + else if (bytes == 2) + val = *(u16 *)buf; + else /* bytes == 4 */ + val = *(u32 *)buf; + } else { + val = 0; /* Null writer; write 1, 2, or 4 zero bytes */ + } + /* Fill the next TX FIFO entry */ + writel(val, drv_data->base + SSP_DATAR); + + drv_data->tx_resid -= bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_TNF is set */ +static bool k1_spi_write(struct k1_spi_driver_data *drv_data, u32 val) +{ + unsigned int count; + + /* Get the number of open slots in the FIFO; zero means all */ + count = FIELD_GET(SSP_STATUS_TFL, val) ? : K1_SPI_FIFO_SIZE; + + /* + * Limit how much we try to send at a time, to reduce the + * chance the other side can overrun our RX FIFO. + */ + count = min3(count, K1_SPI_THRESH, drv_data->tx_resid / drv_data->bytes); + do + k1_spi_write_word(drv_data); + while (--count); + + return !drv_data->tx_resid; +} + +static void k1_spi_read_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer = drv_data->transfer; + u32 bytes = drv_data->bytes; + u32 val; + + /* Consume the next RX FIFO entry */ + val = readl(drv_data->base + SSP_DATAR); + if (transfer->rx_buf) { + void *buf; + + buf = transfer->rx_buf + (transfer->len - drv_data->rx_resid); + + if (bytes == 1) + *(u8 *)buf = val; + else if (bytes == 2) + *(u16 *)buf = val; + else /* bytes == 4 */ + *(u32 *)buf = val; + } /* Otherwise null reader: discard the data */ + + drv_data->rx_resid -= bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_RNE is set */ +static bool k1_spi_read(struct k1_spi_driver_data *drv_data, u32 val) +{ + do { + unsigned int count = FIELD_GET(SSP_STATUS_RFL, val) + 1; + + /* Only read what we need */ + count = min(count, drv_data->rx_resid / drv_data->bytes); + do + k1_spi_read_word(drv_data); + while (--count); + + /* If there's no more to read, we're done */ + if (!drv_data->rx_resid) + return true; + + /* Check again in case more became available to read */ + val = readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_RNE) + writel(SSP_STATUS_RNE, drv_data->base + SSP_STATUS); + else + return false; + } while (true); +} + +static irqreturn_t k1_spi_ssp_isr(int irq, void *dev_id) +{ + struct k1_spi_driver_data *drv_data = dev_id; + u32 status; + u32 top_ctrl; + + /* Get status and clear pending interrupts */ + status = readl(drv_data->base + SSP_STATUS); + writel(status, drv_data->base + SSP_STATUS); + + /* If no actionable status bits are set, this is not our interrupt */ + if (!(status & (SSP_STATUS_ERROR | SSP_STATUS_TNF | SSP_STATUS_RNE))) + return IRQ_NONE; + + /* Check for any error conditions first */ + if (status & SSP_STATUS_ERROR) { + if (drv_data->transfer) + drv_data->transfer->error |= SPI_TRANS_FAIL_IO; + goto done; + } + + /* + * For SPI, bytes are transferred in both directions equally, and + * RX always follows TX. Start by writing if there is anything to + * write, then read. Once there's no more to read, we're done. + */ + if (drv_data->tx_resid && (status & SSP_STATUS_TNF)) { + /* If we finish writing, disable TX interrupts */ + if (k1_spi_write(drv_data, status)) + writel(SSP_INT_EN_RX | SSP_INT_EN_ERROR, + drv_data->base + SSP_INT_EN); + } + + /* We're not done unless we've read all that was requested */ + if (drv_data->rx_resid) { + /* Read more if the FIFO is not empty */ + if (status & SSP_STATUS_RNE) + if (k1_spi_read(drv_data, status)) + goto done; + + return IRQ_HANDLED; + } +done: + /* Disable the port */ + top_ctrl = readl(drv_data->base + SSP_TOP_CTRL); + top_ctrl &= ~TOP_SSE; + writel(top_ctrl, drv_data->base + SSP_TOP_CTRL); + + /* Disable all interrupts */ + writel(0, drv_data->base + SSP_INT_EN); + + if (drv_data->transfer) { + drv_data->transfer = NULL; + spi_finalize_current_transfer(drv_data->host); + } + + return IRQ_HANDLED; +} + +static int +k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev) +{ + struct spi_controller *host = drv_data->host; + struct dma_chan *chan; + + chan = dma_request_chan(dev, "tx"); + if (IS_ERR(chan)) + return PTR_ERR(chan); + host->dma_tx = chan; + + chan = dma_request_chan(dev, "rx"); + if (IS_ERR(chan)) { + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; + return PTR_ERR(chan); + } + host->dma_rx = chan; + + drv_data->dma_enabled = true; + + return 0; +} + +static void k1_spi_dma_cleanup(struct device *dev, void *res) +{ + struct k1_spi_driver_data **ptr = res; + struct k1_spi_driver_data *drv_data = *ptr; + struct spi_controller *host = drv_data->host; + + if (!drv_data->dma_enabled) + return; + + drv_data->dma_enabled = false; + + dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; +} + +static int +devm_k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev) +{ + struct k1_spi_driver_data **ptr; + int ret; + + if (!IS_ENABLED(CONFIG_MMP_PDMA)) { + dev_info(dev, "DMA not available; using PIO\n"); + return 0; + } + + ptr = devres_alloc(k1_spi_dma_cleanup, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = k1_spi_dma_setup(drv_data, dev); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = drv_data; + devres_add(dev, ptr); + + return 0; +} + +static int k1_spi_probe(struct platform_device *pdev) +{ + struct k1_spi_driver_data *drv_data; + struct device *dev = &pdev->dev; + struct reset_control *reset; + struct spi_controller *host; + struct resource *iores; + struct clk *clk_bus; + int ret; + + host = devm_spi_alloc_host(dev, sizeof(*drv_data)); + if (!host) + return -ENOMEM; + drv_data = spi_controller_get_devdata(host); + drv_data->host = host; + platform_set_drvdata(pdev, drv_data); + + ret = devm_k1_spi_dma_setup(drv_data, dev); + if (ret == -EPROBE_DEFER) + return ret; + if (ret) + dev_warn(dev, "DMA setup failed (%d), falling back to PIO\n", ret); + + drv_data->base = devm_platform_get_and_ioremap_resource(pdev, 0, + &iores); + if (IS_ERR(drv_data->base)) + return dev_err_probe(dev, PTR_ERR(drv_data->base), + "error mapping memory\n"); + drv_data->base_addr = iores->start; + + clk_bus = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk_bus)) + return dev_err_probe(dev, PTR_ERR(clk_bus), + "error getting/enabling bus clock\n"); + drv_data->bus_rate = clk_get_rate(clk_bus); + + drv_data->clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(drv_data->clk)) + return dev_err_probe(dev, PTR_ERR(drv_data->clk), + "error getting/enabling core clock\n"); + + reset = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), + "error getting/deasserting reset\n"); + + k1_spi_register_reset(drv_data, true); + + drv_data->irq = platform_get_irq(pdev, 0); + if (drv_data->irq < 0) + return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n"); + + ret = devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr, + IRQF_SHARED, dev_name(dev), drv_data); + if (ret < 0) + return dev_err_probe(dev, ret, "error requesting IRQ\n"); + + /* Initialize the host structure, then register it */ + host->dev.of_node = dev_of_node(dev); + host->dev.parent = dev; + host->num_chipselect = 1; + if (drv_data->dma_enabled) + host->dma_alignment = K1_SPI_DMA_ALIGNMENT; + host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + host->min_speed_hz = K1_SPI_MIN_SPEED_HZ; + host->max_speed_hz = K1_SPI_MAX_SPEED_HZ; + host->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + host->max_dma_len = K1_SPI_MAX_DMA_LEN; + + host->setup = k1_spi_setup; + host->cleanup = k1_spi_cleanup; + host->can_dma = k1_spi_can_dma; + host->prepare_message = k1_spi_prepare_message; + host->set_cs = k1_spi_set_cs; + host->transfer_one = k1_spi_transfer_one; + host->handle_err = k1_spi_handle_err; + + ret = devm_spi_register_controller(dev, host); + if (ret) + dev_err(dev, "error registering controller\n"); + + return ret; +} + +static const struct of_device_id k1_spi_dt_ids[] = { + { .compatible = "spacemit,k1-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, k1_spi_dt_ids); + +static struct platform_driver k1_spi_driver = { + .probe = k1_spi_probe, + .driver = { + .name = "k1-spi", + .of_match_table = k1_spi_dt_ids, + }, +}; +module_platform_driver(k1_spi_driver); + +MODULE_DESCRIPTION("SpacemiT K1 SPI controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index acebf9c2e795..29380938ef65 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -923,16 +923,14 @@ static int sprd_spi_probe(struct platform_device *pdev) int ret; pdev->id = of_alias_get_id(pdev->dev.of_node, "spi"); - sctlr = spi_alloc_host(&pdev->dev, sizeof(*ss)); + sctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*ss)); if (!sctlr) return -ENOMEM; ss = spi_controller_get_devdata(sctlr); ss->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(ss->base)) { - ret = PTR_ERR(ss->base); - goto free_controller; - } + if (IS_ERR(ss->base)) + return PTR_ERR(ss->base); ss->phy_base = res->start; ss->dev = &pdev->dev; @@ -949,15 +947,15 @@ static int sprd_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sctlr); ret = sprd_spi_clk_init(pdev, ss); if (ret) - goto free_controller; + return ret; ret = sprd_spi_irq_init(pdev, ss); if (ret) - goto free_controller; + return ret; ret = sprd_spi_dma_init(pdev, ss); if (ret) - goto free_controller; + return ret; ret = clk_prepare_enable(ss->clk); if (ret) @@ -993,8 +991,6 @@ disable_clk: release_dma: if (ss->dma.enable) sprd_spi_dma_release(ss); -free_controller: - spi_controller_put(sctlr); return ret; } @@ -1009,8 +1005,6 @@ static void sprd_spi_remove(struct platform_device *pdev) if (ret < 0) dev_err(ss->dev, "failed to resume SPI controller\n"); - spi_controller_get(sctlr); - spi_unregister_controller(sctlr); if (ret >= 0) { @@ -1020,8 +1014,6 @@ static void sprd_spi_remove(struct platform_device *pdev) } pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(sctlr); } static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev) diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 9c8099fe6e19..df61094fc444 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -279,7 +279,7 @@ static int spi_st_probe(struct platform_device *pdev) int irq, ret = 0; u32 var; - host = spi_alloc_host(&pdev->dev, sizeof(*spi_st)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi_st)); if (!host) return -ENOMEM; @@ -296,13 +296,12 @@ static int spi_st_probe(struct platform_device *pdev) spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); if (IS_ERR(spi_st->clk)) { dev_err(&pdev->dev, "Unable to request clock\n"); - ret = PTR_ERR(spi_st->clk); - goto put_host; + return PTR_ERR(spi_st->clk); } ret = clk_prepare_enable(spi_st->clk); if (ret) - goto put_host; + return ret; init_completion(&spi_st->done); @@ -361,8 +360,7 @@ rpm_disable: pm_runtime_disable(&pdev->dev); clk_disable: clk_disable_unprepare(spi_st->clk); -put_host: - spi_controller_put(host); + return ret; } @@ -371,16 +369,12 @@ static void spi_st_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct spi_st *spi_st = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); clk_disable_unprepare(spi_st->clk); - spi_controller_put(host); - pinctrl_pm_select_sleep_state(&pdev->dev); } diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 4461c6e24b9e..fa0e8e83cb1b 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -470,10 +470,9 @@ static int stm32_ospi_send(struct spi_device *spi, const struct spi_mem_op *op) u8 cs = spi->chip_select[ffs(spi->cs_index_mask) - 1]; cr = readl_relaxed(ospi->regs_base + OSPI_CR); - cr &= ~CR_CSSEL; - cr |= FIELD_PREP(CR_CSSEL, cs); - cr &= ~CR_FMODE_MASK; - cr |= FIELD_PREP(CR_FMODE_MASK, ospi->fmode); + FIELD_MODIFY(CR_CSSEL, &cr, cs); + + FIELD_MODIFY(CR_FMODE_MASK, &cr, ospi->fmode); writel_relaxed(cr, regs_base + OSPI_CR); if (op->data.nbytes) @@ -602,11 +601,11 @@ static int stm32_ospi_dirmap_create(struct spi_mem_dirmap_desc *desc) { struct stm32_ospi *ospi = spi_controller_get_devdata(desc->mem->spi->controller); - if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) + if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT) return -EOPNOTSUPP; /* Should never happen, as mm_base == null is an error probe exit condition */ - if (!ospi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) + if (!ospi->mm_base && desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN) return -EOPNOTSUPP; if (!ospi->mm_size) @@ -633,7 +632,7 @@ static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc, * spi_mem_op template with offs, len and *buf in order to get * all needed transfer information into struct spi_mem_op */ - memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op)); + memcpy(&op, desc->info.op_tmpl, sizeof(struct spi_mem_op)); dev_dbg(ospi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf); op.data.nbytes = len; diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index df1bbacec90a..b77daba6db83 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -374,9 +374,8 @@ static int stm32_qspi_send(struct spi_device *spi, const struct spi_mem_op *op) int timeout, err = 0, err_poll_status = 0; cr = readl_relaxed(qspi->io_base + QSPI_CR); - cr &= ~CR_PRESC_MASK & ~CR_FSEL; - cr |= FIELD_PREP(CR_PRESC_MASK, flash->presc); - cr |= FIELD_PREP(CR_FSEL, flash->cs); + FIELD_MODIFY(CR_PRESC_MASK, &cr, flash->presc); + FIELD_MODIFY(CR_FSEL, &cr, flash->cs); writel_relaxed(cr, qspi->io_base + QSPI_CR); if (op->data.nbytes) @@ -506,11 +505,11 @@ static int stm32_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) { struct stm32_qspi *qspi = spi_controller_get_devdata(desc->mem->spi->controller); - if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) + if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT) return -EOPNOTSUPP; /* should never happen, as mm_base == null is an error probe exit condition */ - if (!qspi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) + if (!qspi->mm_base && desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN) return -EOPNOTSUPP; if (!qspi->mm_size) @@ -536,7 +535,7 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, * spi_mem_op template with offs, len and *buf in order to get * all needed transfer information into struct spi_mem_op */ - memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op)); + memcpy(&op, desc->info.op_tmpl, sizeof(struct spi_mem_op)); dev_dbg(qspi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf); op.data.nbytes = len; diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index b7fbb5270edb..d5c16392cd4d 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -434,32 +434,26 @@ static int sun4i_spi_probe(struct platform_device *pdev) struct sun4i_spi *sspi; int ret = 0, irq; - host = spi_alloc_host(&pdev->dev, sizeof(struct sun4i_spi)); - if (!host) { - dev_err(&pdev->dev, "Unable to allocate SPI Host\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct sun4i_spi)); + if (!host) return -ENOMEM; - } platform_set_drvdata(pdev, host); sspi = spi_controller_get_devdata(host); sspi->base_addr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(sspi->base_addr)) { - ret = PTR_ERR(sspi->base_addr); - goto err_free_host; - } + if (IS_ERR(sspi->base_addr)) + return PTR_ERR(sspi->base_addr); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = -ENXIO; - goto err_free_host; - } + if (irq < 0) + return -ENXIO; ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, 0, "sun4i-spi", sspi); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); - goto err_free_host; + return ret; } sspi->host = host; @@ -477,15 +471,13 @@ static int sun4i_spi_probe(struct platform_device *pdev) sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); - ret = PTR_ERR(sspi->hclk); - goto err_free_host; + return PTR_ERR(sspi->hclk); } sspi->mclk = devm_clk_get(&pdev->dev, "mod"); if (IS_ERR(sspi->mclk)) { dev_err(&pdev->dev, "Unable to acquire module clock\n"); - ret = PTR_ERR(sspi->mclk); - goto err_free_host; + return PTR_ERR(sspi->mclk); } init_completion(&sspi->done); @@ -497,7 +489,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) ret = sun4i_spi_runtime_resume(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Couldn't resume the device\n"); - goto err_free_host; + return ret; } pm_runtime_set_active(&pdev->dev); @@ -515,8 +507,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(&pdev->dev); sun4i_spi_runtime_suspend(&pdev->dev); -err_free_host: - spi_controller_put(host); + return ret; } @@ -524,13 +515,9 @@ static void sun4i_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_force_suspend(&pdev->dev); - - spi_controller_put(host); } static const struct of_device_id sun4i_spi_match[] = { diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 5ac73d324d06..4631e9c7ca1d 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -633,7 +633,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) struct resource *mem; int ret = 0, irq; - host = spi_alloc_host(&pdev->dev, sizeof(struct sun6i_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct sun6i_spi)); if (!host) { dev_err(&pdev->dev, "Unable to allocate SPI Host\n"); return -ENOMEM; @@ -643,22 +643,18 @@ static int sun6i_spi_probe(struct platform_device *pdev) sspi = spi_controller_get_devdata(host); sspi->base_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); - if (IS_ERR(sspi->base_addr)) { - ret = PTR_ERR(sspi->base_addr); - goto err_free_host; - } + if (IS_ERR(sspi->base_addr)) + return PTR_ERR(sspi->base_addr); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = -ENXIO; - goto err_free_host; - } + if (irq < 0) + return -ENXIO; ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, 0, "sun6i-spi", sspi); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); - goto err_free_host; + return ret; } sspi->host = host; @@ -679,15 +675,13 @@ static int sun6i_spi_probe(struct platform_device *pdev) sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); - ret = PTR_ERR(sspi->hclk); - goto err_free_host; + return PTR_ERR(sspi->hclk); } sspi->mclk = devm_clk_get(&pdev->dev, "mod"); if (IS_ERR(sspi->mclk)) { dev_err(&pdev->dev, "Unable to acquire module clock\n"); - ret = PTR_ERR(sspi->mclk); - goto err_free_host; + return PTR_ERR(sspi->mclk); } init_completion(&sspi->done); @@ -696,17 +690,14 @@ static int sun6i_spi_probe(struct platform_device *pdev) sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) { dev_err(&pdev->dev, "Couldn't get reset controller\n"); - ret = PTR_ERR(sspi->rstc); - goto err_free_host; + return PTR_ERR(sspi->rstc); } host->dma_tx = dma_request_chan(&pdev->dev, "tx"); if (IS_ERR(host->dma_tx)) { /* Check tx to see if we need defer probing driver */ - if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto err_free_host; - } + if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) + return -EPROBE_DEFER; dev_warn(&pdev->dev, "Failed to request TX DMA channel\n"); host->dma_tx = NULL; } @@ -759,8 +750,7 @@ err_free_dma_rx: err_free_dma_tx: if (host->dma_tx) dma_release_channel(host->dma_tx); -err_free_host: - spi_controller_put(host); + return ret; } @@ -768,8 +758,6 @@ static void sun6i_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_force_suspend(&pdev->dev); @@ -778,8 +766,6 @@ static void sun6i_spi_remove(struct platform_device *pdev) dma_release_channel(host->dma_tx); if (host->dma_rx) dma_release_channel(host->dma_rx); - - spi_controller_put(host); } static const struct sun6i_spi_cfg sun6i_a31_spi_cfg = { diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c index 35601212fb78..c1870322d976 100644 --- a/drivers/spi/spi-sunplus-sp7021.c +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -290,8 +290,7 @@ static void sp7021_spi_setup_clk(struct spi_controller *ctlr, struct spi_transfe div = max(2U, clk_rate / xfer->speed_hz); clk_sel = (div / 2) - 1; - pspim->xfer_conf &= ~SP7021_CLK_MASK; - pspim->xfer_conf |= FIELD_PREP(SP7021_CLK_MASK, clk_sel); + FIELD_MODIFY(SP7021_CLK_MASK, &pspim->xfer_conf, clk_sel); writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG); } diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c index 290c439897c4..c14225e39fd1 100644 --- a/drivers/spi/spi-synquacer.c +++ b/drivers/spi/spi-synquacer.c @@ -605,7 +605,7 @@ static int synquacer_spi_probe(struct platform_device *pdev) int ret; int rx_irq, tx_irq; - host = spi_alloc_host(&pdev->dev, sizeof(*sspi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*sspi)); if (!host) return -ENOMEM; @@ -617,10 +617,8 @@ static int synquacer_spi_probe(struct platform_device *pdev) init_completion(&sspi->transfer_done); sspi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(sspi->regs)) { - ret = PTR_ERR(sspi->regs); - goto put_spi; - } + if (IS_ERR(sspi->regs)) + return PTR_ERR(sspi->regs); sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IHCLK; /* Default */ device_property_read_u32(&pdev->dev, "socionext,ihclk-rate", @@ -637,21 +635,19 @@ static int synquacer_spi_probe(struct platform_device *pdev) sspi->clk = devm_clk_get(sspi->dev, "iPCLK"); } else { dev_err(&pdev->dev, "specified wrong clock source\n"); - ret = -EINVAL; - goto put_spi; + return -EINVAL; } if (IS_ERR(sspi->clk)) { - ret = dev_err_probe(&pdev->dev, PTR_ERR(sspi->clk), - "clock not found\n"); - goto put_spi; + return dev_err_probe(&pdev->dev, PTR_ERR(sspi->clk), + "clock not found\n"); } ret = clk_prepare_enable(sspi->clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret); - goto put_spi; + return ret; } host->max_speed_hz = clk_get_rate(sspi->clk); @@ -726,8 +722,6 @@ disable_pm: pm_runtime_disable(sspi->dev); disable_clk: clk_disable_unprepare(sspi->clk); -put_spi: - spi_controller_put(host); return ret; } @@ -737,15 +731,11 @@ static void synquacer_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct synquacer_spi *sspi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(sspi->dev); clk_disable_unprepare(sspi->clk); - - spi_controller_put(host); } static int __maybe_unused synquacer_spi_suspend(struct device *dev) diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index b8b0ebe0fe93..aa44ffd09e61 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1302,7 +1302,7 @@ static int tegra_spi_probe(struct platform_device *pdev) int ret, spi_irq; int bus_num; - host = spi_alloc_host(&pdev->dev, sizeof(*tspi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*tspi)); if (!host) { dev_err(&pdev->dev, "host allocation failed\n"); return -ENOMEM; @@ -1336,36 +1336,31 @@ static int tegra_spi_probe(struct platform_device *pdev) tspi->soc_data = of_device_get_match_data(&pdev->dev); if (!tspi->soc_data) { dev_err(&pdev->dev, "unsupported tegra\n"); - ret = -ENODEV; - goto exit_free_host; + return -ENODEV; } tspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(tspi->base)) { - ret = PTR_ERR(tspi->base); - goto exit_free_host; - } + if (IS_ERR(tspi->base)) + return PTR_ERR(tspi->base); + tspi->phys = r->start; spi_irq = platform_get_irq(pdev, 0); - if (spi_irq < 0) { - ret = spi_irq; - goto exit_free_host; - } + if (spi_irq < 0) + return spi_irq; + tspi->irq = spi_irq; tspi->clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(tspi->clk)) { dev_err(&pdev->dev, "can not get clock\n"); - ret = PTR_ERR(tspi->clk); - goto exit_free_host; + return PTR_ERR(tspi->clk); } tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi"); if (IS_ERR(tspi->rst)) { dev_err(&pdev->dev, "can not get reset\n"); - ret = PTR_ERR(tspi->rst); - goto exit_free_host; + return PTR_ERR(tspi->rst); } tspi->max_buf_size = SPI_FIFO_DEPTH << 2; @@ -1373,7 +1368,7 @@ static int tegra_spi_probe(struct platform_device *pdev) ret = tegra_spi_init_dma_param(tspi, true); if (ret < 0) - goto exit_free_host; + return ret; ret = tegra_spi_init_dma_param(tspi, false); if (ret < 0) goto exit_rx_dma_free; @@ -1431,8 +1426,7 @@ exit_pm_disable: tegra_spi_deinit_dma_param(tspi, false); exit_rx_dma_free: tegra_spi_deinit_dma_param(tspi, true); -exit_free_host: - spi_controller_put(host); + return ret; } @@ -1441,8 +1435,6 @@ static void tegra_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct tegra_spi_data *tspi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); free_irq(tspi->irq, tspi); @@ -1456,8 +1448,6 @@ static void tegra_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) tegra_spi_runtime_suspend(&pdev->dev); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 9256729f2d49..2caa33f0a52c 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -427,11 +427,9 @@ static int tegra_sflash_probe(struct platform_device *pdev) return -ENODEV; } - host = spi_alloc_host(&pdev->dev, sizeof(*tsd)); - if (!host) { - dev_err(&pdev->dev, "host allocation failed\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*tsd)); + if (!host) return -ENOMEM; - } /* the spi->mode bits understood by this driver: */ host->mode_bits = SPI_CPOL | SPI_CPHA; @@ -450,14 +448,13 @@ static int tegra_sflash_probe(struct platform_device *pdev) host->max_speed_hz = 25000000; /* 25MHz */ tsd->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(tsd->base)) { - ret = PTR_ERR(tsd->base); - goto exit_free_host; - } + if (IS_ERR(tsd->base)) + return PTR_ERR(tsd->base); ret = platform_get_irq(pdev, 0); if (ret < 0) - goto exit_free_host; + return ret; + tsd->irq = ret; ret = request_irq(tsd->irq, tegra_sflash_isr, 0, @@ -465,7 +462,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", tsd->irq); - goto exit_free_host; + return ret; } tsd->clk = devm_clk_get(&pdev->dev, NULL); @@ -518,8 +515,7 @@ exit_pm_disable: tegra_sflash_runtime_suspend(&pdev->dev); exit_free_irq: free_irq(tsd->irq, tsd); -exit_free_host: - spi_controller_put(host); + return ret; } @@ -528,8 +524,6 @@ static void tegra_sflash_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct tegra_sflash_data *tsd = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); free_irq(tsd->irq, tsd); @@ -537,8 +531,6 @@ static void tegra_sflash_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) tegra_sflash_runtime_suspend(&pdev->dev); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index db28dd556484..588a929a9785 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -226,11 +226,13 @@ struct tegra_qspi { struct completion xfer_completion; struct spi_transfer *curr_xfer; + struct device *rx_dma_dev; struct dma_chan *rx_dma_chan; u32 *rx_dma_buf; dma_addr_t rx_dma_phys; struct dma_async_tx_descriptor *rx_dma_desc; + struct device *tx_dma_dev; struct dma_chan *tx_dma_chan; u32 *tx_dma_buf; dma_addr_t tx_dma_phys; @@ -574,15 +576,15 @@ static int tegra_qspi_dma_map_xfer(struct tegra_qspi *tqspi, struct spi_transfer len = DIV_ROUND_UP(tqspi->curr_dma_words * tqspi->bytes_per_word, 4) * 4; if (t->tx_buf) { - t->tx_dma = dma_map_single(tqspi->dev, (void *)tx_buf, len, DMA_TO_DEVICE); - if (dma_mapping_error(tqspi->dev, t->tx_dma)) + t->tx_dma = dma_map_single(tqspi->tx_dma_dev, (void *)tx_buf, len, DMA_TO_DEVICE); + if (dma_mapping_error(tqspi->tx_dma_dev, t->tx_dma)) return -ENOMEM; } if (t->rx_buf) { - t->rx_dma = dma_map_single(tqspi->dev, (void *)rx_buf, len, DMA_FROM_DEVICE); - if (dma_mapping_error(tqspi->dev, t->rx_dma)) { - dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); + t->rx_dma = dma_map_single(tqspi->rx_dma_dev, (void *)rx_buf, len, DMA_FROM_DEVICE); + if (dma_mapping_error(tqspi->rx_dma_dev, t->rx_dma)) { + dma_unmap_single(tqspi->tx_dma_dev, t->tx_dma, len, DMA_TO_DEVICE); return -ENOMEM; } } @@ -597,9 +599,9 @@ static void tegra_qspi_dma_unmap_xfer(struct tegra_qspi *tqspi, struct spi_trans len = DIV_ROUND_UP(tqspi->curr_dma_words * tqspi->bytes_per_word, 4) * 4; if (t->tx_buf) - dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); + dma_unmap_single(tqspi->tx_dma_dev, t->tx_dma, len, DMA_TO_DEVICE); if (t->rx_buf) - dma_unmap_single(tqspi->dev, t->rx_dma, len, DMA_FROM_DEVICE); + dma_unmap_single(tqspi->rx_dma_dev, t->rx_dma, len, DMA_FROM_DEVICE); } static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct spi_transfer *t) @@ -745,7 +747,7 @@ static int tegra_qspi_start_cpu_based_transfer(struct tegra_qspi *qspi, struct s static void tegra_qspi_deinit_dma(struct tegra_qspi *tqspi) { if (tqspi->tx_dma_buf) { - dma_free_coherent(tqspi->dev, tqspi->dma_buf_size, + dma_free_coherent(tqspi->tx_dma_dev, tqspi->dma_buf_size, tqspi->tx_dma_buf, tqspi->tx_dma_phys); tqspi->tx_dma_buf = NULL; } @@ -756,7 +758,7 @@ static void tegra_qspi_deinit_dma(struct tegra_qspi *tqspi) } if (tqspi->rx_dma_buf) { - dma_free_coherent(tqspi->dev, tqspi->dma_buf_size, + dma_free_coherent(tqspi->rx_dma_dev, tqspi->dma_buf_size, tqspi->rx_dma_buf, tqspi->rx_dma_phys); tqspi->rx_dma_buf = NULL; } @@ -782,6 +784,7 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) } tqspi->rx_dma_chan = dma_chan; + tqspi->rx_dma_dev = dmaengine_get_dma_device(tqspi->rx_dma_chan); dma_chan = dma_request_chan(tqspi->dev, "tx"); if (IS_ERR(dma_chan)) { @@ -790,15 +793,19 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) } tqspi->tx_dma_chan = dma_chan; + tqspi->tx_dma_dev = dmaengine_get_dma_device(tqspi->tx_dma_chan); } else { if (!device_iommu_mapped(tqspi->dev)) { dev_warn(tqspi->dev, "IOMMU not enabled in device-tree, falling back to PIO mode\n"); return 0; } + + tqspi->rx_dma_dev = tqspi->dev; + tqspi->tx_dma_dev = tqspi->dev; } - dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); + dma_buf = dma_alloc_coherent(tqspi->rx_dma_dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { err = -ENOMEM; goto err_out; @@ -807,7 +814,7 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) tqspi->rx_dma_buf = dma_buf; tqspi->rx_dma_phys = dma_phys; - dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); + dma_buf = dma_alloc_coherent(tqspi->tx_dma_dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { err = -ENOMEM; goto err_out; diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index e3b413b9828c..34b154922ff2 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -765,7 +765,7 @@ static int ti_qspi_probe(struct platform_device *pdev) int ret = 0, num_cs, irq; dma_cap_mask_t mask; - host = spi_alloc_host(&pdev->dev, sizeof(*qspi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*qspi)); if (!host) return -ENOMEM; @@ -793,8 +793,7 @@ static int ti_qspi_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "missing platform data\n"); - ret = -ENODEV; - goto free_host; + return -ENODEV; } } @@ -812,28 +811,22 @@ static int ti_qspi_probe(struct platform_device *pdev) qspi->mmap_size = resource_size(res_mmap); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto free_host; - } + if (irq < 0) + return irq; mutex_init(&qspi->list_lock); qspi->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(qspi->base)) { - ret = PTR_ERR(qspi->base); - goto free_host; - } + if (IS_ERR(qspi->base)) + return PTR_ERR(qspi->base); if (of_property_present(np, "syscon-chipselects")) { qspi->ctrl_base = syscon_regmap_lookup_by_phandle_args(np, "syscon-chipselects", 1, &qspi->ctrl_reg); - if (IS_ERR(qspi->ctrl_base)) { - ret = PTR_ERR(qspi->ctrl_base); - goto free_host; - } + if (IS_ERR(qspi->ctrl_base)) + return PTR_ERR(qspi->ctrl_base); } qspi->fclk = devm_clk_get(&pdev->dev, "fck"); @@ -890,14 +883,16 @@ no_dma: qspi->current_cs = -1; ret = spi_register_controller(host); - if (!ret) - return 0; + if (ret) + goto err_free_dma; + return 0; + +err_free_dma: ti_qspi_dma_cleanup(qspi); pm_runtime_disable(&pdev->dev); -free_host: - spi_controller_put(host); + return ret; } @@ -905,16 +900,12 @@ static void ti_qspi_remove(struct platform_device *pdev) { struct ti_qspi *qspi = platform_get_drvdata(pdev); - spi_controller_get(qspi->host); - spi_unregister_controller(qspi->host); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); ti_qspi_dma_cleanup(qspi); - - spi_controller_put(qspi->host); } static const struct dev_pm_ops ti_qspi_pm_ops = { diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 14d11450e86d..c5409ca7faef 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -207,10 +207,10 @@ struct pch_pd_dev_save { }; static const struct pci_device_id pch_spi_pcidev_id[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_SPI), 1, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), .driver_data = 2 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_SPI), .driver_data = 1 }, { } }; @@ -1406,8 +1406,6 @@ static void pch_spi_pd_remove(struct platform_device *plat_dev) dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n", __func__, plat_dev->id, board_dat->pdev->irq); - spi_controller_get(data->host); - spi_unregister_controller(data->host); /* check for any pending messages; no action is taken if the queue diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index eac6c3e8908b..cc20fd11f03f 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -184,14 +184,12 @@ static void uniphier_spi_set_transfer_size(struct spi_device *spi, int size) u32 val; val = readl(priv->base + SSI_TXWDS); - val &= ~(SSI_TXWDS_WDLEN_MASK | SSI_TXWDS_DTLEN_MASK); - val |= FIELD_PREP(SSI_TXWDS_WDLEN_MASK, size); - val |= FIELD_PREP(SSI_TXWDS_DTLEN_MASK, size); + FIELD_MODIFY(SSI_TXWDS_WDLEN_MASK, &val, size); + FIELD_MODIFY(SSI_TXWDS_DTLEN_MASK, &val, size); writel(val, priv->base + SSI_TXWDS); val = readl(priv->base + SSI_RXWDS); - val &= ~SSI_RXWDS_DTLEN_MASK; - val |= FIELD_PREP(SSI_RXWDS_DTLEN_MASK, size); + FIELD_MODIFY(SSI_RXWDS_DTLEN_MASK, &val, size); writel(val, priv->base + SSI_RXWDS); } @@ -308,9 +306,8 @@ static void uniphier_spi_set_fifo_threshold(struct uniphier_spi_priv *priv, u32 val; val = readl(priv->base + SSI_FC); - val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); - val |= FIELD_PREP(SSI_FC_TXFTH_MASK, SSI_FIFO_DEPTH - threshold); - val |= FIELD_PREP(SSI_FC_RXFTH_MASK, threshold); + FIELD_MODIFY(SSI_FC_TXFTH_MASK, &val, SSI_FIFO_DEPTH - threshold); + FIELD_MODIFY(SSI_FC_RXFTH_MASK, &val, threshold); writel(val, priv->base + SSI_FC); } @@ -649,7 +646,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) int irq; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*priv)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*priv)); if (!host) return -ENOMEM; @@ -660,30 +657,26 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->is_save_param = false; priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(priv->base)) { - ret = PTR_ERR(priv->base); - goto out_host_put; - } + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + priv->base_dma_addr = res->start; priv->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(priv->clk); - goto out_host_put; + return PTR_ERR(priv->clk); } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto out_host_put; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, uniphier_spi_handler, 0, "uniphier-spi", priv); if (ret) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto out_host_put; + return ret; } init_completion(&priv->xfer_done); @@ -710,10 +703,9 @@ static int uniphier_spi_probe(struct platform_device *pdev) host->dma_tx = dma_request_chan(&pdev->dev, "tx"); if (IS_ERR_OR_NULL(host->dma_tx)) { - if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto out_host_put; - } + if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) + return -EPROBE_DEFER; + host->dma_tx = NULL; dma_tx_burst = INT_MAX; } else { @@ -762,8 +754,6 @@ out_release_dma: host->dma_tx = NULL; } -out_host_put: - spi_controller_put(host); return ret; } @@ -771,16 +761,12 @@ static void uniphier_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); - spi_controller_get(host); - spi_unregister_controller(host); if (host->dma_tx) dma_release_channel(host->dma_tx); if (host->dma_rx) dma_release_channel(host->dma_rx); - - spi_controller_put(host); } static const struct of_device_id uniphier_spi_match[] = { diff --git a/drivers/spi/spi-wpcm-fiu.c b/drivers/spi/spi-wpcm-fiu.c index 0e26ff178505..cd78e927953d 100644 --- a/drivers/spi/spi-wpcm-fiu.c +++ b/drivers/spi/spi-wpcm-fiu.c @@ -377,7 +377,7 @@ static int wpcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(desc->mem->spi->controller); int cs = spi_get_chipselect(desc->mem->spi, 0); - if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN) return -EOPNOTSUPP; /* diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 130a3d716dd4..e40ec6ebe4e5 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -269,8 +269,8 @@ static int spi_xcomm_probe(struct i2c_client *i2c) } static const struct i2c_device_id spi_xcomm_ids[] = { - { "spi-xcomm" }, - { }, + { .name = "spi-xcomm" }, + { } }; MODULE_DEVICE_TABLE(i2c, spi_xcomm_ids); diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 9f065d4e27d1..dc1e968659e1 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -285,7 +285,11 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) if (use_irq) { xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); - wait_for_completion(&xspi->done); + if (!wait_for_completion_timeout(&xspi->done, secs_to_jiffies(1))) { + dev_err(&spi->dev, "SPI transfer timed out\n"); + xspi_init_hw(xspi); + return -ETIMEDOUT; + } /* A transmit has just completed. Process received data * and check for more data to transmit. Always inhibit * the transmitter while the Isr refills the transmit @@ -371,11 +375,18 @@ static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi) xspi->regs + XIPIF_V123B_RESETR_OFFSET); /* Fill the Tx FIFO with as many words as possible */ - do { + while (1) { xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); + if (sr & XSPI_SR_TX_FULL_MASK) + break; + n_words++; - } while (!(sr & XSPI_SR_TX_FULL_MASK)); + } + + /* Handle the NO FIFO case separately */ + if (!n_words) + return 1; return n_words; } @@ -515,8 +526,6 @@ static void xilinx_spi_remove(struct platform_device *pdev) xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET); /* Disable the global IPIF interrupt */ xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET); - - spi_controller_put(xspi->bitbang.ctlr); } /* work with hotplug and coldplug */ diff --git a/drivers/spi/spi-xlp.c b/drivers/spi/spi-xlp.c index be8bbe1cbba3..d014955e6d4b 100644 --- a/drivers/spi/spi-xlp.c +++ b/drivers/spi/spi-xlp.c @@ -398,7 +398,7 @@ static int xlp_spi_probe(struct platform_device *pdev) xspi->spi_clk = clk_get_rate(clk); - host = spi_alloc_host(&pdev->dev, 0); + host = devm_spi_alloc_host(&pdev->dev, 0); if (!host) { dev_err(&pdev->dev, "could not alloc host\n"); return -ENOMEM; @@ -418,7 +418,6 @@ static int xlp_spi_probe(struct platform_device *pdev) err = devm_spi_register_controller(&pdev->dev, host); if (err) { dev_err(&pdev->dev, "spi register host failed!\n"); - spi_controller_put(host); return err; } diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c index 71f0f176cfd9..d47a1e548fce 100644 --- a/drivers/spi/spi-xtensa-xtfpga.c +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -122,7 +122,6 @@ static void xtfpga_spi_remove(struct platform_device *pdev) struct xtfpga_spi *xspi = spi_controller_get_devdata(host); spi_bitbang_stop(&xspi->bitbang); - spi_controller_put(host); } MODULE_ALIAS("platform:" XTFPGA_SPI_NAME); diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c index 406fd9d5337e..d762aaf452af 100644 --- a/drivers/spi/spi-zynq-qspi.c +++ b/drivers/spi/spi-zynq-qspi.c @@ -637,7 +637,7 @@ static int zynq_qspi_probe(struct platform_device *pdev) struct zynq_qspi *xqspi; u32 num_cs; - ctlr = spi_alloc_host(&pdev->dev, sizeof(*xqspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xqspi)); if (!ctlr) return -ENOMEM; @@ -645,16 +645,13 @@ static int zynq_qspi_probe(struct platform_device *pdev) xqspi->dev = dev; platform_set_drvdata(pdev, ctlr); xqspi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(xqspi->regs)) { - ret = PTR_ERR(xqspi->regs); - goto remove_ctlr; - } + if (IS_ERR(xqspi->regs)) + return PTR_ERR(xqspi->regs); xqspi->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(xqspi->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); - ret = PTR_ERR(xqspi->pclk); - goto remove_ctlr; + return PTR_ERR(xqspi->pclk); } init_completion(&xqspi->data_completion); @@ -662,21 +659,18 @@ static int zynq_qspi_probe(struct platform_device *pdev) xqspi->refclk = devm_clk_get_enabled(&pdev->dev, "ref_clk"); if (IS_ERR(xqspi->refclk)) { dev_err(&pdev->dev, "ref_clk clock not found.\n"); - ret = PTR_ERR(xqspi->refclk); - goto remove_ctlr; + return PTR_ERR(xqspi->refclk); } xqspi->irq = platform_get_irq(pdev, 0); - if (xqspi->irq < 0) { - ret = xqspi->irq; - goto remove_ctlr; - } + if (xqspi->irq < 0) + return xqspi->irq; + ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq, 0, pdev->name, xqspi); if (ret != 0) { - ret = -ENXIO; dev_err(&pdev->dev, "request_irq failed\n"); - goto remove_ctlr; + return -ENXIO; } ret = of_property_read_u32(np, "num-cs", @@ -684,9 +678,8 @@ static int zynq_qspi_probe(struct platform_device *pdev) if (ret < 0) { ctlr->num_chipselect = 1; } else if (num_cs > ZYNQ_QSPI_MAX_NUM_CS) { - ret = -EINVAL; dev_err(&pdev->dev, "only 2 chip selects are available\n"); - goto remove_ctlr; + return -EINVAL; } else { ctlr->num_chipselect = num_cs; } @@ -705,15 +698,10 @@ static int zynq_qspi_probe(struct platform_device *pdev) ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "failed to register controller\n"); - goto remove_ctlr; + return ret; } - return ret; - -remove_ctlr: - spi_controller_put(ctlr); - - return ret; + return 0; } /** @@ -731,13 +719,9 @@ static void zynq_qspi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct zynq_qspi *xqspi = spi_controller_get_devdata(ctlr); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0); - - spi_controller_put(ctlr); } static const struct of_device_id zynq_qspi_of_match[] = { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 104279858f56..76e3563c523f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -732,8 +732,6 @@ static int __spi_add_device(struct spi_device *spi, struct spi_device *parent) } if (ctlr->cs_gpiods) { - u8 cs; - for (idx = 0; idx < spi->num_chipselect; idx++) { cs = spi_get_chipselect(spi, idx); spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]); @@ -3226,9 +3224,9 @@ extern struct class spi_target_class; /* dummy */ * This must be called from context that can sleep. * * The caller is responsible for assigning the bus number and initializing the - * controller's methods before calling spi_register_controller(); and (after - * errors adding the device) calling spi_controller_put() to prevent a memory - * leak. + * controller's methods before calling spi_register_controller(); and calling + * spi_controller_put() to prevent a memory leak when done with the + * controller. * * Return: the SPI controller structure on success, else NULL. */ @@ -3312,8 +3310,6 @@ struct spi_controller *__devm_spi_alloc_controller(struct device *dev, if (ret) return NULL; - ctlr->devm_allocated = true; - return ctlr; } EXPORT_SYMBOL_GPL(__devm_spi_alloc_controller); @@ -3552,7 +3548,8 @@ int spi_register_controller(struct spi_controller *ctlr) /* Register devices from the device tree and ACPI */ of_register_spi_devices(ctlr); acpi_register_spi_devices(ctlr); - return status; + + return 0; del_ctrl: device_del(&ctlr->dev); @@ -3560,6 +3557,7 @@ free_bus_id: mutex_lock(&board_lock); idr_remove(&spi_controller_idr, ctlr->bus_num); mutex_unlock(&board_lock); + return status; } EXPORT_SYMBOL_GPL(spi_register_controller); @@ -3577,8 +3575,7 @@ static void devm_spi_unregister_controller(void *ctlr) * Context: can sleep * * Register a SPI device as with spi_register_controller() which will - * automatically be unregistered (and freed unless it has been allocated using - * devm_spi_alloc_host/target()). + * automatically be unregistered. * * Return: zero on success, else a negative error code. */ @@ -3591,19 +3588,7 @@ int devm_spi_register_controller(struct device *dev, if (ret) return ret; - /* - * Prevent controller from being freed by spi_unregister_controller() - * if devm_add_action_or_reset() fails for a non-devres allocated - * controller. - */ - spi_controller_get(ctlr); - - ret = devm_add_action_or_reset(dev, devm_spi_unregister_controller, ctlr); - - if (ret == 0 || ctlr->devm_allocated) - spi_controller_put(ctlr); - - return ret; + return devm_add_action_or_reset(dev, devm_spi_unregister_controller, ctlr); } EXPORT_SYMBOL_GPL(devm_spi_register_controller); @@ -3622,9 +3607,6 @@ static int __unregister(struct device *dev, void *null) * only ones directly touching chip registers. * * This must be called from context that can sleep. - * - * Note that this function also drops a reference to the controller unless it - * has been allocated using devm_spi_alloc_host/target(). */ void spi_unregister_controller(struct spi_controller *ctlr) { @@ -3659,13 +3641,6 @@ void spi_unregister_controller(struct spi_controller *ctlr) if (IS_ENABLED(CONFIG_SPI_DYNAMIC)) mutex_unlock(&ctlr->add_lock); - - /* - * Release the last reference on the controller if its driver - * has not yet been converted to devm_spi_alloc_host/target(). - */ - if (!ctlr->devm_allocated) - put_device(&ctlr->dev); } EXPORT_SYMBOL_GPL(spi_unregister_controller); @@ -5003,11 +4978,6 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); spi = of_register_spi_device(ctlr, rd->dn); put_device(&ctlr->dev); diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index 24e9c909fa02..e4d1ae8308aa 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -547,13 +547,10 @@ int gb_spilib_master_init(struct gb_connection *connection, struct device *dev, return 0; -exit_spi_put: - spi_controller_put(ctlr); - - return ret; - exit_spi_unregister: spi_unregister_controller(ctlr); +exit_spi_put: + spi_controller_put(ctlr); return ret; } @@ -564,6 +561,7 @@ void gb_spilib_master_exit(struct gb_connection *connection) struct spi_controller *ctlr = gb_connection_get_data(connection); spi_unregister_controller(ctlr); + spi_controller_put(ctlr); } EXPORT_SYMBOL_GPL(gb_spilib_master_exit); diff --git a/drivers/staging/media/max96712/Kconfig b/drivers/staging/media/max96712/Kconfig index 117fadf81bd0..93a2d583e90d 100644 --- a/drivers/staging/media/max96712/Kconfig +++ b/drivers/staging/media/max96712/Kconfig @@ -2,7 +2,6 @@ config VIDEO_MAX96712 tristate "Maxim MAX96712 Quad GMSL2 Deserializer support" depends on I2C - depends on OF_GPIO depends on VIDEO_DEV select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index ddfc56f0253d..9f21a2226dbd 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -464,8 +464,11 @@ static void update_current_network(struct adapter *adapter, struct wlan_bssid_ex if (check_fwstate(pmlmepriv, _FW_LINKED) && (is_same_network(&pmlmepriv->cur_network.network, pnetwork, 0))) { update_network(&pmlmepriv->cur_network.network, pnetwork, adapter, true); + if (pmlmepriv->cur_network.network.ie_length < sizeof(struct ndis_802_11_fix_ie)) + return; + rtw_update_protection(adapter, (pmlmepriv->cur_network.network.ies) + sizeof(struct ndis_802_11_fix_ie), - pmlmepriv->cur_network.network.ie_length); + pmlmepriv->cur_network.network.ie_length - sizeof(struct ndis_802_11_fix_ie)); } } @@ -601,6 +604,8 @@ static bool rtw_is_desired_network(struct adapter *adapter, struct wlan_network privacy = pnetwork->network.privacy; if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) { + if (pnetwork->network.ie_length < _FIXED_IE_LENGTH_) + return false; if (rtw_get_wps_ie(pnetwork->network.ies + _FIXED_IE_LENGTH_, pnetwork->network.ie_length - _FIXED_IE_LENGTH_, NULL, &wps_ielen)) return true; else @@ -614,11 +619,15 @@ static bool rtw_is_desired_network(struct adapter *adapter, struct wlan_network bselected = false; if (psecuritypriv->ndisauthtype == Ndis802_11AuthModeWPA2PSK) { - p = rtw_get_ie(pnetwork->network.ies + _BEACON_IE_OFFSET_, WLAN_EID_RSN, &ie_len, (pnetwork->network.ie_length - _BEACON_IE_OFFSET_)); - if (p && ie_len > 0) - bselected = true; - else + if (pnetwork->network.ie_length < _BEACON_IE_OFFSET_) { bselected = false; + } else { + p = rtw_get_ie(pnetwork->network.ies + _BEACON_IE_OFFSET_, WLAN_EID_RSN, &ie_len, (pnetwork->network.ie_length - _BEACON_IE_OFFSET_)); + if (p && ie_len > 0) + bselected = true; + else + bselected = false; + } } } @@ -1072,8 +1081,11 @@ static void rtw_joinbss_update_network(struct adapter *padapter, struct wlan_net break; } + if (cur_network->network.ie_length < sizeof(struct ndis_802_11_fix_ie)) + return; + rtw_update_protection(padapter, (cur_network->network.ies) + sizeof(struct ndis_802_11_fix_ie), - (cur_network->network.ie_length)); + (cur_network->network.ie_length - sizeof(struct ndis_802_11_fix_ie))); rtw_update_ht_cap(padapter, cur_network->network.ies, cur_network->network.ie_length, (u8) cur_network->network.configuration.ds_config); } diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index cb832fd523af..62ada3a52210 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -2295,7 +2295,9 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, goto reject; if (conn->conn_ops->DataDigest) { - data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); + data_crc = iscsit_crc_buf(text_in, + ALIGN(payload_length, 4), + 0, NULL); if (checksum != data_crc) { pr_err("Text data CRC32C DataDigest" " 0x%08x does not match computed" @@ -2314,6 +2316,7 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, " Command CmdSN: 0x%08x due to" " DataCRC error.\n", hdr->cmdsn); kfree(text_in); + cmd->text_in_ptr = NULL; return 0; } } else { diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index c46c69a28e97..a3ad2d244dbe 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -340,13 +340,22 @@ static int chap_server_compute_hash( goto out; } break; - case BASE64: + case BASE64: { + size_t r_len = strlen(chap_r); + + while (r_len > 0 && chap_r[r_len - 1] == '=') + r_len--; + if (r_len > DIV_ROUND_UP(chap->digest_size * 4, 3)) { + pr_err("Malformed CHAP_R: base64 payload too long\n"); + goto out; + } if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) != chap->digest_size) { pr_err("Malformed CHAP_R: invalid BASE64\n"); goto out; } break; + } default: pr_err("Could not find CHAP_R\n"); goto out; @@ -473,6 +482,14 @@ static int chap_server_compute_hash( } break; case BASE64: + /* + * No overflow check needed: initiatorchg_binhex is + * CHAP_CHALLENGE_STR_LEN bytes and extract_param() caps + * initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so + * the decoded output is at most DIV_ROUND_UP( + * (CHAP_CHALLENGE_STR_LEN - 1) * 3, 4) bytes, which is + * less than CHAP_CHALLENGE_STR_LEN. + */ initiatorchg_len = chap_base64_decode(initiatorchg_binhex, initiatorchg, strlen(initiatorchg)); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 832588f21f91..b03ed154ca34 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero( SENDER_TARGET, login->rsp_buf, &login->rsp_length, + MAX_KEY_VALUE_PAIRS, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); - if (ret < 0) + if (ret < 0) { + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, + ISCSI_LOGIN_STATUS_INIT_ERR); return -1; + } if (!iscsi_check_negotiated_keys(conn->param_list)) { bool auth_required = iscsi_conn_auth_required(conn); @@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo SENDER_TARGET, login->rsp_buf, &login->rsp_length, + MAX_KEY_VALUE_PAIRS, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); if (ret < 0) { diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index 4ed578c7b98d..2b318b13268e 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1371,19 +1371,42 @@ free_buffer: return -1; } +/* + * Append "key=value" plus a trailing NUL into @textbuf at *@length. + * Returns 0 on success and advances *@length, or -EMSGSIZE if the + * record (including the NUL) would not fit in the remaining buffer. + */ +static int iscsi_encode_text_record(char *textbuf, u32 *length, + u32 textbuf_size, + const char *key, const char *value) +{ + int n; + u32 avail; + + if (*length >= textbuf_size) + return -EMSGSIZE; + + avail = textbuf_size - *length; + n = snprintf(textbuf + *length, avail, "%s=%s", key, value); + if (n < 0 || (u32)n + 1 > avail) + return -EMSGSIZE; + + *length += n + 1; + return 0; +} + int iscsi_encode_text_output( u8 phase, u8 sender, char *textbuf, u32 *length, + u32 textbuf_size, struct iscsi_param_list *param_list, bool keys_workaround) { - char *output_buf = NULL; struct iscsi_extra_response *er; struct iscsi_param *param; - - output_buf = textbuf + *length; + int ret; if (iscsi_enforce_integrity_rules(phase, param_list) < 0) return -1; @@ -1395,10 +1418,12 @@ int iscsi_encode_text_output( !IS_PSTATE_RESPONSE_SENT(param) && !IS_PSTATE_REPLY_OPTIONAL(param) && (param->phase & phase)) { - *length += sprintf(output_buf, "%s=%s", - param->name, param->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, + textbuf_size, + param->name, + param->value); + if (ret < 0) + goto err_overflow; SET_PSTATE_RESPONSE_SENT(param); pr_debug("Sending key: %s=%s\n", param->name, param->value); @@ -1408,10 +1433,12 @@ int iscsi_encode_text_output( !IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param) && (param->phase & phase)) { - *length += sprintf(output_buf, "%s=%s", - param->name, param->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, + textbuf_size, + param->name, + param->value); + if (ret < 0) + goto err_overflow; SET_PSTATE_PROPOSER(param); iscsi_check_proposer_for_optional_reply(param, keys_workaround); @@ -1421,14 +1448,21 @@ int iscsi_encode_text_output( } list_for_each_entry(er, ¶m_list->extra_response_list, er_list) { - *length += sprintf(output_buf, "%s=%s", er->key, er->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, textbuf_size, + er->key, er->value); + if (ret < 0) + goto err_overflow; pr_debug("Sending key: %s=%s\n", er->key, er->value); } iscsi_release_extra_responses(param_list); return 0; + +err_overflow: + pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n", + textbuf_size); + iscsi_release_extra_responses(param_list); + return -1; } int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list) diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h index c672a971fcb7..38d2238dfe08 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.h +++ b/drivers/target/iscsi/iscsi_target_parameters.h @@ -43,7 +43,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_ extern int iscsi_extract_key_value(char *, char **, char **); extern int iscsi_update_param_value(struct iscsi_param *, char *); extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *); -extern int iscsi_encode_text_output(u8, u8, char *, u32 *, +extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32, struct iscsi_param_list *, bool); extern int iscsi_check_negotiated_keys(struct iscsi_param_list *); extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *, diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index a3d11b1f90fa..06747e90c230 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c @@ -10,7 +10,11 @@ struct optee_supp_req { struct list_head link; + int id; + bool in_queue; + bool processed; + u32 func; u32 ret; size_t num_params; @@ -19,6 +23,9 @@ struct optee_supp_req { struct completion c; }; +/* It is temporary request used for revoked pending request in supp->idr. */ +#define INVALID_REQ_PTR ((struct optee_supp_req *)ERR_PTR(-EBADF)) + void optee_supp_init(struct optee_supp *supp) { memset(supp, 0, sizeof(*supp)); @@ -39,21 +46,23 @@ void optee_supp_release(struct optee_supp *supp) { int id; struct optee_supp_req *req; - struct optee_supp_req *req_tmp; mutex_lock(&supp->mutex); - /* Abort all request retrieved by supplicant */ + /* Abort all request */ idr_for_each_entry(&supp->idr, req, id) { idr_remove(&supp->idr, id); - req->ret = TEEC_ERROR_COMMUNICATION; - complete(&req->c); - } + /* Skip if request was already marked invalid */ + if (IS_ERR(req)) + continue; - /* Abort all queued requests */ - list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { - list_del(&req->link); - req->in_queue = false; + /* For queued requests where supplicant has not seen it */ + if (req->in_queue) { + list_del(&req->link); + req->in_queue = false; + } + + req->processed = true; req->ret = TEEC_ERROR_COMMUNICATION; complete(&req->c); } @@ -100,8 +109,16 @@ 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); + req->id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); + if (req->id < 0) { + mutex_unlock(&supp->mutex); + kfree(req); + return TEEC_ERROR_OUT_OF_MEMORY; + } + list_add_tail(&req->link, &supp->reqs); req->in_queue = true; + req->processed = false; mutex_unlock(&supp->mutex); /* Tell an eventual waiter there's a new request */ @@ -117,21 +134,43 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, if (wait_for_completion_killable(&req->c)) { mutex_lock(&supp->mutex); if (req->in_queue) { + /* Supplicant has not seen this request yet. */ + idr_remove(&supp->idr, req->id); list_del(&req->link); req->in_queue = false; + + ret = TEEC_ERROR_COMMUNICATION; + } else if (req->processed) { + /* + * Supplicant has processed this request. Ignore the + * kill signal for now and submit the result. req is not + * in supp->reqs (removed by supp_pop_entry()) nor in + * supp->idr (removed by supp_pop_req()). + */ + ret = req->ret; + } else { + /* + * Supplicant is in the middle of processing this + * request. Replace req with INVALID_REQ_PTR so that + * the ID remains busy, causing optee_supp_send() to + * fail on the next call to supp_pop_req() with this ID. + */ + idr_replace(&supp->idr, INVALID_REQ_PTR, req->id); + ret = TEEC_ERROR_COMMUNICATION; } + mutex_unlock(&supp->mutex); - req->ret = TEEC_ERROR_COMMUNICATION; + } else { + ret = req->ret; } - ret = req->ret; kfree(req); return ret; } static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, - int num_params, int *id) + int num_params) { struct optee_supp_req *req; @@ -153,10 +192,6 @@ static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, return ERR_PTR(-EINVAL); } - *id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); - if (*id < 0) - return ERR_PTR(-ENOMEM); - list_del(&req->link); req->in_queue = false; @@ -214,7 +249,6 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct optee *optee = tee_get_drvdata(teedev); struct optee_supp *supp = &optee->supp; struct optee_supp_req *req = NULL; - int id; size_t num_meta; int rc; @@ -224,15 +258,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, while (true) { mutex_lock(&supp->mutex); - req = supp_pop_entry(supp, *num_params - num_meta, &id); + req = supp_pop_entry(supp, *num_params - num_meta); + if (req) + break; /* Keep mutex held. */ mutex_unlock(&supp->mutex); - if (req) { - if (IS_ERR(req)) - return PTR_ERR(req); - break; - } - /* * If we didn't get a request we'll block in * wait_for_completion() to avoid needless spinning. @@ -245,6 +275,13 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, return -ERESTARTSYS; } + /* supp->mutex held and req != NULL. */ + + if (IS_ERR(req)) { + mutex_unlock(&supp->mutex); + return PTR_ERR(req); + } + if (num_meta) { /* * tee-supplicant support meta parameters -> requsts can be @@ -252,13 +289,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, */ param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | TEE_IOCTL_PARAM_ATTR_META; - param->u.value.a = id; + param->u.value.a = req->id; param->u.value.b = 0; param->u.value.c = 0; } else { - mutex_lock(&supp->mutex); - supp->req_id = id; - mutex_unlock(&supp->mutex); + supp->req_id = req->id; } *func = req->func; @@ -266,6 +301,7 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, memcpy(param + num_meta, req->param, sizeof(struct tee_param) * req->num_params); + mutex_unlock(&supp->mutex); return 0; } @@ -297,12 +333,17 @@ static struct optee_supp_req *supp_pop_req(struct optee_supp *supp, if (!req) return ERR_PTR(-ENOENT); + /* optee_supp_thrd_req() already returned to optee. */ + if (IS_ERR(req)) + goto failed_req; + if ((num_params - nm) != req->num_params) return ERR_PTR(-EINVAL); + *num_meta = nm; +failed_req: idr_remove(&supp->idr, id); supp->req_id = -1; - *num_meta = nm; return req; } @@ -328,10 +369,9 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, mutex_lock(&supp->mutex); req = supp_pop_req(supp, num_params, param, &num_meta); - mutex_unlock(&supp->mutex); - if (IS_ERR(req)) { - /* Something is wrong, let supplicant restart. */ + mutex_unlock(&supp->mutex); + /* Something is wrong, let supplicant handel it. */ return PTR_ERR(req); } @@ -355,9 +395,10 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, } } req->ret = ret; - + req->processed = true; /* Let the requesting thread continue */ complete(&req->c); + mutex_unlock(&supp->mutex); return 0; } diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index b1cb50e434f0..60fe3b5776e3 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -306,8 +306,10 @@ int qcomtee_object_user_init(struct qcomtee_object *object, break; case QCOMTEE_OBJECT_TYPE_CB: object->ops = ops; - if (!object->ops->dispatch) - return -EINVAL; + if (!object->ops->dispatch) { + ret = -EINVAL; + break; + } /* If failed, "no-name". */ object->name = kvasprintf_const(GFP_KERNEL, fmt, ap); diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index ef9642d72672..1aac50c7c1de 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -530,11 +530,24 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, return 0; } +static void free_params(struct tee_param *params, size_t num_params) +{ + size_t n; + + if (!params) + return; + + for (n = 0; n < num_params; n++) + if (tee_param_is_memref(params + n) && params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + + kfree(params); +} + static int tee_ioctl_open_session(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_open_session_arg __user *uarg; struct tee_ioctl_open_session_arg arg; @@ -595,16 +608,7 @@ out: */ if (rc && have_session && ctx->teedev->desc->ops->close_session) ctx->teedev->desc->ops->close_session(ctx, arg.session); - - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } - + free_params(params, arg.num_params); return rc; } @@ -612,7 +616,6 @@ static int tee_ioctl_invoke(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_invoke_arg __user *uarg; struct tee_ioctl_invoke_arg arg; @@ -657,14 +660,7 @@ static int tee_ioctl_invoke(struct tee_context *ctx, } rc = params_to_user(uparams, arg.num_params, params); out: - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } + free_params(params, arg.num_params); return rc; } @@ -672,7 +668,6 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_object_invoke_arg __user *uarg; struct tee_ioctl_object_invoke_arg arg; @@ -716,14 +711,7 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, } rc = params_to_user(uparams, arg.num_params, params); out: - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } + free_params(params, arg.num_params); return rc; } @@ -846,9 +834,15 @@ static int tee_ioctl_supp_recv(struct tee_context *ctx, return -ENOMEM; rc = params_from_user(ctx, params, num_params, uarg->params); - if (rc) - goto out; + if (rc) { + free_params(params, num_params); + return rc; + } + /* + * supp_recv() may consume and replace the supplied parameters, so the + * final cleanup cannot use free_params() like the other ioctl paths. + */ rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params); if (rc) goto out; diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index e9ea9f80cfd9..6742b3579c86 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -435,7 +435,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, num_pages = iov_iter_npages(iter, INT_MAX); if (!num_pages) { ret = ERR_PTR(-ENOMEM); - goto err_ctx_put; + goto err_free_shm; } shm->pages = kzalloc_objs(*shm->pages, num_pages); diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index b10080d61860..810eeccedfba 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -436,6 +436,7 @@ config AMLOGIC_THERMAL tristate "Amlogic Thermal Support" default ARCH_MESON depends on OF && ARCH_MESON + depends on MESON_SM help If you say yes here you get support for Amlogic Thermal for G12 SoC Family. @@ -472,6 +473,8 @@ endmenu source "drivers/thermal/renesas/Kconfig" +source "drivers/thermal/spacemit/Kconfig" + source "drivers/thermal/tegra/Kconfig" config GENERIC_ADC_THERMAL diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index bb21e7ea7fc6..3b249195c088 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -65,6 +65,7 @@ obj-y += mediatek/ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o +obj-y += spacemit/ obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index 5448d772db12..a0b530624b60 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -25,6 +25,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/thermal.h> +#include <linux/firmware/meson/meson_sm.h> #include "thermal_hwmon.h" @@ -84,12 +85,14 @@ struct amlogic_thermal_soc_calib_data { * @u_efuse_off: register offset to read fused calibration value * @calibration_parameters: calibration parameters structure pointer * @regmap_config: regmap config for the device + * @use_sm: read data from secure monitor instead of efuse * This structure is required for configuration of amlogic thermal driver. */ struct amlogic_thermal_data { int u_efuse_off; const struct amlogic_thermal_soc_calib_data *calibration_parameters; const struct regmap_config *regmap_config; + bool use_sm; }; struct amlogic_thermal { @@ -100,6 +103,8 @@ struct amlogic_thermal { struct clk *clk; struct thermal_zone_device *tzd; u32 trim_info; + struct meson_sm_firmware *sm_fw; + u32 tsensor_id; }; /* @@ -133,26 +138,6 @@ static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata, return temp; } -static int amlogic_thermal_initialize(struct amlogic_thermal *pdata) -{ - int ret = 0; - int ver; - - regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off, - &pdata->trim_info); - - ver = TSENSOR_TRIM_VERSION(pdata->trim_info); - - if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) { - ret = -EINVAL; - dev_err(&pdata->pdev->dev, - "tsensor thermal calibration not supported: 0x%x!\n", - ver); - } - - return ret; -} - static int amlogic_thermal_enable(struct amlogic_thermal *data) { int ret; @@ -190,6 +175,67 @@ static int amlogic_thermal_get_temp(struct thermal_zone_device *tz, int *temp) return 0; } +static int amlogic_thermal_probe_sm(struct platform_device *pdev, + struct amlogic_thermal *pdata) +{ + struct device *dev = &pdev->dev; + struct of_phandle_args ph_args; + int ret; + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "amlogic,secure-monitor", + 1, 0, &ph_args); + if (ret) + return ret; + + if (!ph_args.np) { + dev_err(dev, "Failed to parse secure monitor phandle\n"); + return -ENODEV; + } + + pdata->sm_fw = meson_sm_get(ph_args.np); + of_node_put(ph_args.np); + if (!pdata->sm_fw) { + dev_err(dev, "Failed to get secure monitor firmware\n"); + return -EPROBE_DEFER; + } + + pdata->tsensor_id = ph_args.args[0]; + + return meson_sm_get_thermal_calib(pdata->sm_fw, + &pdata->trim_info, + pdata->tsensor_id); +} + +static int amlogic_thermal_probe_syscon(struct platform_device *pdev, + struct amlogic_thermal *pdata) +{ + struct device *dev = &pdev->dev; + int ver; + + pdata->sec_ao_map = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,ao-secure"); + if (IS_ERR(pdata->sec_ao_map)) { + dev_err(dev, "syscon regmap lookup failed.\n"); + return PTR_ERR(pdata->sec_ao_map); + } + + regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off, + &pdata->trim_info); + + ver = TSENSOR_TRIM_VERSION(pdata->trim_info); + + if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) { + dev_err(&pdata->pdev->dev, + "tsensor thermal calibration not supported: 0x%x!\n", + ver); + return -EINVAL; + } + + return 0; +} + static const struct thermal_zone_device_ops amlogic_thermal_ops = { .get_temp = amlogic_thermal_get_temp, }; @@ -226,6 +272,12 @@ static const struct amlogic_thermal_data amlogic_thermal_a1_cpu_param = { .regmap_config = &amlogic_thermal_regmap_config_g12a, }; +static const struct amlogic_thermal_data amlogic_thermal_t7_param = { + .use_sm = true, + .calibration_parameters = &amlogic_thermal_g12a, + .regmap_config = &amlogic_thermal_regmap_config_g12a, +}; + static const struct of_device_id of_amlogic_thermal_match[] = { { .compatible = "amlogic,g12a-ddr-thermal", @@ -239,6 +291,10 @@ static const struct of_device_id of_amlogic_thermal_match[] = { .compatible = "amlogic,a1-cpu-thermal", .data = &amlogic_thermal_a1_cpu_param, }, + { + .compatible = "amlogic,t7-thermal", + .data = &amlogic_thermal_t7_param, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match); @@ -271,12 +327,12 @@ static int amlogic_thermal_probe(struct platform_device *pdev) if (IS_ERR(pdata->clk)) return dev_err_probe(dev, PTR_ERR(pdata->clk), "failed to get clock\n"); - pdata->sec_ao_map = syscon_regmap_lookup_by_phandle - (pdev->dev.of_node, "amlogic,ao-secure"); - if (IS_ERR(pdata->sec_ao_map)) { - dev_err(dev, "syscon regmap lookup failed.\n"); - return PTR_ERR(pdata->sec_ao_map); - } + if (pdata->data->use_sm) + ret = amlogic_thermal_probe_sm(pdev, pdata); + else + ret = amlogic_thermal_probe_syscon(pdev, pdata); + if (ret) + return ret; pdata->tzd = devm_thermal_of_zone_register(&pdev->dev, 0, @@ -290,10 +346,6 @@ static int amlogic_thermal_probe(struct platform_device *pdev) devm_thermal_add_hwmon_sysfs(&pdev->dev, pdata->tzd); - ret = amlogic_thermal_initialize(pdata); - if (ret) - return ret; - ret = amlogic_thermal_enable(pdata); return ret; diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c index 32bf5ab44f4a..768859a7aed0 100644 --- a/drivers/thermal/cpufreq_cooling.c +++ b/drivers/thermal/cpufreq_cooling.c @@ -592,7 +592,7 @@ __cpufreq_cooling_register(struct device_node *np, if (!name) goto remove_qos_req; - cdev = thermal_of_cooling_device_register(np, name, cpufreq_cdev, + cdev = thermal_of_cooling_device_register(np, 0, name, cpufreq_cdev, cooling_ops); kfree(name); diff --git a/drivers/thermal/cpuidle_cooling.c b/drivers/thermal/cpuidle_cooling.c index 425f596614e8..bbd2e91cf5ab 100644 --- a/drivers/thermal/cpuidle_cooling.c +++ b/drivers/thermal/cpuidle_cooling.c @@ -207,7 +207,7 @@ static int __cpuidle_cooling_register(struct device_node *np, goto out_unregister; } - cdev = thermal_of_cooling_device_register(np, name, idle_cdev, + cdev = thermal_of_cooling_device_register(np, 0, name, idle_cdev, &cpuidle_cooling_ops); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index 1c7dffc8d45f..0330a8112832 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -454,7 +454,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, if (!name) goto remove_qos_req; - cdev = thermal_of_cooling_device_register(np, name, dfc, ops); + cdev = thermal_of_cooling_device_register(np, 0, name, dfc, ops); kfree(name); if (IS_ERR(cdev)) { diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 38c993d1bcb3..5aaacbc53478 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -693,8 +693,8 @@ static int imx_thermal_probe(struct platform_device *pdev) goto clk_disable; } - dev_info(dev, "%s CPU temperature grade - max:%dC" - " critical:%dC passive:%dC\n", data->temp_grade, + dev_info(dev, "%s CPU temperature grade - max:%dC critical:%dC passive:%dC\n", + data->temp_grade, data->temp_max / 1000, trips[IMX_TRIP_CRITICAL].temperature / 1000, trips[IMX_TRIP_PASSIVE].temperature / 1000); diff --git a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c index 0ccc72c93499..d92a6f84a778 100644 --- a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c +++ b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c @@ -138,7 +138,7 @@ static void ptc_mmio_write(struct pci_dev *pdev, u32 offset, int index, u32 valu reg_val = readq((void __iomem *) (proc_priv->mmio_base + offset)); reg_val &= ~mask; - reg_val |= (value << ptc_mmio_regs[index].shift); + reg_val |= ((u64)value << ptc_mmio_regs[index].shift); writeq(reg_val, (void __iomem *) (proc_priv->mmio_base + offset)); } @@ -278,12 +278,18 @@ static void ptc_delete_debugfs(void) int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) { if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) { - int i; + int i, ret; for (i = 0; i < PTC_MAX_INSTANCES; i++) { ptc_instance[i].offset = ptc_offsets[i]; ptc_instance[i].pdev = pdev; - ptc_create_groups(pdev, i, &ptc_instance[i]); + ret = ptc_create_groups(pdev, i, &ptc_instance[i]); + if (ret) { + while (i--) + sysfs_remove_group(&pdev->dev.kobj, + &ptc_instance[i].ptc_attr_group); + return ret; + } } ptc_create_debugfs(); diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 8c4ae75231f8..3273b8fe3d4d 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -41,6 +41,7 @@ #include <linux/topology.h> #include <linux/workqueue.h> +#include <asm/cpuid/api.h> #include <asm/msr.h> #include "intel_hfi.h" diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c index ccf380da12f2..bd7fd98dc310 100644 --- a/drivers/thermal/intel/intel_powerclamp.c +++ b/drivers/thermal/intel/intel_powerclamp.c @@ -200,7 +200,7 @@ static int cpumask_get(char *buf, const struct kernel_param *kp) if (!cpumask_available(idle_injection_cpu_mask)) return -ENODEV; - return cpumap_print_to_pagebuf(false, buf, idle_injection_cpu_mask); + return sysfs_emit(buf, "%*pb\n", cpumask_pr_args(idle_injection_cpu_mask)); } static const struct kernel_param_ops cpumask_ops = { diff --git a/drivers/thermal/intel/intel_tcc.c b/drivers/thermal/intel/intel_tcc.c index ab61fb122937..59f70bb5ffa5 100644 --- a/drivers/thermal/intel/intel_tcc.c +++ b/drivers/thermal/intel/intel_tcc.c @@ -181,17 +181,17 @@ static u32 get_temp_mask(bool pkg) */ int intel_tcc_get_tjmax(int cpu) { - u32 low, high; + struct msr msrval; int val, err; if (cpu < 0) - err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high); + err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &msrval.l, &msrval.h); else - err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high); + err = rdmsrq_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &msrval.q); if (err) return err; - val = (low >> 16) & 0xff; + val = (msrval.l >> 16) & 0xff; return val ? val : -ENODATA; } @@ -208,17 +208,17 @@ EXPORT_SYMBOL_NS_GPL(intel_tcc_get_tjmax, "INTEL_TCC"); */ int intel_tcc_get_offset(int cpu) { - u32 low, high; + struct msr val; int err; if (cpu < 0) - err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high); + err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &val.l, &val.h); else - err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high); + err = rdmsrq_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &val.q); if (err) return err; - return (low >> 24) & intel_tcc_temp_masks.tcc_offset; + return (val.l >> 24) & intel_tcc_temp_masks.tcc_offset; } EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, "INTEL_TCC"); @@ -235,7 +235,7 @@ EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, "INTEL_TCC"); int intel_tcc_set_offset(int cpu, int offset) { - u32 low, high; + struct msr val; int err; if (!intel_tcc_temp_masks.tcc_offset) @@ -245,23 +245,23 @@ int intel_tcc_set_offset(int cpu, int offset) return -EINVAL; if (cpu < 0) - err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high); + err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &val.l, &val.h); else - err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high); + err = rdmsrq_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &val.q); if (err) return err; /* MSR Locked */ - if (low & BIT(31)) + if (val.l & BIT(31)) return -EPERM; - low &= ~(intel_tcc_temp_masks.tcc_offset << 24); - low |= offset << 24; + val.l &= ~(intel_tcc_temp_masks.tcc_offset << 24); + val.l |= offset << 24; if (cpu < 0) - return wrmsr_safe(MSR_IA32_TEMPERATURE_TARGET, low, high); + return wrmsr_safe(MSR_IA32_TEMPERATURE_TARGET, val.l, val.h); else - return wrmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, low, high); + return wrmsrq_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, val.q); } EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, "INTEL_TCC"); @@ -279,7 +279,8 @@ EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, "INTEL_TCC"); int intel_tcc_get_temp(int cpu, int *temp, bool pkg) { u32 msr = pkg ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS; - u32 low, high, mask; + u32 mask; + struct msr val; int tjmax, err; tjmax = intel_tcc_get_tjmax(cpu); @@ -287,19 +288,19 @@ int intel_tcc_get_temp(int cpu, int *temp, bool pkg) return tjmax; if (cpu < 0) - err = rdmsr_safe(msr, &low, &high); + err = rdmsr_safe(msr, &val.l, &val.h); else - err = rdmsr_safe_on_cpu(cpu, msr, &low, &high); + err = rdmsrq_safe_on_cpu(cpu, msr, &val.q); if (err) return err; /* Temperature is beyond the valid thermal sensor range */ - if (!(low & BIT(31))) + if (!(val.l & BIT(31))) return -ENODATA; mask = get_temp_mask(pkg); - *temp = tjmax - ((low >> 16) & mask); + *temp = tjmax - ((val.l >> 16) & mask); return 0; } diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c index 6c2ba0ba3178..52ea4f7fc9f9 100644 --- a/drivers/thermal/intel/intel_tcc_cooling.c +++ b/drivers/thermal/intel/intel_tcc_cooling.c @@ -65,6 +65,9 @@ static const struct x86_cpu_id tcc_ids[] __initconst = { X86_MATCH_VFM(INTEL_RAPTORLAKE, NULL), X86_MATCH_VFM(INTEL_RAPTORLAKE_P, NULL), X86_MATCH_VFM(INTEL_RAPTORLAKE_S, NULL), + X86_MATCH_VFM(INTEL_ARROWLAKE, NULL), + X86_MATCH_VFM(INTEL_ARROWLAKE_U, NULL), + X86_MATCH_VFM(INTEL_ARROWLAKE_H, NULL), X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL), X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL), X86_MATCH_VFM(INTEL_NOVALAKE, NULL), diff --git a/drivers/thermal/intel/x86_pkg_temp_thermal.c b/drivers/thermal/intel/x86_pkg_temp_thermal.c index 540109761f0a..688e04c63761 100644 --- a/drivers/thermal/intel/x86_pkg_temp_thermal.c +++ b/drivers/thermal/intel/x86_pkg_temp_thermal.c @@ -20,6 +20,7 @@ #include <linux/debugfs.h> #include <asm/cpu_device_id.h> +#include <asm/cpuid/api.h> #include <asm/msr.h> #include "thermal_interrupt.h" @@ -125,8 +126,9 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, { struct zone_device *zonedev = thermal_zone_device_priv(tzd); unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv); - u32 l, h, mask, shift, intr; + u32 mask, shift, intr; int tj_max, val, ret; + struct msr v; if (temp == THERMAL_TEMP_INVALID) temp = 0; @@ -141,8 +143,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, if (trip_index >= MAX_NUMBER_OF_TRIPS || val < 0 || val > 0x7f) return -EINVAL; - ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - &l, &h); + ret = rdmsrq_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &v.q); if (ret < 0) return ret; @@ -155,20 +156,19 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, shift = THERM_SHIFT_THRESHOLD0; intr = THERM_INT_THRESHOLD0_ENABLE; } - l &= ~mask; + v.l &= ~mask; /* * When users space sets a trip temperature == 0, which is indication * that, it is no longer interested in receiving notifications. */ if (!temp) { - l &= ~intr; + v.l &= ~intr; } else { - l |= val << shift; - l |= intr; + v.l |= val << shift; + v.l |= intr; } - return wrmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - l, h); + return wrmsrq_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, v.q); } /* Thermal zone callback registry */ @@ -277,7 +277,8 @@ static int pkg_temp_thermal_trips_init(int cpu, int tj_max, struct thermal_trip *trips, int num_trips) { unsigned long thres_reg_value; - u32 mask, shift, eax, edx; + u32 mask, shift; + struct msr val; int ret, i; for (i = 0; i < num_trips; i++) { @@ -290,12 +291,11 @@ static int pkg_temp_thermal_trips_init(int cpu, int tj_max, shift = THERM_SHIFT_THRESHOLD0; } - ret = rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - &eax, &edx); + ret = rdmsrq_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &val.q); if (ret < 0) return ret; - thres_reg_value = (eax & mask) >> shift; + thres_reg_value = (val.l & mask) >> shift; trips[i].temperature = thres_reg_value ? tj_max - thres_reg_value * 1000 : THERMAL_TEMP_INVALID; diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c index d35e5313bea4..21b3d0a71bd0 100644 --- a/drivers/thermal/khadas_mcu_fan.c +++ b/drivers/thermal/khadas_mcu_fan.c @@ -90,9 +90,10 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev) ctx->mcu = mcu; platform_set_drvdata(pdev, ctx); - cdev = devm_thermal_of_cooling_device_register(dev->parent, - dev->parent->of_node, "khadas-mcu-fan", ctx, - &khadas_mcu_fan_cooling_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev->parent, + dev->parent->of_node, + "khadas-mcu-fan", ctx, + &khadas_mcu_fan_cooling_ops); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n", diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index 8d9698ea3ec4..2ee117aa91ba 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -263,7 +263,6 @@ static int __init init_tsens_v2_no_rpm(struct tsens_priv *priv) static const struct tsens_ops ops_generic_v2 = { .init = init_common, .get_temp = get_temp_tsens_valid, - .resume = tsens_resume_common, }; struct tsens_plat_data data_tsens_v2 = { @@ -307,3 +306,11 @@ struct tsens_plat_data data_8996 = { .feat = &tsens_v2_feat, .fields = tsens_v2_regfields, }; + +/* Do not enable wakeup capable interrupts for automotive platforms */ +struct tsens_plat_data data_automotive_v2 = { + .ops = &ops_generic_v2, + .feat = &tsens_v2_feat, + .fields = tsens_v2_regfields, + .no_irq_wake = true, +}; diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index a2422ebee816..6e3714ecab1d 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -27,7 +27,7 @@ * @up_viol: upper threshold violated * @up_thresh: upper threshold temperature value * @up_irq_mask: mask register for upper threshold irqs - * @up_irq_clear: clear register for uppper threshold irqs + * @up_irq_clear: clear register for upper threshold irqs * @low_viol: lower threshold violated * @low_thresh: lower threshold temperature value * @low_irq_mask: mask register for lower threshold irqs @@ -316,9 +316,66 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) } /** - * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. + * tsens_read_temp - Retrieve temperature readings from the hardware. * @s: Pointer to sensor struct * @field: Index into regmap_field array pointing to temperature data + * @temp: temperature in deciCelsius to be read from hardware + * + * This function handles temperature returned in ADC code or deciCelsius + * depending on IP version. + * + * Return: 0 on success, a negative errno will be returned in error cases + */ +static int tsens_read_temp(const struct tsens_sensor *s, int field, int *temp) +{ + struct tsens_priv *priv = s->priv; + int temp_val[MAX_READ_RETRY] = {0}; + u32 status; + int ret; + u32 last_temp_mask = GENMASK(priv->fields[LAST_TEMP_0].msb, + priv->fields[LAST_TEMP_0].lsb); + u32 valid_bit = priv->rf[VALID_0] ? BIT(priv->fields[VALID_0].lsb) : 0; + + for (int i = 0; i < MAX_READ_RETRY; i++) { + ret = regmap_read(priv->tm_map, priv->fields[field].reg, &status); + if (ret) + return ret; + + /* VER_0 doesn't have a VALID bit */ + if (!valid_bit) { + *temp = status & last_temp_mask; + return 0; + } + + temp_val[i] = status & last_temp_mask; + + if (status & valid_bit) { + *temp = temp_val[i]; + return 0; + } + } + + /* + * As per the HW guidelines, if none of the attempts observe a + * valid sample, a stable fallback value must be returned. If the + * first and second samples match, the second value is returned; + * otherwise, if the second and third samples match, the third + * value is returned. + */ + if (temp_val[0] == temp_val[1]) + *temp = temp_val[1]; + else if (temp_val[1] == temp_val[2]) + *temp = temp_val[2]; + else + return -EAGAIN; + + return 0; +} + +/** + * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. + * @s: Pointer to sensor struct + * @temp: temperature in milliCelsius to be read from hardware * * This function handles temperature returned in ADC code or deciCelsius * depending on IP version. @@ -326,20 +383,14 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) * Return: Temperature in milliCelsius on success, a negative errno will * be returned in error cases */ -static int tsens_hw_to_mC(const struct tsens_sensor *s, int field) +static int tsens_hw_to_mC(const struct tsens_sensor *s, int temp) { struct tsens_priv *priv = s->priv; u32 resolution; - u32 temp = 0; - int ret; resolution = priv->fields[LAST_TEMP_0].msb - priv->fields[LAST_TEMP_0].lsb; - ret = regmap_field_read(priv->rf[field], &temp); - if (ret) - return ret; - /* Convert temperature from ADC code to milliCelsius */ if (priv->feat->adc) return code_to_degc(temp, s) * 1000; @@ -514,8 +565,10 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, &d->crit_irq_mask); if (ret) return ret; - - d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); + ret = regmap_field_read(priv->rf[CRIT_THRESH_0 + hw_id], &d->crit_thresh); + if (ret) + return ret; + d->crit_thresh = tsens_hw_to_mC(s, d->crit_thresh); } else { /* No mask register on older TSENS */ d->up_irq_mask = 0; @@ -525,8 +578,16 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, d->crit_thresh = 0; } - d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); - d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); + ret = regmap_field_read(priv->rf[UP_THRESH_0 + hw_id], &d->up_thresh); + if (ret) + return ret; + + d->up_thresh = tsens_hw_to_mC(s, d->up_thresh); + ret = regmap_field_read(priv->rf[LOW_THRESH_0 + hw_id], &d->low_thresh); + if (ret) + return ret; + + d->low_thresh = tsens_hw_to_mC(s, d->low_thresh); dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n", hw_id, __func__, @@ -750,33 +811,15 @@ static void tsens_disable_irq(struct tsens_priv *priv) int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) { - struct tsens_priv *priv = s->priv; int hw_id = s->hw_id; u32 temp_idx = LAST_TEMP_0 + hw_id; - u32 valid_idx = VALID_0 + hw_id; - u32 valid; int ret; - /* VER_0 doesn't have VALID bit */ - if (tsens_version(priv) == VER_0) - goto get_temp; - - /* Valid bit is 0 for 6 AHB clock cycles. - * At 19.2MHz, 1 AHB clock is ~60ns. - * We should enter this loop very, very rarely. - * Wait 1 us since it's the min of poll_timeout macro. - * Old value was 400 ns. - */ - ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid, - valid, 1, 20 * USEC_PER_MSEC); - if (ret) - return ret; - -get_temp: - /* Valid bit is set, OK to read the temperature */ - *temp = tsens_hw_to_mC(s, temp_idx); + ret = tsens_read_temp(s, temp_idx, temp); + if (!ret) + *temp = tsens_hw_to_mC(s, *temp); - return 0; + return ret; } int get_temp_common(const struct tsens_sensor *s, int *temp) @@ -1086,22 +1129,30 @@ static int tsens_get_temp(struct thermal_zone_device *tz, int *temp) static int __maybe_unused tsens_suspend(struct device *dev) { + int ret = 0; struct tsens_priv *priv = dev_get_drvdata(dev); - if (priv->ops && priv->ops->suspend) - return priv->ops->suspend(priv); + if (priv->ops && priv->ops->suspend) { + ret = priv->ops->suspend(priv); + if (ret) + return ret; + } - return 0; + return tsens_suspend_common(priv); } static int __maybe_unused tsens_resume(struct device *dev) { + int ret = 0; struct tsens_priv *priv = dev_get_drvdata(dev); - if (priv->ops && priv->ops->resume) - return priv->ops->resume(priv); + if (priv->ops && priv->ops->resume) { + ret = priv->ops->resume(priv); + if (ret) + return ret; + } - return 0; + return tsens_resume_common(priv); } static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); @@ -1161,6 +1212,12 @@ static const struct of_device_id tsens_table[] = { }, { .compatible = "qcom,tsens-v2", .data = &data_tsens_v2, + }, { + .compatible = "qcom,sa8775p-tsens", + .data = &data_automotive_v2, + }, { + .compatible = "qcom,sa8255p-tsens", + .data = &data_automotive_v2, }, {} }; @@ -1172,7 +1229,7 @@ static const struct thermal_zone_device_ops tsens_of_ops = { }; static int tsens_register_irq(struct tsens_priv *priv, char *irqname, - irq_handler_t thread_fn) + irq_handler_t thread_fn, int *irq_num) { struct platform_device *pdev; int ret, irq; @@ -1205,7 +1262,7 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname, dev_err(&pdev->dev, "%s: failed to get irq\n", __func__); else - enable_irq_wake(irq); + *irq_num = irq; } put_device(&pdev->dev); @@ -1232,11 +1289,38 @@ static int tsens_reinit(struct tsens_priv *priv) return 0; } +int tsens_suspend_common(struct tsens_priv *priv) +{ + if (!device_may_wakeup(priv->dev)) + return 0; + + if (priv->feat->combo_int) + enable_irq_wake(priv->combined_irq); + else { + enable_irq_wake(priv->uplow_irq); + if (priv->feat->crit_int) + enable_irq_wake(priv->crit_irq); + } + + return 0; +} + int tsens_resume_common(struct tsens_priv *priv) { if (pm_suspend_target_state == PM_SUSPEND_MEM) tsens_reinit(priv); + if (!device_may_wakeup(priv->dev)) + return 0; + + if (priv->feat->combo_int) + disable_irq_wake(priv->combined_irq); + else { + disable_irq_wake(priv->uplow_irq); + if (priv->feat->crit_int) + disable_irq_wake(priv->crit_irq); + } + return 0; } @@ -1276,15 +1360,18 @@ static int tsens_register(struct tsens_priv *priv) if (priv->feat->combo_int) { ret = tsens_register_irq(priv, "combined", - tsens_combined_irq_thread); + tsens_combined_irq_thread, &priv->combined_irq); } else { - ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); + ret = tsens_register_irq(priv, "uplow", tsens_irq_thread, + &priv->uplow_irq); if (ret < 0) return ret; - if (priv->feat->crit_int) + if (priv->feat->crit_int) { ret = tsens_register_irq(priv, "critical", - tsens_critical_irq_thread); + tsens_critical_irq_thread, + &priv->crit_irq); + } } return ret; @@ -1343,6 +1430,8 @@ static int tsens_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); + device_init_wakeup(dev, !data->no_irq_wake); + if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) return -EINVAL; diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 2a7afa4c899b..e8376accdff3 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -21,6 +21,7 @@ #define THRESHOLD_MIN_ADC_CODE 0x0 #define MAX_SENSORS 16 +#define MAX_READ_RETRY 3 #include <linux/interrupt.h> #include <linux/thermal.h> @@ -531,6 +532,7 @@ struct tsens_features { * @hw_ids: Subset of sensors ids supported by platform, if not the first n * @feat: features of the IP * @fields: bitfield locations + * @no_irq_wake: if set, TSENS interrupts will not be configured as wakeup sources */ struct tsens_plat_data { const u32 num_sensors; @@ -538,6 +540,7 @@ struct tsens_plat_data { unsigned int *hw_ids; struct tsens_features *feat; const struct reg_field *fields; + bool no_irq_wake; }; /** @@ -567,6 +570,9 @@ struct tsens_context { * @ops: pointer to list of callbacks supported by this device * @debug_root: pointer to debugfs dentry for all tsens * @debug: pointer to debugfs dentry for tsens controller + * @uplow_irq: IRQ number for uplow (upper/lower) threshold interrupts + * @crit_irq: IRQ number for critical threshold interrupts + * @combined_irq: IRQ number for combined threshold interrupts * @sensor: list of sensors attached to this device */ struct tsens_priv { @@ -588,6 +594,10 @@ struct tsens_priv { struct dentry *debug_root; struct dentry *debug; + int uplow_irq; + int crit_irq; + int combined_irq; + struct tsens_sensor sensor[] __counted_by(num_sensors); }; @@ -639,8 +649,17 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp); int get_temp_common(const struct tsens_sensor *s, int *temp); #ifdef CONFIG_SUSPEND int tsens_resume_common(struct tsens_priv *priv); +int tsens_suspend_common(struct tsens_priv *priv); #else -#define tsens_resume_common NULL +static inline int tsens_resume_common(struct tsens_priv *priv) +{ + return 0; +} + +static inline int tsens_suspend_common(struct tsens_priv *priv) +{ + return 0; +} #endif /* TSENS target */ @@ -659,4 +678,7 @@ extern const struct tsens_plat_data data_ipq5018; extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; extern const struct tsens_plat_data data_ipq5332, data_ipq5424; +/* TSENS automotive targets */ +extern struct tsens_plat_data data_automotive_v2; + #endif /* __QCOM_TSENS_H__ */ diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 01b58be0dcc6..35439ec5f8bc 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // // Copyright 2016 Freescale Semiconductor, Inc. +// Copyright 2025 NXP +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> @@ -24,10 +26,14 @@ #define TMTMIR_DEFAULT 0x0000000f #define TIER_DISABLE 0x0 #define TEUMR0_V2 0x51009c00 +#define TEUMR0_V21 0x55000c00 #define TMSARA_V2 0xe #define TMU_VER1 0x1 #define TMU_VER2 0x2 +/* errata ID info define */ +#define TMU_ERR052243 BIT(0) + #define REGS_TMR 0x000 /* Mode Register */ #define TMR_DISABLE 0x0 #define TMR_ME 0x80000000 @@ -43,6 +49,15 @@ #define REGS_TIER 0x020 /* Interrupt Enable Register */ #define TIER_DISABLE 0x0 +#define REGS_TIDR 0x24 +#define TEMP_RATE_IRQ_MASK GENMASK(25, 24) +#define TMRTRCTR 0x70 +#define TMRTRCTR_EN BIT(31) +#define TMRTRCTR_TEMP_MASK GENMASK(7, 0) +#define TMFTRCTR 0x74 +#define TMFTRCTR_EN BIT(31) +#define TMFTRCTR_TEMP_MASK GENMASK(7, 0) +#define TEMP_RATE_THR_LVL 0x7 #define REGS_TTCFGR 0x080 /* Temperature Configuration Register */ #define REGS_TSCFGR 0x084 /* Sensor Configuration Register */ @@ -73,14 +88,26 @@ struct qoriq_sensor { int id; }; +struct tmu_drvdata { + u32 teumr0; + u32 tmu_errata; +}; + struct qoriq_tmu_data { int ver; u32 ttrcr[NUM_TTRCR_MAX]; struct regmap *regmap; struct clk *clk; struct qoriq_sensor sensor[SITES_MAX]; + const struct tmu_drvdata *drvdata; }; +static inline bool qoriq_tmu_has_errata(const struct tmu_drvdata *drvdata, + u32 flag) +{ + return drvdata->tmu_errata & flag; +} + static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) { return container_of(s, struct qoriq_tmu_data, sensor[s->id]); @@ -90,7 +117,7 @@ static int tmu_get_temp(struct thermal_zone_device *tz, int *temp) { struct qoriq_sensor *qsensor = thermal_zone_device_priv(tz); struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor); - u32 val; + u32 val, tidr; /* * REGS_TRITSR(id) has the following layout: * @@ -123,6 +150,15 @@ static int tmu_get_temp(struct thermal_zone_device *tz, int *temp) 10 * USEC_PER_MSEC)) return -ENODATA; + /*ERR052243: If a raising or falling edge happens, try later */ + if (qoriq_tmu_has_errata(qdata->drvdata, TMU_ERR052243)) { + regmap_read(qdata->regmap, REGS_TIDR, &tidr); + if (tidr & TEMP_RATE_IRQ_MASK) { + regmap_write(qdata->regmap, REGS_TIDR, TEMP_RATE_IRQ_MASK); + return -EAGAIN; + } + } + if (qdata->ver == TMU_VER1) { *temp = (val & GENMASK(7, 0)) * MILLIDEGREE_PER_DEGREE; } else { @@ -234,9 +270,18 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT); } else { regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT); - regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2); + regmap_write(data->regmap, REGS_V2_TEUMR(0), + data->drvdata->teumr0); } + /* ERR052243: Set the raising & falling edge monitor */ + if (qoriq_tmu_has_errata(data->drvdata, TMU_ERR052243)) { + regmap_write(data->regmap, TMRTRCTR, TMRTRCTR_EN | + FIELD_PREP(TMRTRCTR_TEMP_MASK, TEMP_RATE_THR_LVL)); + regmap_write(data->regmap, TMFTRCTR, TMFTRCTR_EN | + FIELD_PREP(TMFTRCTR_TEMP_MASK, TEMP_RATE_THR_LVL)); + + } /* Disable monitoring */ regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); } @@ -319,6 +364,10 @@ static int qoriq_tmu_probe(struct platform_device *pdev) data->ver = (ver >> 8) & 0xff; + data->drvdata = of_device_get_match_data(&pdev->dev); + if (!data->drvdata) + return dev_err_probe(dev, -EINVAL, "Failed to get match data\n"); + qoriq_tmu_init_device(data); /* TMU initialization */ ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */ @@ -376,9 +425,23 @@ static int qoriq_tmu_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, qoriq_tmu_suspend, qoriq_tmu_resume); +static const struct tmu_drvdata qoriq_tmu_data = { + .teumr0 = TEUMR0_V2, +}; + +static const struct tmu_drvdata imx8mq_tmu_data = { + .teumr0 = TEUMR0_V2, +}; + +static const struct tmu_drvdata imx93_data = { + .teumr0 = TEUMR0_V21, + .tmu_errata = TMU_ERR052243, +}; + static const struct of_device_id qoriq_tmu_match[] = { - { .compatible = "fsl,qoriq-tmu", }, - { .compatible = "fsl,imx8mq-tmu", }, + { .compatible = "fsl,qoriq-tmu", .data = &qoriq_tmu_data }, + { .compatible = "fsl,imx8mq-tmu", .data = &imx8mq_tmu_data }, + { .compatible = "fsl,imx93-tmu", .data = &imx93_data }, {}, }; MODULE_DEVICE_TABLE(of, qoriq_tmu_match); diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig index f4eff5a41a84..e1e8035d24fb 100644 --- a/drivers/thermal/samsung/Kconfig +++ b/drivers/thermal/samsung/Kconfig @@ -3,6 +3,7 @@ config EXYNOS_THERMAL tristate "Exynos thermal management unit driver" depends on THERMAL_OF depends on HAS_IOMEM + default ARCH_EXYNOS help If you say yes here you get support for the TMU (Thermal Management Unit) driver for Samsung Exynos series of SoCs. This driver initialises diff --git a/drivers/thermal/spacemit/Kconfig b/drivers/thermal/spacemit/Kconfig new file mode 100644 index 000000000000..de7b5ece5af2 --- /dev/null +++ b/drivers/thermal/spacemit/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "SpacemiT thermal drivers" +depends on ARCH_SPACEMIT || COMPILE_TEST + +config SPACEMIT_K1_TSENSOR + tristate "SpacemiT K1 thermal sensor driver" + depends on THERMAL_OF + help + This driver provides support for the thermal sensor + integrated in the SpacemiT K1 SoC. + + The thermal sensor monitors temperatures for five thermal zones: + soc, package, gpu, cluster0, and cluster1. It supports reporting + temperature values and handling high/low threshold interrupts. + + Say Y here if you want to enable thermal monitoring on SpacemiT K1. + If compiled as a module, it will be called k1_tsensor. + +endmenu diff --git a/drivers/thermal/spacemit/Makefile b/drivers/thermal/spacemit/Makefile new file mode 100644 index 000000000000..82b30741e4ec --- /dev/null +++ b/drivers/thermal/spacemit/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SPACEMIT_K1_TSENSOR) += k1_tsensor.o diff --git a/drivers/thermal/spacemit/k1_tsensor.c b/drivers/thermal/spacemit/k1_tsensor.c new file mode 100644 index 000000000000..79222d233129 --- /dev/null +++ b/drivers/thermal/spacemit/k1_tsensor.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thermal sensor driver for SpacemiT K1 SoC + * + * Copyright (C) 2026 Shuwei Wu <shuwei.wu@mailbox.org> + */ +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/thermal.h> + +#include "../thermal_hwmon.h" + +#define K1_TSENSOR_PCTRL_REG 0x00 +#define K1_TSENSOR_PCTRL_ENABLE BIT(0) +#define K1_TSENSOR_PCTRL_TEMP_MODE BIT(3) +#define K1_TSENSOR_PCTRL_RAW_SEL BIT(7) + +#define K1_TSENSOR_PCTRL_CTUNE GENMASK(11, 8) +#define K1_TSENSOR_PCTRL_SW_CTRL GENMASK(21, 18) +#define K1_TSENSOR_PCTRL_HW_AUTO_MODE BIT(23) + +#define K1_TSENSOR_EN_REG 0x08 +#define K1_TSENSOR_EN_ALL GENMASK(MAX_SENSOR_NUMBER - 1, 0) + +#define K1_TSENSOR_TIME_REG 0x0C +#define K1_TSENSOR_TIME_WAIT_REF_CNT GENMASK(3, 0) +#define K1_TSENSOR_TIME_ADC_CNT_RST GENMASK(7, 4) +#define K1_TSENSOR_TIME_FILTER_PERIOD GENMASK(21, 20) +#define K1_TSENSOR_TIME_MASK GENMASK(23, 0) + +#define K1_TSENSOR_INT_CLR_REG 0x10 +#define K1_TSENSOR_INT_EN_REG 0x14 +#define K1_TSENSOR_INT_STA_REG 0x18 + +#define K1_TSENSOR_INT_EN_MASK BIT(0) +#define K1_TSENSOR_INT_MASK(x) (GENMASK(2, 1) << ((x) * 2)) + +#define K1_TSENSOR_DATA_BASE_REG 0x20 +#define K1_TSENSOR_DATA_REG(x) (K1_TSENSOR_DATA_BASE_REG + ((x) / 2) * 4) +#define K1_TSENSOR_DATA_LOW_MASK GENMASK(15, 0) +#define K1_TSENSOR_DATA_HIGH_MASK GENMASK(31, 16) + +#define K1_TSENSOR_THRSH_BASE_REG 0x40 +#define K1_TSENSOR_THRSH_REG(x) (K1_TSENSOR_THRSH_BASE_REG + ((x) * 4)) +#define K1_TSENSOR_THRSH_LOW_MASK GENMASK(15, 0) +#define K1_TSENSOR_THRSH_HIGH_MASK GENMASK(31, 16) + +#define MAX_SENSOR_NUMBER 5 + +/* Hardware offset value required for temperature calculation */ +#define TEMPERATURE_OFFSET 278 + +struct k1_tsensor_channel { + struct k1_tsensor *ts; + struct thermal_zone_device *tzd; + int id; +}; + +struct k1_tsensor { + void __iomem *base; + struct k1_tsensor_channel ch[MAX_SENSOR_NUMBER]; +}; + +static void k1_tsensor_init(struct k1_tsensor *ts) +{ + u32 val; + + /* Disable all the interrupts */ + writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG); + + /* Configure ADC sampling time and filter period */ + val = readl(ts->base + K1_TSENSOR_TIME_REG); + val &= ~K1_TSENSOR_TIME_MASK; + val |= K1_TSENSOR_TIME_FILTER_PERIOD | + K1_TSENSOR_TIME_ADC_CNT_RST | + K1_TSENSOR_TIME_WAIT_REF_CNT; + writel(val, ts->base + K1_TSENSOR_TIME_REG); + + /* + * Enable all sensors' auto mode, enable dither control, + * consecutive mode, and power up sensor. + */ + val = readl(ts->base + K1_TSENSOR_PCTRL_REG); + val &= ~K1_TSENSOR_PCTRL_SW_CTRL; + val &= ~K1_TSENSOR_PCTRL_CTUNE; + val |= K1_TSENSOR_PCTRL_RAW_SEL | + K1_TSENSOR_PCTRL_TEMP_MODE | + K1_TSENSOR_PCTRL_HW_AUTO_MODE | + K1_TSENSOR_PCTRL_ENABLE; + writel(val, ts->base + K1_TSENSOR_PCTRL_REG); + + /* Enable each sensor */ + val = readl(ts->base + K1_TSENSOR_EN_REG); + val |= K1_TSENSOR_EN_ALL; + writel(val, ts->base + K1_TSENSOR_EN_REG); +} + +static void k1_tsensor_enable_irq(struct k1_tsensor_channel *ch) +{ + struct k1_tsensor *ts = ch->ts; + u32 val; + + val = readl(ts->base + K1_TSENSOR_INT_CLR_REG); + val |= K1_TSENSOR_INT_MASK(ch->id); + writel(val, ts->base + K1_TSENSOR_INT_CLR_REG); + + val = readl(ts->base + K1_TSENSOR_INT_EN_REG); + val &= ~K1_TSENSOR_INT_MASK(ch->id); + writel(val, ts->base + K1_TSENSOR_INT_EN_REG); + + /* Enable thermal interrupt */ + val = readl(ts->base + K1_TSENSOR_INT_EN_REG); + val |= K1_TSENSOR_INT_EN_MASK; + writel(val, ts->base + K1_TSENSOR_INT_EN_REG); +} + +/* + * The conversion formula used is: + * T(m°C) = (((raw_value & mask) >> shift) - TEMPERATURE_OFFSET) * 1000 + */ +static int k1_tsensor_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz); + struct k1_tsensor *ts = ch->ts; + u32 val; + + val = readl(ts->base + K1_TSENSOR_DATA_REG(ch->id)); + if (ch->id % 2) + *temp = FIELD_GET(K1_TSENSOR_DATA_HIGH_MASK, val); + else + *temp = FIELD_GET(K1_TSENSOR_DATA_LOW_MASK, val); + + *temp -= TEMPERATURE_OFFSET; + *temp *= 1000; + + return 0; +} + +/* + * For each sensor, the hardware threshold register is 32 bits: + * - Lower 16 bits [15:0] configure the low threshold temperature. + * - Upper 16 bits [31:16] configure the high threshold temperature. + */ +static int k1_tsensor_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz); + struct k1_tsensor *ts = ch->ts; + u32 val; + + if (low >= high) + return -EINVAL; + + low = clamp_val(low / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET, + FIELD_MAX(K1_TSENSOR_THRSH_LOW_MASK)); + high = clamp_val(high / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET, + FIELD_MAX(K1_TSENSOR_THRSH_HIGH_MASK)); + + val = readl(ts->base + K1_TSENSOR_THRSH_REG(ch->id)); + + val &= ~(K1_TSENSOR_THRSH_LOW_MASK | K1_TSENSOR_THRSH_HIGH_MASK); + val |= FIELD_PREP(K1_TSENSOR_THRSH_LOW_MASK, low); + val |= FIELD_PREP(K1_TSENSOR_THRSH_HIGH_MASK, high); + + writel(val, ts->base + K1_TSENSOR_THRSH_REG(ch->id)); + + return 0; +} + +static const struct thermal_zone_device_ops k1_tsensor_ops = { + .get_temp = k1_tsensor_get_temp, + .set_trips = k1_tsensor_set_trips, +}; + +static irqreturn_t k1_tsensor_irq_thread(int irq, void *data) +{ + struct k1_tsensor *ts = (struct k1_tsensor *)data; + int mask, status, i; + + status = readl(ts->base + K1_TSENSOR_INT_STA_REG); + + for (i = 0; i < MAX_SENSOR_NUMBER; i++) { + if (status & K1_TSENSOR_INT_MASK(i)) { + mask = readl(ts->base + K1_TSENSOR_INT_CLR_REG); + mask |= K1_TSENSOR_INT_MASK(i); + writel(mask, ts->base + K1_TSENSOR_INT_CLR_REG); + thermal_zone_device_update(ts->ch[i].tzd, THERMAL_EVENT_UNSPECIFIED); + } + } + + return IRQ_HANDLED; +} + +static int k1_tsensor_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct k1_tsensor *ts; + struct reset_control *reset; + struct clk *clk; + int i, irq, ret; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ts->base)) + return dev_err_probe(dev, PTR_ERR(ts->base), "Failed to get reg\n"); + + reset = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), "Failed to get/deassert reset control\n"); + + clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get core clock\n"); + + clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get bus clock\n"); + + k1_tsensor_init(ts); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + k1_tsensor_irq_thread, + IRQF_ONESHOT, "k1_tsensor", ts); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_SENSOR_NUMBER; ++i) { + ts->ch[i].id = i; + ts->ch[i].ts = ts; + ts->ch[i].tzd = devm_thermal_of_zone_register(dev, i, ts->ch + i, &k1_tsensor_ops); + if (IS_ERR(ts->ch[i].tzd)) + return PTR_ERR(ts->ch[i].tzd); + + /* Attach sysfs hwmon attributes for userspace monitoring */ + ret = devm_thermal_add_hwmon_sysfs(dev, ts->ch[i].tzd); + if (ret) + dev_warn(dev, "Failed to add hwmon sysfs attributes\n"); + + k1_tsensor_enable_irq(ts->ch + i); + } + + platform_set_drvdata(pdev, ts); + + return 0; +} + +static const struct of_device_id k1_tsensor_dt_ids[] = { + { .compatible = "spacemit,k1-tsensor" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, k1_tsensor_dt_ids); + +static struct platform_driver k1_tsensor_driver = { + .driver = { + .name = "k1_tsensor", + .of_match_table = k1_tsensor_dt_ids, + }, + .probe = k1_tsensor_probe, +}; +module_platform_driver(k1_tsensor_driver); + +MODULE_DESCRIPTION("SpacemiT K1 Thermal Sensor Driver"); +MODULE_AUTHOR("Shuwei Wu <shuwei.wu@mailbox.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 5d26b52beaba..d8e988a0d43e 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1499,6 +1499,13 @@ static int soctherm_clk_enable(struct platform_device *pdev, bool enable) return 0; } +static void soctherm_clk_disable(void *data) +{ + struct platform_device *pdev = data; + + soctherm_clk_enable(pdev, false); +} + static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev, unsigned long *max_state) { @@ -1700,9 +1707,9 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) stc->init = true; } else { - tcd = thermal_of_cooling_device_register(np_stcc, - (char *)name, ts, - &throt_cooling_ops); + tcd = devm_thermal_of_child_cooling_device_register(dev, np_stcc, + (char *)name, ts, + &throt_cooling_ops); if (IS_ERR_OR_NULL(tcd)) { dev_err(dev, "throttle-cfg: %s: failed to register cooling device\n", @@ -2175,6 +2182,10 @@ static int tegra_soctherm_probe(struct platform_device *pdev) if (err) return err; + err = devm_add_action_or_reset(&pdev->dev, soctherm_clk_disable, pdev); + if (err) + return err; + soctherm_thermtrips_parse(pdev); soctherm_init_hw_throt_cdev(pdev); @@ -2184,10 +2195,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) for (i = 0; i < soc->num_ttgs; ++i) { struct tegra_thermctl_zone *zone = devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); - if (!zone) { - err = -ENOMEM; - goto disable_clocks; - } + if (!zone) + return -ENOMEM; zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; zone->dev = &pdev->dev; @@ -2201,7 +2210,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) err = PTR_ERR(z); dev_err(&pdev->dev, "failed to register sensor: %d\n", err); - goto disable_clocks; + return err; } zone->tz = z; @@ -2210,7 +2219,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) /* Configure hw trip points */ err = tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); if (err) - goto disable_clocks; + return err; } err = soctherm_interrupts_init(pdev, tegra); @@ -2218,11 +2227,6 @@ static int tegra_soctherm_probe(struct platform_device *pdev) soctherm_debug_init(pdev); return 0; - -disable_clocks: - soctherm_clk_enable(pdev, false); - - return err; } static void tegra_soctherm_remove(struct platform_device *pdev) @@ -2230,8 +2234,6 @@ static void tegra_soctherm_remove(struct platform_device *pdev) struct tegra_soctherm *tegra = platform_get_drvdata(pdev); debugfs_remove_recursive(tegra->debugfs_dir); - - soctherm_clk_enable(pdev, false); } static int __maybe_unused soctherm_suspend(struct device *dev) diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c index 1159ecea57e7..fbf7ab9729b5 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -116,18 +116,30 @@ static int tt_command_exec(int index, const char *arg) break; case TT_CMD_DELTZ: + if (!arg || !*arg) + return -EINVAL; + ret = tt_del_tz(arg); break; case TT_CMD_TZADDTRIP: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_add_trip(arg); break; case TT_CMD_TZREG: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_reg(arg); break; case TT_CMD_TZUNREG: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_unreg(arg); break; diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index 3c339242f52d..f7f9ca2f1f2c 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -239,9 +239,9 @@ int tt_del_tz(const char *arg) int ret; int id; - ret = sscanf(arg, "%d", &id); - if (ret != 1) - return -EINVAL; + ret = kstrtoint(arg, 10, &id); + if (ret < 0) + return ret; struct tt_work *tt_work __free(kfree) = kzalloc_obj(*tt_work); if (!tt_work) @@ -279,9 +279,9 @@ static struct tt_thermal_zone *tt_get_tt_zone(const char *arg) struct tt_thermal_zone *tt_zone; int ret, id; - ret = sscanf(arg, "%d", &id); - if (ret != 1) - return ERR_PTR(-EINVAL); + ret = kstrtoint(arg, 10, &id); + if (ret < 0) + return ERR_PTR(ret); guard(mutex)(&tt_thermal_zones_lock); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 2f4e2dc46b8f..28a20d4b475c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -116,78 +116,24 @@ static int thermal_set_governor(struct thermal_zone_device *tz, return ret; } -int thermal_register_governor(struct thermal_governor *governor) +static int __init thermal_register_governor(struct thermal_governor *governor) { - int err; - const char *name; - struct thermal_zone_device *pos; if (!governor) return -EINVAL; - guard(mutex)(&thermal_governor_lock); - - err = -EBUSY; - if (!__find_governor(governor->name)) { - bool match_default; - - err = 0; - list_add(&governor->governor_list, &thermal_governor_list); - match_default = !strncmp(governor->name, - DEFAULT_THERMAL_GOVERNOR, - THERMAL_NAME_LENGTH); - - if (!def_governor && match_default) - def_governor = governor; - } - - guard(mutex)(&thermal_list_lock); - - list_for_each_entry(pos, &thermal_tz_list, node) { - /* - * only thermal zones with specified tz->tzp->governor_name - * may run with tz->govenor unset - */ - if (pos->governor) - continue; - - name = pos->tzp->governor_name; - - if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) { - int ret; - - ret = thermal_set_governor(pos, governor); - if (ret) - dev_err(&pos->device, - "Failed to set governor %s for thermal zone %s: %d\n", - governor->name, pos->type, ret); - } - } - - return err; -} - -void thermal_unregister_governor(struct thermal_governor *governor) -{ - struct thermal_zone_device *pos; + if (__find_governor(governor->name)) + return -EBUSY; - if (!governor) - return; - - guard(mutex)(&thermal_governor_lock); - - if (!__find_governor(governor->name)) - return; + list_add(&governor->governor_list, &thermal_governor_list); - list_del(&governor->governor_list); + if (strncmp(governor->name, DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH)) + return 0; - guard(mutex)(&thermal_list_lock); + if (!def_governor) + def_governor = governor; - list_for_each_entry(pos, &thermal_tz_list, node) { - if (!strncasecmp(pos->governor->name, governor->name, - THERMAL_NAME_LENGTH)) - thermal_set_governor(pos, NULL); - } + return 0; } int thermal_zone_device_set_policy(struct thermal_zone_device *tz, @@ -225,40 +171,34 @@ int thermal_build_list_of_policies(char *buf) static void __init thermal_unregister_governors(void) { - struct thermal_governor **governor; + struct thermal_governor *gov, *pos; + + guard(mutex)(&thermal_governor_lock); - for_each_governor_table(governor) - thermal_unregister_governor(*governor); + list_for_each_entry_safe(gov, pos, &thermal_governor_list, governor_list) + list_del(&gov->governor_list); } static int __init thermal_register_governors(void) { - int ret = 0; struct thermal_governor **governor; + guard(mutex)(&thermal_governor_lock); + for_each_governor_table(governor) { + int ret; + ret = thermal_register_governor(*governor); if (ret) { pr_err("Failed to register governor: '%s'", (*governor)->name); - break; + return ret; } - pr_info("Registered thermal governor '%s'", - (*governor)->name); + pr_info("Registered thermal governor '%s'", (*governor)->name); } - if (ret) { - struct thermal_governor **gov; - - for_each_governor_table(gov) { - if (gov == governor) - break; - thermal_unregister_governor(*gov); - } - } - - return ret; + return 0; } static int __thermal_zone_device_set_mode(struct thermal_zone_device *tz, @@ -949,34 +889,7 @@ unbind: kfree(pos); } -static void thermal_release(struct device *dev) -{ - struct thermal_zone_device *tz; - struct thermal_cooling_device *cdev; - - if (!strncmp(dev_name(dev), "thermal_zone", - sizeof("thermal_zone") - 1)) { - tz = to_thermal_zone(dev); - thermal_zone_destroy_device_groups(tz); - thermal_set_governor(tz, NULL); - ida_destroy(&tz->ida); - mutex_destroy(&tz->lock); - complete(&tz->removal); - } else if (!strncmp(dev_name(dev), "cooling_device", - sizeof("cooling_device") - 1)) { - cdev = to_cooling_device(dev); - thermal_cooling_device_destroy_sysfs(cdev); - kfree_const(cdev->type); - ida_free(&thermal_cdev_ida, cdev->id); - kfree(cdev); - } -} - -static const struct class thermal_class = { - .name = "thermal", - .dev_release = thermal_release, -}; -static bool thermal_class_unavailable __ro_after_init = true; +static struct class *thermal_class __ro_after_init; static inline void print_bind_err_msg(struct thermal_zone_device *tz, @@ -1040,42 +953,35 @@ static void thermal_cooling_device_init_complete(struct thermal_cooling_device * thermal_zone_cdev_bind(tz, cdev); } -/** - * __thermal_cooling_device_register() - register a new thermal cooling device - * @np: a pointer to a device tree node. - * @type: the thermal cooling device type. - * @devdata: device private data. - * @ops: standard thermal cooling devices callbacks. - * - * This interface function adds a new thermal cooling device (fan/processor/...) - * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself - * to all the thermal zone devices registered at the same time. - * It also gives the opportunity to link the cooling device to a device tree - * node, so that it can be bound to a thermal zone created out of device tree. - * - * Return: a pointer to the created struct thermal_cooling_device or an - * ERR_PTR. Caller must check return value with IS_ERR*() helpers. - */ -static struct thermal_cooling_device * -__thermal_cooling_device_register(struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) +static void thermal_cdev_release(struct device *dev) +{ + struct thermal_cooling_device *cdev = to_cooling_device(dev); + + thermal_cooling_device_destroy_sysfs(cdev); + kfree_const(cdev->type); + ida_free(&thermal_cdev_ida, cdev->id); + kfree(cdev); +} + +struct thermal_cooling_device * +thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; - unsigned long current_state; int ret; if (!ops || !ops->get_max_state || !ops->get_cur_state || !ops->set_cur_state) return ERR_PTR(-EINVAL); - if (thermal_class_unavailable) + if (!thermal_class) return ERR_PTR(-ENODEV); cdev = kzalloc_obj(*cdev); if (!cdev) return ERR_PTR(-ENOMEM); + cdev->ops = ops; + ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL); if (ret < 0) goto out_kfree_cdev; @@ -1087,17 +993,35 @@ __thermal_cooling_device_register(struct device_node *np, goto out_ida_remove; } + return cdev; + +out_ida_remove: + ida_free(&thermal_cdev_ida, cdev->id); +out_kfree_cdev: + kfree(cdev); + return ERR_PTR(ret); +} + +int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata) +{ + unsigned long current_state; + int ret; + mutex_init(&cdev->lock); INIT_LIST_HEAD(&cdev->thermal_instances); - cdev->np = np; - cdev->ops = ops; cdev->updated = false; - cdev->device.class = &thermal_class; + cdev->device.class = thermal_class; + cdev->device.release = thermal_cdev_release; + device_initialize(&cdev->device); cdev->devdata = devdata; + ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); + if (ret) + goto out_put_device; + ret = cdev->ops->get_max_state(cdev, &cdev->max_state); if (ret) - goto out_cdev_type; + goto out_put_device; /* * The cooling device's current state is only needed for debug @@ -1113,40 +1037,32 @@ __thermal_cooling_device_register(struct device_node *np, thermal_cooling_device_setup_sysfs(cdev); - ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); + ret = device_add(&cdev->device); if (ret) - goto out_cooling_dev; - - ret = device_register(&cdev->device); - if (ret) { - /* thermal_release() handles rest of the cleanup */ - put_device(&cdev->device); - return ERR_PTR(ret); - } + goto out_put_device; if (current_state <= cdev->max_state) thermal_debug_cdev_add(cdev, current_state); thermal_cooling_device_init_complete(cdev); - return cdev; + return 0; -out_cooling_dev: - thermal_cooling_device_destroy_sysfs(cdev); -out_cdev_type: - kfree_const(cdev->type); -out_ida_remove: - ida_free(&thermal_cdev_ida, cdev->id); -out_kfree_cdev: - kfree(cdev); - return ERR_PTR(ret); +out_put_device: + /* + * The device core will release the memory via + * thermal_release() after put_device() is called in the error + * path + */ + put_device(&cdev->device); + return ret; } /** * thermal_cooling_device_register() - register a new thermal cooling device * @type: the thermal cooling device type. * @devdata: device private data. - * @ops: standard thermal cooling devices callbacks. + * @ops: standard thermal cooling devices callbacks. * * This interface function adds a new thermal cooling device (fan/processor/...) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself @@ -1159,82 +1075,62 @@ struct thermal_cooling_device * thermal_cooling_device_register(const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { - return __thermal_cooling_device_register(NULL, type, devdata, ops); + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_cooling_device_alloc(type, ops); + if (IS_ERR(cdev)) + return cdev; + + ret = thermal_cooling_device_add(cdev, devdata); + if (ret) + return ERR_PTR(ret); + + return cdev; } EXPORT_SYMBOL_GPL(thermal_cooling_device_register); -/** - * thermal_of_cooling_device_register() - register an OF thermal cooling device - * @np: a pointer to a device tree node. - * @type: the thermal cooling device type. - * @devdata: device private data. - * @ops: standard thermal cooling devices callbacks. - * - * This function will register a cooling device with device tree node reference. - * This interface function adds a new thermal cooling device (fan/processor/...) - * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself - * to all the thermal zone devices registered at the same time. - * - * Return: a pointer to the created struct thermal_cooling_device or an - * ERR_PTR. Caller must check return value with IS_ERR*() helpers. - */ -struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) +static void thermal_cooling_device_release(void *data) { - return __thermal_cooling_device_register(np, type, devdata, ops); -} -EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); + struct thermal_cooling_device *cdev = data; -static void thermal_cooling_device_release(struct device *dev, void *res) -{ - thermal_cooling_device_unregister( - *(struct thermal_cooling_device **)res); + thermal_cooling_device_unregister(cdev); } /** - * devm_thermal_of_cooling_device_register() - register an OF thermal cooling - * device + * devm_thermal_cooling_device_register() - register a thermal cooling device + * * @dev: a valid struct device pointer of a sensor device. - * @np: a pointer to a device tree node. * @type: the thermal cooling device type. * @devdata: device private data. * @ops: standard thermal cooling devices callbacks. * - * This function will register a cooling device with device tree node reference. - * This interface function adds a new thermal cooling device (fan/processor/...) - * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself - * to all the thermal zone devices registered at the same time. + * This function will register a cooling device. This interface + * function adds a new thermal cooling device (fan/processor/...) to + * /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind + * itself to all the thermal zone devices registered at the same time. * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ struct thermal_cooling_device * -devm_thermal_of_cooling_device_register(struct device *dev, - struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) +devm_thermal_cooling_device_register(struct device *dev, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) { - struct thermal_cooling_device **ptr, *tcd; - - ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct thermal_cooling_device *cdev; + int ret; - tcd = __thermal_cooling_device_register(np, type, devdata, ops); - if (IS_ERR(tcd)) { - devres_free(ptr); - return tcd; - } + cdev = thermal_cooling_device_register(type, devdata, ops); + if (IS_ERR(cdev)) + return cdev; - *ptr = tcd; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, thermal_cooling_device_release, cdev); + if (ret) + return ERR_PTR(ret); - return tcd; + return cdev; } -EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); +EXPORT_SYMBOL_GPL(devm_thermal_cooling_device_register); static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev) { @@ -1470,6 +1366,17 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz) __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } +static void thermal_zone_device_release(struct device *dev) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + + thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); + ida_destroy(&tz->ida); + mutex_destroy(&tz->lock); + complete(&tz->removal); +} + /** * thermal_zone_device_register_with_trips() - register a new thermal zone device * @type: the thermal zone device type @@ -1540,7 +1447,7 @@ thermal_zone_device_register_with_trips(const char *type, if (polling_delay && passive_delay > polling_delay) return ERR_PTR(-EINVAL); - if (thermal_class_unavailable) + if (!thermal_class) return ERR_PTR(-ENODEV); tz = kzalloc_flex(*tz, trips, num_trips); @@ -1576,7 +1483,8 @@ thermal_zone_device_register_with_trips(const char *type, if (!tz->ops.critical) tz->ops.critical = thermal_zone_device_critical; - tz->device.class = &thermal_class; + tz->device.class = thermal_class; + tz->device.release = thermal_zone_device_release; tz->devdata = devdata; tz->num_trips = num_trips; for_each_trip_desc(tz, td) { @@ -1837,7 +1745,7 @@ static void __thermal_pm_prepare(void) void thermal_pm_prepare(void) { - if (thermal_class_unavailable) + if (!thermal_class) return; __thermal_pm_prepare(); @@ -1868,7 +1776,7 @@ void thermal_pm_complete(void) { struct thermal_zone_device *tz; - if (thermal_class_unavailable) + if (!thermal_class) return; guard(mutex)(&thermal_list_lock); @@ -1881,6 +1789,7 @@ void thermal_pm_complete(void) static int __init thermal_init(void) { + struct class *tc; int result; thermal_debug_init(); @@ -1889,7 +1798,7 @@ static int __init thermal_init(void) if (result) goto error; - thermal_wq = alloc_workqueue("thermal_events", WQ_POWER_EFFICIENT, 0); + thermal_wq = alloc_workqueue("thermal_events", WQ_UNBOUND, 0); if (!thermal_wq) { result = -ENOMEM; goto unregister_netlink; @@ -1897,19 +1806,19 @@ static int __init thermal_init(void) result = thermal_register_governors(); if (result) - goto destroy_workqueue; - - result = class_register(&thermal_class); - if (result) goto unregister_governors; - thermal_class_unavailable = false; + tc = class_create("thermal"); + if (IS_ERR(tc)) { + result = PTR_ERR(tc); + goto unregister_governors; + } + thermal_class = tc; return 0; unregister_governors: thermal_unregister_governors(); -destroy_workqueue: destroy_workqueue(thermal_wq); unregister_netlink: thermal_netlink_exit(); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index d3acff602f9c..e98b0aa5aacc 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -258,8 +258,6 @@ struct thermal_instance { #define to_cooling_device(_dev) \ container_of(_dev, struct thermal_cooling_device, device) -int thermal_register_governor(struct thermal_governor *); -void thermal_unregister_governor(struct thermal_governor *); int thermal_zone_device_set_policy(struct thermal_zone_device *, char *); int thermal_build_list_of_policies(char *buf); void __thermal_zone_device_update(struct thermal_zone_device *tz, @@ -269,6 +267,11 @@ void thermal_zone_device_critical_shutdown(struct thermal_zone_device *tz); void thermal_governor_update_tz(struct thermal_zone_device *tz, enum thermal_notify_event reason); +struct thermal_cooling_device * +thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_device_ops *ops); + +int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata); + /* Helpers */ #define for_each_trip_desc(__tz, __td) \ for (__td = __tz->trips; __td - __tz->trips < __tz->num_trips; __td++) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index b624892bc6d6..386dfb9f559e 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -19,27 +19,19 @@ #include "thermal_hwmon.h" #include "thermal_core.h" +/* + * Needs to be large enough to hold a thermal zone type string followed by an + * underline character and a 32-bit integer in decimal representation. + */ +#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11) + /* hwmon sys I/F */ /* thermal zone devices with the same type share one hwmon device */ struct thermal_hwmon_device { - char type[THERMAL_NAME_LENGTH]; + char name[THERMAL_HWMON_NAME_LENGTH]; struct device *device; - int count; - struct list_head tz_list; struct list_head node; -}; - -struct thermal_hwmon_attr { - struct device_attribute attr; - char name[16]; -}; - -/* one temperature input for each thermal zone */ -struct thermal_hwmon_temp { - struct list_head hwmon_node; struct thermal_zone_device *tz; - struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ - struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ }; static LIST_HEAD(thermal_hwmon_list); @@ -47,19 +39,14 @@ static LIST_HEAD(thermal_hwmon_list); static DEFINE_MUTEX(thermal_hwmon_list_lock); static ssize_t -temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) +temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev); + struct thermal_zone_device *tz = hwmon->tz; int temperature; int ret; - struct thermal_hwmon_attr *hwmon_attr - = container_of(attr, struct thermal_hwmon_attr, attr); - struct thermal_hwmon_temp *temp - = container_of(hwmon_attr, struct thermal_hwmon_temp, - temp_input); - struct thermal_zone_device *tz = temp->tz; ret = thermal_zone_get_temp(tz, &temperature); - if (ret) return ret; @@ -67,14 +54,10 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) +temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_hwmon_attr *hwmon_attr - = container_of(attr, struct thermal_hwmon_attr, attr); - struct thermal_hwmon_temp *temp - = container_of(hwmon_attr, struct thermal_hwmon_temp, - temp_crit); - struct thermal_zone_device *tz = temp->tz; + struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev); + struct thermal_zone_device *tz = hwmon->tz; int temperature; int ret; @@ -87,166 +70,97 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) return sysfs_emit(buf, "%d\n", temperature); } +static DEVICE_ATTR_RO(temp1_input); +static DEVICE_ATTR_RO(temp1_crit); -static struct thermal_hwmon_device * -thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) +static struct attribute *thermal_hwmon_attrs[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp1_crit.attr, + NULL, +}; + +static umode_t thermal_hwmon_attr_is_visible(struct kobject *kobj, + struct attribute *a, int n) { - struct thermal_hwmon_device *hwmon; - char type[THERMAL_NAME_LENGTH]; + if (a == &dev_attr_temp1_input.attr) + return a->mode; - mutex_lock(&thermal_hwmon_list_lock); - list_for_each_entry(hwmon, &thermal_hwmon_list, node) { - strscpy(type, tz->type); - strreplace(type, '-', '_'); - if (!strcmp(hwmon->type, type)) { - mutex_unlock(&thermal_hwmon_list_lock); - return hwmon; - } + if (a == &dev_attr_temp1_crit.attr) { + struct thermal_hwmon_device *hwmon = dev_get_drvdata(kobj_to_dev(kobj)); + struct thermal_zone_device *tz = hwmon->tz; + int dummy; + + if (tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &dummy)) + return a->mode; } - mutex_unlock(&thermal_hwmon_list_lock); - return NULL; + return 0; } -/* Find the temperature input matching a given thermal zone */ -static struct thermal_hwmon_temp * -thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon, - const struct thermal_zone_device *tz) -{ - struct thermal_hwmon_temp *temp; - - mutex_lock(&thermal_hwmon_list_lock); - list_for_each_entry(temp, &hwmon->tz_list, hwmon_node) - if (temp->tz == tz) { - mutex_unlock(&thermal_hwmon_list_lock); - return temp; - } - mutex_unlock(&thermal_hwmon_list_lock); - - return NULL; -} +static const struct attribute_group thermal_hwmon_group = { + .attrs = thermal_hwmon_attrs, + .is_visible = thermal_hwmon_attr_is_visible, +}; -static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz) -{ - int temp; - return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp); -} +__ATTRIBUTE_GROUPS(thermal_hwmon); int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; - struct thermal_hwmon_temp *temp; - int new_hwmon_device = 1; - int result; - - hwmon = thermal_hwmon_lookup_by_type(tz); - if (hwmon) { - new_hwmon_device = 0; - goto register_sys_interface; - } hwmon = kzalloc_obj(*hwmon); if (!hwmon) return -ENOMEM; - INIT_LIST_HEAD(&hwmon->tz_list); - strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH); - strreplace(hwmon->type, '-', '_'); + hwmon->tz = tz; + /* + * Append the thermal zone ID preceded by an underline character to the + * type to disambiguate the sensors command output. + */ + scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id); + strreplace(hwmon->name, '-', '_'); hwmon->device = hwmon_device_register_for_thermal(&tz->device, - hwmon->type, hwmon); + hwmon->name, hwmon, + thermal_hwmon_groups); if (IS_ERR(hwmon->device)) { - result = PTR_ERR(hwmon->device); - goto free_mem; - } - - register_sys_interface: - temp = kzalloc_obj(*temp); - if (!temp) { - result = -ENOMEM; - goto unregister_name; - } + int result = PTR_ERR(hwmon->device); - temp->tz = tz; - hwmon->count++; - - snprintf(temp->temp_input.name, sizeof(temp->temp_input.name), - "temp%d_input", hwmon->count); - temp->temp_input.attr.attr.name = temp->temp_input.name; - temp->temp_input.attr.attr.mode = 0444; - temp->temp_input.attr.show = temp_input_show; - sysfs_attr_init(&temp->temp_input.attr.attr); - result = device_create_file(hwmon->device, &temp->temp_input.attr); - if (result) - goto free_temp_mem; - - if (thermal_zone_crit_temp_valid(tz)) { - snprintf(temp->temp_crit.name, - sizeof(temp->temp_crit.name), - "temp%d_crit", hwmon->count); - temp->temp_crit.attr.attr.name = temp->temp_crit.name; - temp->temp_crit.attr.attr.mode = 0444; - temp->temp_crit.attr.show = temp_crit_show; - sysfs_attr_init(&temp->temp_crit.attr.attr); - result = device_create_file(hwmon->device, - &temp->temp_crit.attr); - if (result) - goto unregister_input; + kfree(hwmon); + return result; } + /* The list is needed for hwmon lookup during removal. */ mutex_lock(&thermal_hwmon_list_lock); - if (new_hwmon_device) - list_add_tail(&hwmon->node, &thermal_hwmon_list); - list_add_tail(&temp->hwmon_node, &hwmon->tz_list); + list_add_tail(&hwmon->node, &thermal_hwmon_list); mutex_unlock(&thermal_hwmon_list_lock); return 0; - - unregister_input: - device_remove_file(hwmon->device, &temp->temp_input.attr); - free_temp_mem: - kfree(temp); - unregister_name: - if (new_hwmon_device) - hwmon_device_unregister(hwmon->device); - free_mem: - kfree(hwmon); - - return result; } EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs); -void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) +static struct thermal_hwmon_device * +thermal_hwmon_lookup(const struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; - struct thermal_hwmon_temp *temp; - hwmon = thermal_hwmon_lookup_by_type(tz); - if (unlikely(!hwmon)) { - /* Should never happen... */ - dev_dbg(&tz->device, "hwmon device lookup failed!\n"); - return; + list_for_each_entry(hwmon, &thermal_hwmon_list, node) { + if (hwmon->tz == tz) + return hwmon; } + return NULL; +} - temp = thermal_hwmon_lookup_temp(hwmon, tz); - if (unlikely(!temp)) { - /* Should never happen... */ - dev_dbg(&tz->device, "temperature input lookup failed!\n"); - return; - } +void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) +{ + struct thermal_hwmon_device *hwmon; - device_remove_file(hwmon->device, &temp->temp_input.attr); - if (thermal_zone_crit_temp_valid(tz)) - device_remove_file(hwmon->device, &temp->temp_crit.attr); + scoped_guard(mutex, &thermal_hwmon_list_lock) { + hwmon = thermal_hwmon_lookup(tz); + if (!hwmon) + return; - mutex_lock(&thermal_hwmon_list_lock); - list_del(&temp->hwmon_node); - kfree(temp); - if (!list_empty(&hwmon->tz_list)) { - mutex_unlock(&thermal_hwmon_list_lock); - return; + list_del(&hwmon->node); } - list_del(&hwmon->node); - mutex_unlock(&thermal_hwmon_list_lock); hwmon_device_unregister(hwmon->device); kfree(hwmon); diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 99085c806a1f..100fd8a0c8ce 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -98,7 +98,7 @@ static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n int ret, count; *ntrips = 0; - + struct device_node *trips __free(device_node) = of_get_child_by_name(np, "trips"); if (!trips) return NULL; @@ -259,16 +259,34 @@ static bool thermal_of_get_cooling_spec(struct device_node *map_np, int index, of_node_put(cooling_spec.np); - if (cooling_spec.args_count < 2) { - pr_err("wrong reference to cooling device, missing limits\n"); + /* + * There are two formats: + * - Legacy format : <&cdev lower upper> + * - New format : <&cdev cdev_id lower upper> + * + * With the new format, along with the device node pointer, + * the cdev_id must match with the cooling device cdev_id in + * order to bind + */ + if (cooling_spec.args_count < 2 || cooling_spec.args_count > 3) { + pr_err("Invalid number of cooling device parameters\n"); return false; } if (cooling_spec.np != cdev->np) return false; - c->lower = cooling_spec.args[0]; - c->upper = cooling_spec.args[1]; + if (cooling_spec.args_count == 3 && + cooling_spec.args[0] != cdev->cdev_id) + return false; + + if (cooling_spec.args_count != 3) { + c->lower = cooling_spec.args[0]; + c->upper = cooling_spec.args[1]; + } else { + c->lower = cooling_spec.args[1]; + c->upper = cooling_spec.args[2]; + } c->weight = weight; return true; @@ -494,7 +512,7 @@ EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register); /** * devm_thermal_of_zone_unregister - Resource managed version of * thermal_of_zone_unregister(). - * @dev: Device for which which resource was allocated. + * @dev: Device for which resource was allocated. * @tz: a pointer to struct thermal_zone where the sensor is registered. * * This function removes the sensor callbacks and private data from the @@ -510,3 +528,125 @@ void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_dev devm_thermal_of_zone_match, tz)); } EXPORT_SYMBOL_GPL(devm_thermal_of_zone_unregister); + +/** + * thermal_of_cooling_device_register() - register an OF thermal cooling device + * @np: a pointer to a device tree node. + * @cdev_id: a cooling device id in the cooling controller + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * It also gives the opportunity to link the cooling device to a device tree + * node, so that it can be bound to a thermal zone created out of device tree. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, u32 cdev_id, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_cooling_device_alloc(type, ops); + if (IS_ERR(cdev)) + return cdev; + + cdev->np = np; + cdev->cdev_id = cdev_id; + + ret = thermal_cooling_device_add(cdev, devdata); + if (ret) + return ERR_PTR(ret); + + return cdev; +} +EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); + +static void thermal_of_cooling_device_release(void *data) +{ + struct thermal_cooling_device *cdev = data; + + thermal_cooling_device_unregister(cdev); +} + +static struct thermal_cooling_device * +__devm_thermal_of_cooling_device_register(struct device *dev, struct device_node *np, + u32 cdev_id, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_of_cooling_device_register(np, cdev_id, type, devdata, ops); + if (IS_ERR(cdev)) + return cdev; + + ret = devm_add_action_or_reset(dev, thermal_of_cooling_device_release, cdev); + if (ret) + return ERR_PTR(ret); + + return cdev; +} + +/** + * devm_thermal_of_cooling_device_register() - register an OF thermal cooling device + * @dev: a valid struct device pointer of a sensor device. + * @cdev_id: a cooling device index in the cooling controller + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This function will register a cooling device with device tree node reference. + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, u32 cdev_id, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return __devm_thermal_of_cooling_device_register(dev, dev->of_node, cdev_id, + type, devdata, ops); +} +EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); + +/** + * devm_thermal_of_child_cooling_device_register() - register an OF thermal cooling + * device + * @dev: a valid struct device pointer of a sensor device. + * @np: a pointer to a device tree node. + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This function will register a cooling device with device tree node reference. + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * + * This function should be used when a cooling controller has child + * nodes which are referenced in the thermal zone cooling map. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +devm_thermal_of_child_cooling_device_register(struct device *dev, + struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return __devm_thermal_of_cooling_device_register(dev, np, 0, type, devdata, ops); +} +EXPORT_SYMBOL_GPL(devm_thermal_of_child_cooling_device_register); diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 5eecae13f07d..b44abfc997ed 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -82,7 +82,7 @@ mode_store(struct device *dev, struct device_attribute *attr, } #define thermal_trip_of_attr(_ptr_, _attr_) \ - ({ \ + ({ \ struct thermal_trip_desc *td; \ \ td = container_of(_ptr_, struct thermal_trip_desc, \ @@ -536,11 +536,9 @@ cur_state_store(struct device *dev, struct device_attribute *attr, unsigned long state; int result; - if (sscanf(buf, "%ld\n", &state) != 1) - return -EINVAL; - - if ((long)state < 0) - return -EINVAL; + result = kstrtoul(buf, 10, &state); + if (result < 0) + return result; /* Requested state should be less than max_state + 1 */ if (state > cdev->max_state) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 50cbfc92fe65..59beab43f90a 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -8,6 +8,7 @@ */ #include <linux/err.h> +#include <linux/overflow.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/uuid.h> @@ -34,10 +35,11 @@ struct tb_property_dir_entry { }; #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 +#define TB_PROPERTY_MAX_DEPTH 8 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, size_t block_len, unsigned int dir_offset, size_t dir_len, - bool is_root); + bool is_root, unsigned int depth); static inline void parse_dwdata(void *dst, const void *src, size_t dwords) { @@ -52,13 +54,18 @@ static inline void format_dwdata(void *dst, const void *src, size_t dwords) static bool tb_property_entry_valid(const struct tb_property_entry *entry, size_t block_len) { + u32 end; + switch (entry->type) { case TB_PROPERTY_TYPE_DIRECTORY: case TB_PROPERTY_TYPE_DATA: case TB_PROPERTY_TYPE_TEXT: + if (!entry->length) + return false; if (entry->length > block_len) return false; - if (entry->value + entry->length > block_len) + if (check_add_overflow(entry->value, entry->length, &end) || + end > block_len) return false; break; @@ -93,7 +100,8 @@ tb_property_alloc(const char *key, enum tb_property_type type) } static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, - const struct tb_property_entry *entry) + const struct tb_property_entry *entry, + unsigned int depth) { char key[TB_PROPERTY_KEY_SIZE + 1]; struct tb_property *property; @@ -114,7 +122,7 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, switch (property->type) { case TB_PROPERTY_TYPE_DIRECTORY: dir = __tb_property_parse_dir(block, block_len, entry->value, - entry->length, false); + entry->length, false, depth + 1); if (!dir) { kfree(property); return NULL; @@ -159,21 +167,35 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, } static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, - size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) + size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root, + unsigned int depth) { const struct tb_property_entry *entries; size_t i, content_len, nentries; unsigned int content_offset; struct tb_property_dir *dir; + if (depth > TB_PROPERTY_MAX_DEPTH) + return NULL; + dir = kzalloc_obj(*dir); if (!dir) return NULL; + INIT_LIST_HEAD(&dir->properties); + if (is_root) { content_offset = dir_offset + 2; content_len = dir_len; + if (content_offset + content_len > block_len) { + tb_property_free_dir(dir); + return NULL; + } } else { + if (dir_len < 4) { + tb_property_free_dir(dir); + return NULL; + } dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid), GFP_KERNEL); if (!dir->uuid) { @@ -187,12 +209,10 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, entries = (const struct tb_property_entry *)&block[content_offset]; nentries = content_len / (sizeof(*entries) / 4); - INIT_LIST_HEAD(&dir->properties); - for (i = 0; i < nentries; i++) { struct tb_property *property; - property = tb_property_parse(block, block_len, &entries[i]); + property = tb_property_parse(block, block_len, &entries[i], depth); if (!property) { tb_property_free_dir(dir); return NULL; @@ -231,7 +251,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block, return NULL; return __tb_property_parse_dir(block, block_len, 0, rootdir->length, - true); + true, 0); } /** diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 754808c43f00..1fd1cf4295a2 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -55,6 +55,7 @@ static const char * const state_names[] = { struct xdomain_request_work { struct work_struct work; struct tb_xdp_header *pkg; + size_t pkg_len; struct tb *tb; }; @@ -122,7 +123,9 @@ static bool tb_xdomain_match(const struct tb_cfg_request *req, static bool tb_xdomain_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) { - memcpy(req->response, pkg->buffer, req->response_size); + size_t len = min_t(size_t, pkg->frame.size, req->response_size); + + memcpy(req->response, pkg->buffer, len); req->result.err = 0; return true; } @@ -393,6 +396,8 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route, } } + if (req.offset + len > data_len) + len = data_len - req.offset; memcpy(data + req.offset, res->data, len * 4); req.offset += len; } while (!data_len || req.offset < data_len); @@ -731,6 +736,7 @@ static void tb_xdp_handle_request(struct work_struct *work) struct xdomain_request_work *xw = container_of(work, typeof(*xw), work); const struct tb_xdp_header *pkg = xw->pkg; const struct tb_xdomain_header *xhdr = &pkg->xd_hdr; + size_t pkg_len = xw->pkg_len; struct tb *tb = xw->tb; struct tb_ctl *ctl = tb->ctl; struct tb_xdomain *xd; @@ -762,7 +768,7 @@ static void tb_xdp_handle_request(struct work_struct *work) switch (pkg->type) { case PROPERTIES_REQUEST: tb_dbg(tb, "%llx: received XDomain properties request\n", route); - if (xd) { + if (xd && pkg_len >= sizeof(struct tb_xdp_properties)) { ret = tb_xdp_properties_response(tb, ctl, xd, sequence, (const struct tb_xdp_properties *)pkg); } @@ -816,7 +822,8 @@ static void tb_xdp_handle_request(struct work_struct *work) tb_dbg(tb, "%llx: received XDomain link state change request\n", route); - if (xd && xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH) { + if (xd && xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH && + pkg_len >= sizeof(struct tb_xdp_link_state_change)) { const struct tb_xdp_link_state_change *lsc = (const struct tb_xdp_link_state_change *)pkg; @@ -868,6 +875,7 @@ tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr, kfree(xw); return false; } + xw->pkg_len = size; xw->tb = tb_domain_get(tb); schedule_work(&xw->work); diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 37db8a3e5158..d29a86d161f6 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -1060,7 +1060,7 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) INIT_DELAYED_WORK(&priv->sndbuf_work, hvc_iucv_sndbuf_work); init_waitqueue_head(&priv->sndbuf_waitq); - priv->sndbuf = (void *) get_zeroed_page(GFP_KERNEL); + priv->sndbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!priv->sndbuf) { kfree(priv); return -ENOMEM; @@ -1103,7 +1103,7 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) out_error_dev: hvc_remove(priv->hvc); out_error_hvc: - free_page((unsigned long) priv->sndbuf); + kfree(priv->sndbuf); kfree(priv); return rc; @@ -1116,7 +1116,7 @@ static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv) { hvc_remove(priv->hvc); device_unregister(priv->dev); - free_page((unsigned long) priv->sndbuf); + kfree(priv->sndbuf); kfree(priv); } diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 94beadb4024d..2af0c4d0ad82 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -427,7 +427,7 @@ static int dw8250_handle_irq(struct uart_port *p) unsigned int quirks = d->pdata->quirks; unsigned int status; - guard(uart_port_lock_irqsave)(p); + guard(uart_port_lock_check_sysrq_irqsave)(p); switch (FIELD_GET(DW_UART_IIR_IID, iir)) { case UART_IIR_NO_INT: diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index af78cc02f38e..c66ba714caa5 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1784,7 +1784,10 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) } /* - * Context: port's lock must be held by the caller. + * Context: port's lock must be held by the caller. The caller must + * release it via guard(uart_port_lock_check_sysrq_irqsave) or + * uart_unlock_and_check_sysrq_irqrestore(), which captures SysRq + * character on unlock. */ void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir) { @@ -1837,7 +1840,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if (iir & UART_IIR_NO_INT) return 0; - guard(uart_port_lock_irqsave)(port); + guard(uart_port_lock_check_sysrq_irqsave)(port); serial8250_handle_irq_locked(port, iir); return 1; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 9aa61c93d7bc..ec284aceb909 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -334,7 +334,7 @@ config SERIAL_MAX310X Say Y here if you want to support this ICs. config SERIAL_DZ - bool "DECstation DZ serial driver" + tristate "DECstation DZ serial driver" depends on MACH_DECSTATION && 32BIT select SERIAL_CORE default y diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d47a62d1c9f7..20f079fe11d8 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -379,6 +379,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev) struct resource *res_mem; int i = pdev->id; int irq; + int ret; /* -1 emphasizes that the platform must have one port, no .N suffix */ if (i == -1) @@ -418,7 +419,11 @@ static int altera_jtaguart_probe(struct platform_device *pdev) port->flags = UPF_BOOT_AUTOCONF; port->dev = &pdev->dev; - uart_add_one_port(&altera_jtaguart_driver, port); + ret = uart_add_one_port(&altera_jtaguart_driver, port); + if (ret) { + iounmap(port->membase); + return ret; + } return 0; } diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index e53c54353c3e..39d93e9c2d15 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -40,6 +40,7 @@ #include <linux/kernel.h> #include <linux/major.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/sysrq.h> @@ -48,14 +49,6 @@ #include <linux/atomic.h> #include <linux/io.h> -#include <asm/bootinfo.h> - -#include <asm/dec/interrupts.h> -#include <asm/dec/kn01.h> -#include <asm/dec/kn02.h> -#include <asm/dec/machtype.h> -#include <asm/dec/prom.h> -#include <asm/dec/system.h> #include "dz.h" @@ -65,7 +58,9 @@ MODULE_LICENSE("GPL"); static char dz_name[] __initdata = "DECstation DZ serial driver version "; -static char dz_version[] __initdata = "1.04"; +static char dz_version[] __initdata = "1.05"; + +#define DZ_IO_SIZE 0x20 /* IOMEM space size. */ struct dz_port { struct dz_mux *mux; @@ -81,6 +76,7 @@ struct dz_mux { }; static struct dz_mux dz_mux; +static struct uart_driver dz_reg; static inline struct dz_port *to_dport(struct uart_port *uport) { @@ -542,14 +538,47 @@ static int dz_encode_baud_rate(unsigned int baud) static void dz_reset(struct dz_port *dport) { struct dz_mux *mux = dport->mux; + unsigned short tcr; + int loops = 10000; if (mux->initialised) return; + tcr = dz_in(dport, DZ_TCR); + + /* Do not disturb any ongoing transmissions. */ + if (dz_in(dport, DZ_CSR) & DZ_MSE) { + unsigned short csr, mask; + + mask = tcr; + while ((mask & DZ_LNENB) && loops--) { + csr = dz_in(dport, DZ_CSR); + if (!(csr & DZ_TRDY)) + continue; + mask &= ~(1 << ((csr & DZ_TLINE) >> 8)); + dz_out(dport, DZ_TCR, mask); + iob(); + udelay(2); /* 1.4us TRDY recovery. */ + } + fsleep(1200); /* Transmitter drain. */ + } + dz_out(dport, DZ_CSR, DZ_CLR); while (dz_in(dport, DZ_CSR) & DZ_CLR); iob(); + /* + * Set parameters across all lines such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to produce rubbish. + */ + for (int line = 0; line < DZ_NB_PORT; line++) + dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); + + /* Re-enable transmission for the initial PROM-based console. */ + dz_out(dport, DZ_TCR, tcr); + /* Enable scanning. */ dz_out(dport, DZ_CSR, DZ_MSE); @@ -633,26 +662,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, uart_port_unlock_irqrestore(&dport->port, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void dz_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - - uart_port_lock_irqsave(&dport->port, &flags); - if (state < 3) - dz_start_tx(&dport->port); - else - dz_stop_tx(&dport->port); - uart_port_unlock_irqrestore(&dport->port, flags); -} - - static const char *dz_type(struct uart_port *uport) { return "DZ"; @@ -668,14 +677,13 @@ static void dz_release_port(struct uart_port *uport) map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); } static int dz_map_port(struct uart_port *uport) { if (!uport->membase) - uport->membase = ioremap(uport->mapbase, - dec_kn_slot_size); + uport->membase = ioremap(uport->mapbase, DZ_IO_SIZE); if (!uport->membase) { printk(KERN_ERR "dz: Cannot map MMIO\n"); return -ENOMEM; @@ -691,8 +699,7 @@ static int dz_request_port(struct uart_port *uport) map_guard = atomic_add_return(1, &mux->map_guard); if (map_guard == 1) { - if (!request_mem_region(uport->mapbase, dec_kn_slot_size, - "dz")) { + if (!request_mem_region(uport->mapbase, DZ_IO_SIZE, "dz")) { atomic_add(-1, &mux->map_guard); printk(KERN_ERR "dz: Unable to reserve MMIO resource\n"); @@ -703,7 +710,7 @@ static int dz_request_port(struct uart_port *uport) if (ret) { map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); return ret; } return 0; @@ -748,7 +755,6 @@ static const struct uart_ops dz_ops = { .startup = dz_startup, .shutdown = dz_shutdown, .set_termios = dz_set_termios, - .pm = dz_pm, .type = dz_type, .release_port = dz_release_port, .request_port = dz_request_port, @@ -756,20 +762,15 @@ static const struct uart_ops dz_ops = { .verify_port = dz_verify_port, }; -static void __init dz_init_ports(void) +static int __init dz_probe(struct platform_device *pdev) { - static int first = 1; - unsigned long base; + struct resource *mem_resource, *irq_resource; int line; - if (!first) - return; - first = 0; - - if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) - base = dec_kn_slot_base + KN01_DZ11; - else - base = dec_kn_slot_base + KN02_DZ11; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; for (line = 0; line < DZ_NB_PORT; line++) { struct dz_port *dport = &dz_mux.dport[line]; @@ -777,14 +778,33 @@ static void __init dz_init_ports(void) dport->mux = &dz_mux; - uport->irq = dec_interrupt[DEC_IRQ_DZ11]; + uport->dev = &pdev->dev; + uport->irq = irq_resource->start; uport->fifosize = 1; uport->iotype = UPIO_MEM; uport->flags = UPF_BOOT_AUTOCONF; uport->ops = &dz_ops; uport->line = line; - uport->mapbase = base; + uport->mapbase = mem_resource->start; uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE); + + if (uart_add_one_port(&dz_reg, uport)) + uport->dev = NULL; + } + + return 0; +} + +static void __exit dz_remove(struct platform_device *pdev) +{ + int line; + + for (line = DZ_NB_PORT - 1; line >= 0; line--) { + struct dz_port *dport = &dz_mux.dport[line]; + struct uart_port *uport = &dport->port; + + if (uport->dev) + uart_remove_one_port(&dz_reg, uport); } } @@ -867,24 +887,14 @@ static int __init dz_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = dz_map_port(uport); - if (ret) - return ret; - - spin_lock_init(&dport->port.lock); /* For dz_pm(). */ - - dz_reset(dport); - dz_pm(uport, 0, -1); + if (!dport->mux) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&dport->port, co, baud, parity, bits, flow); + return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver dz_reg; static struct console dz_console = { .name = "ttyS", .write = dz_console_print, @@ -895,18 +905,6 @@ static struct console dz_console = { .data = &dz_reg, }; -static int __init dz_serial_console_init(void) -{ - if (!IOASIC) { - dz_init_ports(); - register_console(&dz_console); - return 0; - } else - return -ENXIO; -} - -console_initcall(dz_serial_console_init); - #define SERIAL_DZ_CONSOLE &dz_console #else #define SERIAL_DZ_CONSOLE NULL @@ -922,25 +920,32 @@ static struct uart_driver dz_reg = { .cons = SERIAL_DZ_CONSOLE, }; +static struct platform_driver dz_driver = { + .remove = __exit_p(dz_remove), + .driver = { .name = "dz" }, +}; + static int __init dz_init(void) { - int ret, i; - - if (IOASIC) - return -ENXIO; + int ret; printk("%s%s\n", dz_name, dz_version); - dz_init_ports(); - ret = uart_register_driver(&dz_reg); if (ret) return ret; + ret = platform_driver_probe(&dz_driver, dz_probe); + if (ret) + uart_unregister_driver(&dz_reg); - for (i = 0; i < DZ_NB_PORT; i++) - uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); + return ret; +} - return 0; +static void __exit dz_exit(void) +{ + platform_driver_unregister(&dz_driver); + uart_unregister_driver(&dz_reg); } module_init(dz_init); +module_exit(dz_exit); diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 1bd7ec9c81ea..b7919c05f0fb 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1379,7 +1379,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (!nent) { dev_err(sport->port.dev, "DMA Rx mapping error\n"); - return -EINVAL; + ret = -EINVAL; + goto err_free_buf; } dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport); @@ -1391,7 +1392,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (ret < 0) { dev_err(sport->port.dev, "DMA Rx slave config failed, err = %d\n", ret); - return ret; + goto err_unmap_sg; } sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan, @@ -1402,7 +1403,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) DMA_PREP_INTERRUPT); if (!sport->dma_rx_desc) { dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n"); - return -EFAULT; + ret = -ENOMEM; + goto err_unmap_sg; } sport->dma_rx_desc->callback = lpuart_dma_rx_complete; @@ -1426,6 +1428,13 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) } return 0; + +err_unmap_sg: + dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); +err_free_buf: + kfree(ring->buf); + ring->buf = NULL; + return ret; } static void lpuart_dma_rx_free(struct uart_port *port) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 6729d8e83c3c..ba1fcd663fe2 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -689,8 +689,7 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n", __func__); - pci_dev_put(dma_dev); - return; + goto err_pci_get; } priv->chan_tx = chan; @@ -704,18 +703,26 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n", __func__); - dma_release_channel(priv->chan_tx); - priv->chan_tx = NULL; - pci_dev_put(dma_dev); - return; + goto err_req_tx; } /* Get Consistent memory for DMA */ priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize, &priv->rx_buf_dma, GFP_KERNEL); + if (!priv->rx_buf_virt) + goto err_req_rx; priv->chan_rx = chan; pci_dev_put(dma_dev); + return; + +err_req_rx: + dma_release_channel(chan); +err_req_tx: + dma_release_channel(priv->chan_tx); + priv->chan_tx = NULL; +err_pci_get: + pci_dev_put(dma_dev); } static void pch_dma_rx_complete(void *arg) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index b365dd5da3cb..17da115b1e78 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -50,7 +50,7 @@ #define TX_STOP_BIT_LEN_2 2 /* SE_UART_RX_TRANS_CFG */ -#define UART_RX_PAR_EN BIT(3) +#define UART_RX_PAR_EN BIT(4) /* SE_UART_RX_WORD_LEN */ #define RX_WORD_LEN_MASK GENMASK(9, 0) @@ -1031,8 +1031,20 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; + unsigned int fifo_len = kfifo_len(&tport->xmit_fifo); + + /* + * Only advance the kfifo if it still contains the bytes that were + * transferred. uart_flush_buffer() may have run before this IRQ + * fired: it calls kfifo_reset() under the port lock, making + * fifo_len = 0 while tx_remaining remains non-zero. Calling + * uart_xmit_advance() in that case would underflow kfifo->out past + * kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining + * and triggering a spurious large DMA transfer of stale data. + */ + if (fifo_len >= port->tx_remaining) + uart_xmit_advance(uport, port->tx_remaining); - uart_xmit_advance(uport, port->tx_remaining); geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; port->tx_remaining = 0; diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index e27806bf2cf3..17cd5bb100b1 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -245,12 +245,9 @@ static bool s3c24xx_serial_txempty_nofifo(const struct uart_port *port) static void s3c24xx_serial_rx_enable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; int count = 10000; u32 ucon, ufcon; - uart_port_lock_irqsave(port, &flags); - while (--count && !s3c24xx_serial_txempty_nofifo(port)) udelay(100); @@ -263,23 +260,18 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 1; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_rx_disable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; u32 ucon; - uart_port_lock_irqsave(port, &flags); - ucon = rd_regl(port, S3C2410_UCON); ucon &= ~S3C2410_UCON_RXIRQMODE; wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 0; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_stop_tx(struct uart_port *port) diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index a12935f6b992..5f23284a8778 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -74,7 +74,7 @@ static int serial_base_device_init(struct uart_port *port, dev->parent = parent_dev; dev->bus = &serial_base_bus_type; dev->release = release; - dev->of_node_reused = true; + dev_set_of_node_reused(dev); device_set_node(dev, fwnode_handle_get(dev_fwnode(parent_dev))); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6c819b6b2425..54db019a5bfc 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3025,7 +3025,7 @@ int sci_request_port(struct uart_port *port) ret = sci_remap_port(port); if (unlikely(ret != 0)) { - release_resource(res); + release_mem_region(port->mapbase, sport->reg_size); return ret; } diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 72a3c0d90f40..8f92b4129a38 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -56,6 +56,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/major.h> +#include <linux/platform_device.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/spinlock.h> @@ -66,10 +67,6 @@ #include <linux/atomic.h> -#include <asm/dec/interrupts.h> -#include <asm/dec/ioasic_addrs.h> -#include <asm/dec/system.h> - #include "zs.h" @@ -79,7 +76,7 @@ MODULE_LICENSE("GPL"); static char zs_name[] __initdata = "DECstation Z85C30 serial driver version "; -static char zs_version[] __initdata = "0.10"; +static char zs_version[] __initdata = "0.11"; /* * It would be nice to dynamically allocate everything that @@ -98,25 +95,27 @@ static char zs_version[] __initdata = "0.10"; #define to_zport(uport) container_of(uport, struct zs_port, port) -struct zs_parms { - resource_size_t scc[ZS_NUM_SCCS]; - int irq[ZS_NUM_SCCS]; -}; - static struct zs_scc zs_sccs[ZS_NUM_SCCS]; +static struct uart_driver zs_reg; +/* + * Set parameters in WR5, WR12, WR13 such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to hang (TxENAB) or produce rubbish (Tx8, B9600). + */ static u8 zs_init_regs[ZS_NUM_REGS] __initdata = { 0, /* write 0 */ PAR_SPEC, /* write 1 */ 0, /* write 2 */ 0, /* write 3 */ X16CLK | SB1, /* write 4 */ - 0, /* write 5 */ + Tx8 | TxENAB, /* write 5 */ 0, 0, 0, /* write 6, 7, 8 */ MIE | DLC | NV, /* write 9 */ NRZ, /* write 10 */ TCBR | RCBR, /* write 11 */ - 0, 0, /* BRG time constant, write 12 + 13 */ + 0x16, 0x00, /* BRG time constant, write 12 + 13 */ BRSRC | BRENABL, /* write 14 */ 0, /* write 15 */ }; @@ -680,9 +679,9 @@ static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) uart_handle_dcd_change(uport, zport->mctrl & TIOCM_CAR); if (delta & TIOCM_RNG) - uport->icount.dsr++; - if (delta & TIOCM_DSR) uport->icount.rng++; + if (delta & TIOCM_DSR) + uport->icount.dsr++; if (delta) wake_up_interruptible(&uport->state->port.delta_msr_wait); @@ -826,22 +825,22 @@ static void zs_shutdown(struct uart_port *uport) static void zs_reset(struct zs_port *zport) { + struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; struct zs_scc *scc = zport->scc; int irq; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); - if (!scc->initialised) { - /* Reset the pointer first, just in case... */ - read_zsreg(zport, R0); - /* And let the current transmission finish. */ - zs_line_drain(zport, irq); - write_zsreg(zport, R9, FHWRES); - udelay(10); - write_zsreg(zport, R9, 0); - scc->initialised = 1; - } + + /* Reset the pointer first, just in case... */ + read_zsreg(zport, R0); + /* And let the current transmission finish. */ + zs_line_drain(zport, irq); + write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); + udelay(10); + write_zsreg(zport, R9, 0); + load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); } @@ -956,23 +955,6 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, spin_unlock_irqrestore(&scc->zlock, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void zs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct zs_port *zport = to_zport(uport); - - if (state < 3) - zport->regs[5] |= TxENAB; - else - zport->regs[5] &= ~TxENAB; - write_zsreg(zport, R5, zport->regs[5]); -} - static const char *zs_type(struct uart_port *uport) { @@ -1055,7 +1037,6 @@ static const struct uart_ops zs_ops = { .startup = zs_startup, .shutdown = zs_shutdown, .set_termios = zs_set_termios, - .pm = zs_pm, .type = zs_type, .release_port = zs_release_port, .request_port = zs_request_port, @@ -1066,63 +1047,62 @@ static const struct uart_ops zs_ops = { /* * Initialize Z85C30 port structures. */ -static int __init zs_probe_sccs(void) +static int __init zs_probe(struct platform_device *pdev) { - static int probed; - struct zs_parms zs_parms; - int chip, side, irq; - int n_chips = 0; + struct resource *mem_resource, *irq_resource; + int chip, side; int i; - if (probed) - return 0; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; - irq = dec_interrupt[DEC_IRQ_SCC0]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC0; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; - n_chips++; - } - irq = dec_interrupt[DEC_IRQ_SCC1]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC1; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; - n_chips++; - } - if (!n_chips) - return -ENXIO; - - probed = 1; - - for (chip = 0; chip < n_chips; chip++) { - spin_lock_init(&zs_sccs[chip].zlock); - for (side = 0; side < ZS_NUM_CHAN; side++) { - struct zs_port *zport = &zs_sccs[chip].zport[side]; - struct uart_port *uport = &zport->port; - - zport->scc = &zs_sccs[chip]; - zport->clk_mode = 16; - - uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); - uport->irq = zs_parms.irq[chip]; - uport->uartclk = ZS_CLOCK; - uport->fifosize = 1; - uport->iotype = UPIO_MEM; - uport->flags = UPF_BOOT_AUTOCONF; - uport->ops = &zs_ops; - uport->line = chip * ZS_NUM_CHAN + side; - uport->mapbase = dec_kn_slot_base + - zs_parms.scc[chip] + - (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; - - for (i = 0; i < ZS_NUM_REGS; i++) - zport->regs[i] = zs_init_regs[i]; - } + chip = pdev->id; + spin_lock_init(&zs_sccs[chip].zlock); + for (side = 0; side < ZS_NUM_CHAN; side++) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + + zport->scc = &zs_sccs[chip]; + zport->clk_mode = 16; + + uport->dev = &pdev->dev; + uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); + uport->irq = irq_resource->start; + uport->uartclk = ZS_CLOCK; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &zs_ops; + uport->line = chip * ZS_NUM_CHAN + side; + uport->mapbase = mem_resource->start + + (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; + + for (i = 0; i < ZS_NUM_REGS; i++) + zport->regs[i] = zs_init_regs[i]; + + if (uart_add_one_port(&zs_reg, uport)) + uport->dev = NULL; } return 0; } +static void __exit zs_remove(struct platform_device *pdev) +{ + int chip, side; + + chip = pdev->id; + for (side = ZS_NUM_CHAN - 1; side >= 0; side--) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + + if (uport->dev) + uart_remove_one_port(&zs_reg, uport); + } +} + #ifdef CONFIG_SERIAL_ZS_CONSOLE static void zs_console_putchar(struct uart_port *uport, unsigned char ch) @@ -1203,21 +1183,14 @@ static int __init zs_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = zs_map_port(uport); - if (ret) - return ret; - - zs_reset(zport); - zs_pm(uport, 0, -1); + if (!zport->scc) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver zs_reg; static struct console zs_console = { .name = "ttyS", .write = zs_console_write, @@ -1228,23 +1201,6 @@ static struct console zs_console = { .data = &zs_reg, }; -/* - * Register console. - */ -static int __init zs_serial_console_init(void) -{ - int ret; - - ret = zs_probe_sccs(); - if (ret) - return ret; - register_console(&zs_console); - - return 0; -} - -console_initcall(zs_serial_console_init); - #define SERIAL_ZS_CONSOLE &zs_console #else #define SERIAL_ZS_CONSOLE NULL @@ -1260,47 +1216,31 @@ static struct uart_driver zs_reg = { .cons = SERIAL_ZS_CONSOLE, }; +static struct platform_driver zs_driver = { + .remove = __exit_p(zs_remove), + .driver = { .name = "zs" }, +}; + /* zs_init inits the driver. */ static int __init zs_init(void) { - int i, ret; + int ret; pr_info("%s%s\n", zs_name, zs_version); - /* Find out how many Z85C30 SCCs we have. */ - ret = zs_probe_sccs(); - if (ret) - return ret; - ret = uart_register_driver(&zs_reg); if (ret) return ret; + ret = platform_driver_probe(&zs_driver, zs_probe); + if (ret) + uart_unregister_driver(&zs_reg); - for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_add_one_port(&zs_reg, uport); - } - - return 0; + return ret; } static void __exit zs_exit(void) { - int i; - - for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_remove_one_port(&zs_reg, uport); - } - + platform_driver_unregister(&zs_driver); uart_unregister_driver(&zs_reg); } diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h index 26ef8eafa1c1..e0d3c189b33f 100644 --- a/drivers/tty/serial/zs.h +++ b/drivers/tty/serial/zs.h @@ -41,7 +41,6 @@ struct zs_scc { struct zs_port zport[2]; spinlock_t zlock; atomic_t irq_guard; - int initialised; }; #endif /* __KERNEL__ */ diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index e99636ab9db5..3ca5e3dc5ac0 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3978,9 +3978,6 @@ int __init vty_init(const struct file_operations *console_fops) panic("Couldn't register console driver\n"); kbd_init(); console_map_init(); -#ifdef CONFIG_MDA_CONSOLE - mda_console_init(); -#endif return 0; } diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index bc037db46624..9c0973a7ffc3 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -177,14 +177,14 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host) int i; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); + dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; + return 0; } - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); - host->ice = ice; /* Initialize the blk_crypto_profile */ diff --git a/drivers/uio/uio_pci_generic_sva.c b/drivers/uio/uio_pci_generic_sva.c index 4a46acd994a8..d05ef77f7e32 100644 --- a/drivers/uio/uio_pci_generic_sva.c +++ b/drivers/uio/uio_pci_generic_sva.c @@ -129,15 +129,13 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = devm_uio_register_device(&pdev->dev, &udev->info); if (ret) { dev_err(&pdev->dev, "Failed to register uio device\n"); - goto out_free; + goto out_disable; } pci_set_drvdata(pdev, udev); return 0; -out_free: - kfree(udev); out_disable: pci_disable_device(pdev); @@ -146,11 +144,8 @@ out_disable: static void remove(struct pci_dev *pdev) { - struct uio_pci_sva_dev *udev = pci_get_drvdata(pdev); - pci_release_regions(pdev); pci_disable_device(pdev); - kfree(udev); } static ssize_t pasid_show(struct device *dev, diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 8382231af357..1db8db1b7cc3 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2817,9 +2817,19 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING); if (request) { - if (trb) + if (trb) { *trb = trb_tmp; + /* + * Per datasheet, EPRST causes DMA to reposition to the next TD. + * Manually reset EP_TRADDR to the current TRB to prevent + * the hardware from skipping the interrupted request. + */ + writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma + + priv_req->start_trb * TRB_SIZE), + &priv_dev->regs->ep_traddr); + } + cdns3_rearm_transfer(priv_ep, 1); } diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 735df88774e4..94e9706a1806 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -126,15 +126,15 @@ static int cdns3_plat_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), "Failed to get cdn3,usb2-phy\n"); - ret = phy_init(cdns->usb2_phy); - if (ret) - return ret; - cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); if (IS_ERR(cdns->usb3_phy)) return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), "Failed to get cdn3,usb3-phy\n"); + ret = phy_init(cdns->usb2_phy); + if (ret) + return ret; + ret = phy_init(cdns->usb3_phy); if (ret) goto err_phy3_init; @@ -186,6 +186,9 @@ static void cdns3_plat_remove(struct platform_device *pdev) struct device *dev = cdns->dev; pm_runtime_get_sync(dev); + if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))) + pm_runtime_allow(dev); + pm_runtime_disable(dev); pm_runtime_put_noidle(dev); cdns_remove(cdns); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 7cfabb04a4fb..2ab3db3c1015 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -655,12 +655,6 @@ static enum ci_role ci_get_role(struct ci_hdrc *ci) return role; } -static struct usb_role_switch_desc ci_role_switch = { - .set = ci_usb_role_switch_set, - .get = ci_usb_role_switch_get, - .allow_userspace_control = true, -}; - static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { @@ -787,9 +781,6 @@ static int ci_get_platdata(struct device *dev, cable->connected = false; } - if (device_property_read_bool(dev, "usb-role-switch")) - ci_role_switch.fwnode = dev->fwnode; - platdata->pctl = devm_pinctrl_get(dev); if (!IS_ERR(platdata->pctl)) { struct pinctrl_state *p; @@ -1033,6 +1024,7 @@ ATTRIBUTE_GROUPS(ci); static int ci_hdrc_probe(struct platform_device *pdev) { + struct usb_role_switch_desc ci_role_switch = {}; struct device *dev = &pdev->dev; struct ci_hdrc *ci; struct resource *res; @@ -1179,7 +1171,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - if (ci_role_switch.fwnode) { + if (device_property_read_bool(dev, "usb-role-switch")) { + ci_role_switch.set = ci_usb_role_switch_set; + ci_role_switch.get = ci_usb_role_switch_get; + ci_role_switch.allow_userspace_control = true; + ci_role_switch.fwnode = dev_fwnode(dev); ci_role_switch.driver_data = ci; ci->role_switch = usb_role_switch_register(dev, &ci_role_switch); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 54059e4fc6ed..ddf0b5963859 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -114,8 +114,6 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, int retval; retval = usb_autopm_get_interface(acm->control); -#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */ -#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */ if (retval) return retval; diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 25fd5329a878..01f448a783c0 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -115,3 +115,5 @@ struct acm { #define DISABLE_ECHO BIT(7) #define MISSING_CAP_BRK BIT(8) #define NO_UNION_12 BIT(9) +#define VENDOR_CLASS_DATA_IFACE BIT(10) /* data interface uses vendor-specific class */ +#define ALWAYS_POLL_CTRL BIT(11) /* keep ctrl URB active even without an open TTY */ diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index bd9347804dec..af9ae55dae14 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2306,6 +2306,14 @@ static void usbtmc_interrupt(struct urb *urb) switch (status) { case 0: /* SUCCESS */ + /* ensure at least two bytes of headers were transferred */ + if (urb->actual_length < 2) { + dev_warn(dev, + "actual length %d not sufficient for interrupt headers\n", + urb->actual_length); + goto exit; + } + /* check for valid STB notification */ if (data->iin_buffer[0] > 0x81) { data->bNotify1 = data->iin_buffer[0]; @@ -2432,6 +2440,12 @@ static int usbtmc_probe(struct usb_interface *intf, data->iin_ep = int_in->bEndpointAddress; data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in); data->iin_interval = int_in->bInterval; + /* wMaxPacketSize should be 0x02 or more as per USB488 Table 22 */ + if (iface_desc->desc.bInterfaceProtocol == 1 && + data->iin_wMaxPacketSize < 2) { + retcode = -EINVAL; + goto err_put; + } dev_dbg(&intf->dev, "Found Int in endpoint at %u\n", data->iin_ep); } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 417140b012bb..45e20c6d76c0 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -56,8 +56,7 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev, desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer; if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE || desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) { - dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion" - "for config %d interface %d altsetting %d ep %d.\n", + dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion for config %d interface %d altsetting %d ep 0x%X.\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); return; } @@ -91,7 +90,7 @@ static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev, size -= h->bLength; } - dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n", + dev_notice(ddev, "No eUSB2 isoc ep 0x%X companion for config %d interface %d altsetting %d\n", ep->desc.bEndpointAddress, cfgno, inum, asnum); } @@ -115,9 +114,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, } if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { - dev_notice(ddev, "No SuperSpeed endpoint companion for config %d " - " interface %d altsetting %d ep %d: " - "using minimum values\n", + dev_notice(ddev, "No SuperSpeed endpoint companion for config %d interface %d altsetting %d ep 0x%X: using minimum values\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); /* Fill in some default values. @@ -141,42 +138,32 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, /* Check the various values */ if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) { - dev_notice(ddev, "Control endpoint with bMaxBurst = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to zero\n", desc->bMaxBurst, - cfgno, inum, asnum, ep->desc.bEndpointAddress); + dev_notice(ddev, "Control endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", + desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bMaxBurst = 0; } else if (desc->bMaxBurst > 15) { - dev_notice(ddev, "Endpoint with bMaxBurst = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to 15\n", desc->bMaxBurst, - cfgno, inum, asnum, ep->desc.bEndpointAddress); + dev_notice(ddev, "Endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to 15\n", + desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bMaxBurst = 15; } if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) && desc->bmAttributes != 0) { - dev_notice(ddev, "%s endpoint with bmAttributes = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to zero\n", + dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", desc->bmAttributes, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 0; } else if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) { - dev_notice(ddev, "Bulk endpoint with more than 65536 streams in " - "config %d interface %d altsetting %d ep %d: " - "setting to max\n", + dev_notice(ddev, "Bulk endpoint with more than 65536 streams in config %d interface %d altsetting %d ep 0x%X: setting to max\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 16; } else if (usb_endpoint_xfer_isoc(&ep->desc) && !USB_SS_SSP_ISOC_COMP(desc->bmAttributes) && USB_SS_MULT(desc->bmAttributes) > 3) { - dev_notice(ddev, "Isoc endpoint has Mult of %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to 3\n", + dev_notice(ddev, "Isoc endpoint has Mult of %d in config %d interface %d altsetting %d ep 0x%X: setting to 3\n", USB_SS_MULT(desc->bmAttributes), cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 2; @@ -191,10 +178,15 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, (desc->bMaxBurst + 1); else max_tx = 999999; - if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) { - dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to %d\n", + /* + * wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite. + * Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN + * endpoints is usually bogus too, and recent HCs enforce interrupt BW limits. + */ + if (le16_to_cpu(desc->wBytesPerInterval) > max_tx || + (le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) && + usb_endpoint_is_int_in(&ep->desc))) { + dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in config %d interface %d altsetting %d ep 0x%X: setting to %d\n", usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int", le16_to_cpu(desc->wBytesPerInterval), cfgno, inum, asnum, ep->desc.bEndpointAddress, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 89221f1ce769..b181b43a35dc 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -328,9 +328,7 @@ static const u8 ss_rh_config_descriptor[] = { USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ - /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) - * see hub.c:hub_configure() for details. */ - (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 2 bytes per USB3 10.15.1 */ 0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ /* one SuperSpeed endpoint companion descriptor */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 0ffdaefba508..87810eff974e 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -513,6 +513,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */ { USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM }, + /* Lenovo ThinkPad USB-C Dock Gen2 USB 3.1 and USB 2.0 hub controllers */ + { USB_DEVICE(0x17ef, 0xa391), .driver_info = USB_QUIRK_NO_LPM }, + { USB_DEVICE(0x17ef, 0xa392), .driver_info = USB_QUIRK_NO_LPM }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 1a763ad4f721..2414291aa908 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4804,6 +4804,7 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); int rc; unsigned long flags; + int urb_status; dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n"); dwc2_dump_urb_info(hcd, urb, "urb_dequeue"); @@ -4828,11 +4829,12 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, /* Higher layer software sets URB status */ spin_unlock(&hsotg->lock); + urb_status = urb->status; usb_hcd_giveback_urb(hcd, urb, status); spin_lock(&hsotg->lock); dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n"); - dev_dbg(hsotg->dev, " urb->status = %d\n", urb->status); + dev_dbg(hsotg->dev, " urb->status = %d\n", urb_status); out: spin_unlock_irqrestore(&hsotg->lock, flags); diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index f41b0da5e89d..9b9525592a85 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -184,15 +184,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } ret = phy_init(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); + if (ret < 0) goto err; - } ret = reset_control_deassert(apbrst); if (ret < 0) { dev_err(dev, "Failed to release APB reset\n"); - goto err; + goto err_phy_exit; } if (priv_data->usb3_phy) { @@ -208,26 +206,24 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) ret = reset_control_deassert(crst); if (ret < 0) { dev_err(dev, "Failed to release core reset\n"); - goto err; + goto err_phy_exit; } ret = reset_control_deassert(hibrst); if (ret < 0) { dev_err(dev, "Failed to release hibernation reset\n"); - goto err; + goto err_phy_exit; } ret = phy_power_on(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); - goto err; - } + if (ret < 0) + goto err_phy_exit; /* ulpi reset via gpio-modepin or gpio-framework driver */ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) { - return dev_err_probe(dev, PTR_ERR(reset_gpio), - "Failed to request reset GPIO\n"); + ret = PTR_ERR(reset_gpio); + goto err_phy_power_off; } if (reset_gpio) { @@ -237,6 +233,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } dwc3_xlnx_set_coherency(priv_data, XLNX_USB_TRAFFIC_ROUTE_CONFIG); + + return 0; + +err_phy_power_off: + phy_power_off(priv_data->usb3_phy); +err_phy_exit: + phy_exit(priv_data->usb3_phy); err: return ret; } diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a902184bdf82..dc3664374596 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2172,7 +2172,10 @@ unknown: sizeof(url_descriptor->URL) - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset); - if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) + if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH) + landing_page_length = landing_page_offset; + else if (w_length < + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) landing_page_length = w_length - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 002c3441bea3..75912ce6ab55 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -150,6 +150,8 @@ struct ffs_dma_fence { struct dma_fence base; struct ffs_dmabuf_priv *priv; struct work_struct work; + struct usb_ep *ep; + struct usb_request *req; }; struct ffs_epfile { @@ -619,7 +621,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ ret = __ffs_ep0_queue_wait(ffs, data, len); - if ((ret > 0) && (copy_to_user(buf, data, len))) + if ((ret > 0) && (copy_to_user(buf, data, ret))) ret = -EFAULT; goto done_mutex; @@ -1385,6 +1387,21 @@ static void ffs_dmabuf_cleanup(struct work_struct *work) struct ffs_dmabuf_priv *priv = dma_fence->priv; struct dma_buf_attachment *attach = priv->attach; struct dma_fence *fence = &dma_fence->base; + struct usb_request *req = dma_fence->req; + struct usb_ep *ep = dma_fence->ep; + + /* + * eps_lock pairs with the cancel paths so they cannot pass a freed + * req to usb_ep_dequeue(). Only clear if priv->req still names ours; + * a re-queue on the same attachment may have taken that slot. + */ + spin_lock_irq(&priv->ffs->eps_lock); + if (priv->req == req) + priv->req = NULL; + spin_unlock_irq(&priv->ffs->eps_lock); + + if (ep && req) + usb_ep_free_request(ep, req); ffs_dmabuf_put(attach); dma_fence_put(fence); @@ -1414,8 +1431,8 @@ static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep, struct usb_request *req) { pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status); + /* req is freed by ffs_dmabuf_cleanup() under eps_lock. */ ffs_dmabuf_signal_done(req->context, req->status); - usb_ep_free_request(ep, req); } static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence) @@ -1699,6 +1716,10 @@ static int ffs_dmabuf_transfer(struct file *file, usb_req->context = fence; usb_req->complete = ffs_epfile_dmabuf_io_complete; + /* ffs_dmabuf_cleanup() frees usb_req via these two fields. */ + fence->req = usb_req; + fence->ep = ep->ep; + cookie = dma_fence_begin_signalling(); ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC); dma_fence_end_signalling(cookie); @@ -1708,7 +1729,6 @@ static int ffs_dmabuf_transfer(struct file *file, } else { pr_warn("FFS: Failed to queue DMABUF: %d\n", ret); ffs_dmabuf_signal_done(fence, ret); - usb_ep_free_request(ep->ep, usb_req); } spin_unlock_irq(&epfile->ffs->eps_lock); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index c5a12a6760ea..3c6b43d06a6d 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1622,7 +1622,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) hidg->dev.devt = MKDEV(major, opts->minor); ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor); if (ret) - goto err_unlock; + goto err_put_device; hidg->bInterfaceSubClass = opts->subclass; hidg->bInterfaceProtocol = opts->protocol; @@ -1659,7 +1659,6 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) err_put_device: put_device(&hidg->dev); -err_unlock: mutex_unlock(&opts->lock); return ERR_PTR(ret); } diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 8d404d88391c..73dc7e42875f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -769,6 +769,16 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; /* + * Hold opts->lock across both the XU string-descriptor fixup below and + * the descriptor-copy block further down. Without this, configfs + * uvcg_extension_drop() (which takes opts->lock) can race with the + * list_for_each_entry() walks here and inside uvc_copy_descriptors(), + * leading to a UAF on a freed struct uvcg_extension. See + * drivers/usb/gadget/function/uvc_configfs.c::uvcg_extension_drop(). + */ + mutex_lock(&opts->lock); + + /* * XUs can have an arbitrary string descriptor describing them. If they * have one pick up the ID. */ @@ -785,7 +795,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) ARRAY_SIZE(uvc_en_us_strings)); if (IS_ERR(us)) { ret = PTR_ERR(us); - goto error; + goto error_unlock; } uvc_iad.iFunction = opts->iad_index ? cdev->usb_strings[opts->iad_index].id : @@ -799,14 +809,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_iad.bFirstInterface = ret; uvc_control_intf.bInterfaceNumber = ret; uvc->control_intf = ret; opts->control_interface = ret; if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_streaming_intf_alt0.bInterfaceNumber = ret; uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; @@ -817,30 +827,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if (IS_ERR(f->fs_descriptors)) { ret = PTR_ERR(f->fs_descriptors); f->fs_descriptors = NULL; - goto error; + goto error_unlock; } f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); if (IS_ERR(f->hs_descriptors)) { ret = PTR_ERR(f->hs_descriptors); f->hs_descriptors = NULL; - goto error; + goto error_unlock; } f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); if (IS_ERR(f->ss_descriptors)) { ret = PTR_ERR(f->ss_descriptors); f->ss_descriptors = NULL; - goto error; + goto error_unlock; } f->ssp_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER_PLUS); if (IS_ERR(f->ssp_descriptors)) { ret = PTR_ERR(f->ssp_descriptors); f->ssp_descriptors = NULL; - goto error; + goto error_unlock; } + mutex_unlock(&opts->lock); + /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL); @@ -872,6 +884,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; +error_unlock: + mutex_unlock(&opts->lock); v4l2_error: v4l2_device_unregister(&uvc->v4l2_dev); error: diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 2ecd049dacc2..8b9449d16324 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -593,7 +593,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) d->gadget.max_speed = USB_SPEED_HIGH; d->gadget.speed = USB_SPEED_UNKNOWN; d->gadget.dev.of_node = vhub->pdev->dev.of_node; - d->gadget.dev.of_node_reused = true; + dev_set_of_node_reused(&d->gadget.dev); rc = usb_add_gadget_udc(d->port_dev, &d->gadget); if (rc != 0) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index f094491b1041..f47903461ed5 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2134,6 +2134,8 @@ static int dummy_hub_control( case ClearHubFeature: break; case ClearPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed == HCD_USB3) { @@ -2248,6 +2250,8 @@ static int dummy_hub_control( retval = -EPIPE; break; case SetPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_LINK_STATE: if (hcd->speed != HCD_USB3) { diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d02765bd49ce..7c5f30cfd24d 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3790,10 +3790,8 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; done: - if (dev) { + if (dev) net2280_remove(pdev); - kfree(dev); - } return retval; } diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index d2214d309e96..d5637b376367 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -247,6 +247,7 @@ struct tegra_xusb_soc { bool has_ipfs; bool lpm_support; bool otg_reset_sspi; + bool otg_set_port_power; bool has_bar2; }; @@ -1352,12 +1353,13 @@ static void tegra_xhci_id_work(struct work_struct *work) struct tegra_xusb_mbox_msg msg; struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", tegra->otg_usb2_port); + bool host_mode = tegra->host_mode; u32 status; int ret; - dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); + dev_dbg(tegra->dev, "host mode %s\n", str_on_off(host_mode)); - if (tegra->host_mode) + if (host_mode) phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); else phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); @@ -1366,41 +1368,43 @@ static void tegra_xhci_id_work(struct work_struct *work) tegra->otg_usb2_port); pm_runtime_get_sync(tegra->dev); - if (tegra->host_mode) { - /* switch to host mode */ - if (tegra->otg_usb3_port >= 0) { - if (tegra->soc->otg_reset_sspi) { - /* set PP=0 */ - tegra_xhci_hc_driver.hub_control( - xhci->shared_hcd, GetPortStatus, - 0, tegra->otg_usb3_port+1, - (char *) &status, sizeof(status)); - if (status & USB_SS_PORT_STAT_POWER) - tegra_xhci_set_port_power(tegra, false, - false); - - /* reset OTG port SSPI */ - msg.cmd = MBOX_CMD_RESET_SSPI; - msg.data = tegra->otg_usb3_port+1; - - ret = tegra_xusb_mbox_send(tegra, &msg); - if (ret < 0) { - dev_info(tegra->dev, - "failed to RESET_SSPI %d\n", - ret); + if (tegra->soc->otg_set_port_power) { + if (host_mode) { + /* switch to host mode */ + if (tegra->otg_usb3_port >= 0) { + if (tegra->soc->otg_reset_sspi) { + /* set PP=0 */ + tegra_xhci_hc_driver.hub_control( + xhci->shared_hcd, GetPortStatus, + 0, tegra->otg_usb3_port+1, + (char *) &status, sizeof(status)); + if (status & USB_SS_PORT_STAT_POWER) + tegra_xhci_set_port_power(tegra, false, + false); + + /* reset OTG port SSPI */ + msg.cmd = MBOX_CMD_RESET_SSPI; + msg.data = tegra->otg_usb3_port+1; + + ret = tegra_xusb_mbox_send(tegra, &msg); + if (ret < 0) { + dev_info(tegra->dev, + "failed to RESET_SSPI %d\n", + ret); + } } - } - tegra_xhci_set_port_power(tegra, false, true); - } + tegra_xhci_set_port_power(tegra, false, true); + } - tegra_xhci_set_port_power(tegra, true, true); + tegra_xhci_set_port_power(tegra, true, true); - } else { - if (tegra->otg_usb3_port >= 0) - tegra_xhci_set_port_power(tegra, false, false); + } else { + if (tegra->otg_usb3_port >= 0) + tegra_xhci_set_port_power(tegra, false, false); - tegra_xhci_set_port_power(tegra, true, false); + tegra_xhci_set_port_power(tegra, true, false); + } } pm_runtime_put_autosuspend(tegra->dev); } @@ -2553,6 +2557,7 @@ static const struct tegra_xusb_soc tegra124_soc = { .scale_ss_clock = true, .has_ipfs = true, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2593,6 +2598,7 @@ static const struct tegra_xusb_soc tegra210_soc = { .scale_ss_clock = false, .has_ipfs = true, .otg_reset_sspi = true, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2640,6 +2646,7 @@ static const struct tegra_xusb_soc tegra186_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2673,6 +2680,7 @@ static const struct tegra_xusb_soc tegra194_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra124_ops, .mbox = { .cmd = 0x68, @@ -2708,6 +2716,7 @@ static const struct tegra_xusb_soc tegra234_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra234_ops, .mbox = { .cmd = XUSB_BAR2_ARU_MBOX_CMD, diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 48bb9bfb2204..333ab79f0ca9 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -337,7 +337,6 @@ static int omap2430_probe(struct platform_device *pdev) } else { device_set_of_node_from_dev(&musb->dev, &pdev->dev); } - of_node_put(np); glue->dev = &pdev->dev; glue->musb = musb; @@ -455,6 +454,7 @@ static int omap2430_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register musb device\n"); goto err_disable_rpm; } + of_node_put(np); return 0; @@ -464,6 +464,7 @@ err_put_control_otghs: if (!IS_ERR(glue->control_otghs)) put_device(glue->control_otghs); err_put_musb: + of_node_put(np); platform_device_put(musb); return ret; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 38ac910b1082..7bbd9523d4e9 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -194,6 +194,9 @@ static void belkin_sa_read_int_callback(struct urb *urb) usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); + if (urb->actual_length < BELKIN_SA_MSR_INDEX + 1) + goto exit; + /* Handle known interrupt data */ /* ignore data[0] and data[1] */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index afff1a0f4298..bcf302e88ca4 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -445,6 +445,14 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * The buffer must be large enough for the one or two-byte header (and + * following data), but assume anything smaller than eight bytes is + * broken. + */ + if (port->interrupt_out_size < 8) + return -EINVAL; + priv = kzalloc_obj(struct cypress_private); if (!priv) return -ENOMEM; @@ -1017,8 +1025,8 @@ static void cypress_read_int_callback(struct urb *urb) char tty_flag = TTY_NORMAL; int bytes = 0; int result; - int i = 0; int status = urb->status; + int i; switch (status) { case 0: /* success */ @@ -1056,22 +1064,32 @@ static void cypress_read_int_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); result = urb->actual_length; + i = 0; switch (priv->pkt_fmt) { default: case packet_format_1: /* This is for the CY7C64013... */ + if (result < 2) + break; priv->current_status = data[0] & 0xF8; bytes = data[1] + 2; i = 2; break; case packet_format_2: /* This is for the CY7C63743... */ + if (result < 1) + break; priv->current_status = data[0] & 0xF8; bytes = (data[0] & 0x07) + 1; i = 1; break; } spin_unlock_irqrestore(&priv->lock, flags); + if (i == 0) { + dev_dbg(dev, "%s - short packet received: %d bytes\n", + __func__, result); + goto continue_read; + } if (result < bytes) { dev_dbg(dev, "%s - wrong packet size - received %d bytes but packet said %d bytes\n", diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index d515df045c4c..c481208255eb 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1229,15 +1229,34 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) static int digi_startup(struct usb_serial *serial) { struct digi_serial *serial_priv; + int oob_port_num; int ret; + int i; + + /* + * The port bulk-out buffers must be large enough for header and + * buffered data. + */ + for (i = 0; i < serial->type->num_ports; i++) { + if (serial->port[i]->bulk_out_size < DIGI_OUT_BUF_SIZE + 2) + return -EINVAL; + } + + /* + * The OOB port bulk-out buffer must be large enough for the two + * commands in digi_set_modem_signals(). + */ + oob_port_num = serial->type->num_ports; + if (serial->port[oob_port_num]->bulk_out_size < 8) + return -EINVAL; serial_priv = kzalloc_obj(*serial_priv); if (!serial_priv) return -ENOMEM; spin_lock_init(&serial_priv->ds_serial_lock); - serial_priv->ds_oob_port_num = serial->type->num_ports; - serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num]; + serial_priv->ds_oob_port_num = oob_port_num; + serial_priv->ds_oob_port = serial->port[oob_port_num]; ret = digi_port_init(serial_priv->ds_oob_port, serial_priv->ds_oob_port_num); diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index cb55370e036f..d48819d785ef 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -773,6 +773,12 @@ static int get_manuf_info(struct edgeport_serial *serial, u8 *buffer) } /* Read the descriptor data */ + if (le16_to_cpu(rom_desc->Size) != sizeof(struct edge_ti_manuf_descriptor)) { + dev_err(dev, "unexpected Edge descriptor length: %u\n", + le16_to_cpu(rom_desc->Size)); + status = -EINVAL; + goto exit; + } status = read_rom(serial, start_address+sizeof(struct ti_i2c_desc), le16_to_cpu(rom_desc->Size), buffer); if (status) @@ -838,6 +844,11 @@ static int build_i2c_fw_hdr(u8 *header, const struct firmware *fw) /* Pointer to fw_down memory image */ img_header = (struct ti_i2c_image_header *)&fw->data[4]; + if (le16_to_cpu(img_header->Length) > + buffer_size - sizeof(struct ti_i2c_firmware_rec)) { + kfree(buffer); + return -EINVAL; + } memcpy(buffer + sizeof(struct ti_i2c_firmware_rec), &fw->data[4 + sizeof(struct ti_i2c_image_header)], le16_to_cpu(img_header->Length)); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 46448843541a..28b80607cebd 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1187,6 +1187,10 @@ static void usa49wg_indat_callback(struct urb *urb) len = 0; while (i < urb->actual_length) { + if (urb->actual_length - i < 3) { + dev_warn_ratelimited(&urb->dev->dev, "malformed indat packet\n"); + break; + } /* Check port number from message */ if (data[i] >= serial->num_ports) { diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index ed8531a64768..e72a0b45a707 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -330,8 +330,8 @@ static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, unsigned char *buf = dest; int count; - count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size, - &port->lock); + count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, + size - KLSI_HDR_LEN, &port->lock); put_unaligned_le16(count, buf); return count + KLSI_HDR_LEN; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 18844b92bd08..163161881d2d 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -378,6 +378,7 @@ static int mct_u232_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv; + u16 pid; /* check first to simplify error handling */ if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) { @@ -385,6 +386,16 @@ static int mct_u232_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * Compensate for a hardware bug: although the Sitecom U232-P25 + * device reports a maximum output packet size of 32 bytes, + * it seems to be able to accept only 16 bytes (and that's what + * SniffUSB says too...) + */ + pid = le16_to_cpu(serial->dev->descriptor.idProduct); + if (pid == MCT_U232_SITECOM_PID) + port->bulk_out_size = min(16, port->bulk_out_size); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; @@ -410,7 +421,6 @@ static void mct_u232_port_remove(struct usb_serial_port *port) static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); int retval = 0; unsigned int control_state; @@ -418,15 +428,6 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char last_lcr; unsigned char last_msr; - /* Compensate for a hardware bug: although the Sitecom U232-P25 - * device reports a maximum output packet size of 32 bytes, - * it seems to be able to accept only 16 bytes (and that's what - * SniffUSB says too...) - */ - if (le16_to_cpu(serial->dev->descriptor.idProduct) - == MCT_U232_SITECOM_PID) - port->bulk_out_size = 16; - /* Do a defined restart: the normal serial device seems to * always turn on DTR and RTS here, so do the same. I'm not * sure if this is really necessary. But it should not harm @@ -543,6 +544,11 @@ static void mct_u232_read_int_callback(struct urb *urb) goto exit; } + if (urb->actual_length < 2) { + dev_warn_ratelimited(&port->dev, "short interrupt-in packet\n"); + goto exit; + } + /* * The interrupt-in pipe signals exceptional conditions (modem line * signal changes and errors). data[0] holds MSR, data[1] holds LSR. diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index ad5fdf55a02e..c9b9928c473a 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct usb_serial *serial, */ BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16); + /* + * The bulk-out buffers must be large enough for the four-byte header + * (and following data), but assume anything smaller than eight bytes + * is broken. + */ + if (usb_endpoint_maxp(epds->bulk_out[0]) < 8) + return -EINVAL; + for (i = 1; i < num_ports; ++i) epds->bulk_out[i] = epds->bulk_out[0]; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index aa1e9745f967..b59982ed8b25 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -30,6 +30,10 @@ /* This one seems to be a re-branded ZyXEL device */ #define BT_IGNITIONPRO_ID 0x2000 +#define OMNINET_HEADERLEN 4 +#define OMNINET_BULKOUTSIZE 64 +#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) + /* function prototypes */ static void omninet_process_read_urb(struct urb *urb); static int omninet_prepare_write_buffer(struct usb_serial_port *port, @@ -54,6 +58,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net usb", .id_table = id_table, .num_bulk_out = 2, + .bulk_out_size = OMNINET_BULKOUTSIZE, .calc_num_ports = omninet_calc_num_ports, .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, @@ -130,10 +135,6 @@ static void omninet_port_remove(struct usb_serial_port *port) kfree(od); } -#define OMNINET_HEADERLEN 4 -#define OMNINET_BULKOUTSIZE 64 -#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) - static void omninet_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 42e4cecd28ac..a34e79cfd5b6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -202,6 +202,7 @@ static void option_instat_callback(struct urb *urb); #define DELL_PRODUCT_5821E_ESIM 0x81e0 #define DELL_PRODUCT_5829E_ESIM 0x81e4 #define DELL_PRODUCT_5829E 0x81e6 +#define DELL_PRODUCT_5826E_ESIM 0x81ea #define DELL_PRODUCT_FM101R_ESIM 0x8213 #define DELL_PRODUCT_FM101R 0x8215 @@ -1123,6 +1124,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(0) | RSVD(6) }, { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5829E_ESIM), .driver_info = RSVD(0) | RSVD(6) }, + { USB_DEVICE_INTERFACE_CLASS(DELL_VENDOR_ID, DELL_PRODUCT_5826E_ESIM, 0xff), + .driver_info = RSVD(1) | RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(DELL_VENDOR_ID, DELL_PRODUCT_FM101R, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(DELL_VENDOR_ID, DELL_PRODUCT_FM101R_ESIM, 0xff) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */ @@ -2450,6 +2453,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x60) }, /* MeiG SRM813Q (NMEA) */ + { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ @@ -2470,7 +2479,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */ .driver_info = RSVD(5) }, - { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */ + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 238b54993446..d267a31dcccf 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -259,6 +259,7 @@ static int safe_prepare_write_buffer(struct usb_serial_port *port, static int safe_startup(struct usb_serial *serial) { struct usb_interface_descriptor *desc; + int bulk_out_size; if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS) return -ENODEV; @@ -279,6 +280,16 @@ static int safe_startup(struct usb_serial *serial) default: return -EINVAL; } + + /* + * The bulk-out buffer needs to be large enough for the two-byte + * trailer in safe mode, but assume anything smaller than eight bytes + * is broken. + */ + bulk_out_size = serial->port[0]->bulk_out_size; + if (bulk_out_size > 0 && bulk_out_size < 8) + return -EINVAL; + return 0; } diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 939a98c2d3f7..d6f86d5db3bf 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -132,6 +132,13 @@ UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), +/* Reported-by: Sam Burkels <sam@1a38.nl> */ +UNUSUAL_DEV(0x154b, 0xf009, 0x0000, 0x9999, + "PNY", + "PNY ELITE PSSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES), + /* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */ UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999, "PNY", diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 35d9c3086990..263a89c5f324 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -405,6 +405,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt, dp->state = DP_STATE_EXIT_PRIME; break; case DP_CMD_STATUS_UPDATE: + if (count < 2) + break; dp->data.status = *vdo; ret = dp_altmode_status_update(dp); break; diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 889c4c29c1b8..9ab1277b7ed1 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1751,19 +1751,22 @@ static int fusb302_probe(struct i2c_client *client) bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode)); if (IS_ERR(bridge_dev)) { - ret = PTR_ERR(bridge_dev); - dev_err_probe(chip->dev, ret, "failed to alloc bridge\n"); - goto destroy_workqueue; + ret = dev_err_probe(chip->dev, PTR_ERR(bridge_dev), + "failed to alloc bridge\n"); + goto fwnode_put; } chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { - fwnode_handle_put(chip->tcpc_dev.fwnode); ret = dev_err_probe(dev, PTR_ERR(chip->tcpm_port), "cannot register tcpm port\n"); - goto destroy_workqueue; + goto fwnode_put; } + ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); + if (ret) + goto tcpm_unregister_port; + ret = request_threaded_irq(chip->gpio_int_n_irq, NULL, fusb302_irq_intn, IRQF_ONESHOT | IRQF_TRIGGER_LOW, "fsc_interrupt_int_n", chip); @@ -1774,14 +1777,11 @@ static int fusb302_probe(struct i2c_client *client) enable_irq_wake(chip->gpio_int_n_irq); i2c_set_clientdata(client, chip); - ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); - if (ret) - return ret; - - return ret; + return 0; tcpm_unregister_port: tcpm_unregister_port(chip->tcpm_port); +fwnode_put: fwnode_handle_put(chip->tcpc_dev.fwnode); destroy_workqueue: fusb302_debugfs_exit(chip); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index c0ee7e6959ed..7324139d51c8 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -181,6 +181,15 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET; msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr); rx_buf_ptr = rx_buf_ptr + sizeof(msg.header); + + if (count < TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET + sizeof(msg.header) + + pd_header_cnt_le(msg.header) * sizeof(msg.payload[0])) { + max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS); + dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d for header cnt %d\n", + count, pd_header_cnt_le(msg.header)); + return; + } + for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++, rx_buf_ptr += sizeof(msg.payload[0])) msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 55fee96d3342..7ef746a90a17 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1855,6 +1855,9 @@ static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt) u32 vdo = p[VDO_INDEX_IDH]; u32 product = p[VDO_INDEX_PRODUCT]; + if (cnt <= VDO_INDEX_PRODUCT) + return; + memset(&port->mode_data, 0, sizeof(port->mode_data)); port->partner_ident.id_header = vdo; @@ -1875,6 +1878,9 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p u32 product = p[VDO_INDEX_PRODUCT]; int svdm_version; + if (cnt <= VDO_INDEX_CABLE_1) + return; + /* * Attempt to consume identity only if cable currently is not set */ @@ -1898,7 +1904,7 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p switch (port->negotiated_rev_prime) { case PD_REV30: port->cable_desc.pd_revision = 0x0300; - if (port->cable_desc.active) + if (port->cable_desc.active && cnt > VDO_INDEX_CABLE_2) port->cable_ident.vdo[1] = p[VDO_INDEX_CABLE_2]; break; case PD_REV20: @@ -1986,23 +1992,19 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, switch (rx_sop_type) { case TCPC_TX_SOP_PRIME: pmdata = &port->mode_data_prime; - if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; case TCPC_TX_SOP: pmdata = &port->mode_data; - if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; default: return; } for (i = 1; i < cnt; i++) { + if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { + /* Already logged in svdm_consume_svids() */ + return; + } paltmode = &pmdata->altmode_desc[pmdata->altmodes]; memset(paltmode, 0, sizeof(*paltmode)); @@ -2147,6 +2149,55 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port) tcpm_can_communicate_sop_prime(port); } +static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) +{ + struct typec_port *typec = port->typec_port; + struct pd_mode_data *modep; + + if (rx_sop_type == TCPC_TX_SOP) { + modep = &port->mode_data; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(svid, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + if (tcpm_cable_vdm_supported(port)) { + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(USB_SID_PD, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_SVID); + return 1; + } + + tcpm_register_partner_altmodes(port); + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { + modep = &port->mode_data_prime; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(svid, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + tcpm_register_plug_altmodes(port); + tcpm_register_partner_altmodes(port); + } + + return 0; +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, enum adev_actions *adev_action, @@ -2404,41 +2455,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, } break; case CMD_DISCOVER_MODES: - if (rx_sop_type == TCPC_TX_SOP) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep->svid_index++; - if (modep->svid_index < modep->nsvids) { - u16 svid = modep->svids[modep->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP; - response[0] = VDO(svid, 1, svdm_version, - CMD_DISCOVER_MODES); - rlen = 1; - } else if (tcpm_cable_vdm_supported(port)) { - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(USB_SID_PD, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_SVID); - rlen = 1; - } else { - tcpm_register_partner_altmodes(port); - } - } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep_prime->svid_index++; - if (modep_prime->svid_index < modep_prime->nsvids) { - u16 svid = modep_prime->svids[modep_prime->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(svid, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_MODES); - rlen = 1; - } else { - tcpm_register_plug_altmodes(port); - tcpm_register_partner_altmodes(port); - } - } + /* 6.4.4.3.3 */ + svdm_consume_modes(port, p, cnt, rx_sop_type); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); break; case CMD_ENTER_MODE: *response_tx_sop_type = rx_sop_type; @@ -2481,9 +2502,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, switch (cmd) { case CMD_DISCOVER_IDENT: case CMD_DISCOVER_SVID: - case CMD_DISCOVER_MODES: case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): break; + case CMD_DISCOVER_MODES: + tcpm_log(port, "Skip SVID 0x%04x (failed to discover mode)", + PD_VDO_SVID_SVID0(p[0])); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); + break; case CMD_ENTER_MODE: /* Back to USB Operation */ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM; diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 759c982bb16a..0e5a3e277c3e 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -444,9 +444,11 @@ static int wcove_start_toggling(struct tcpc_dev *tcpc, return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl); } -static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) +static int wcove_read_rx_buffer(struct wcove_typec *wcove, + struct pd_message *msg) { - unsigned int info; + unsigned int info, val, len; + u8 *buf = (u8 *)msg; int ret; int i; @@ -454,12 +456,13 @@ static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) if (ret) return ret; - /* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */ + len = min(USBC_RXINFO_RXBYTES(info), sizeof(*msg)); - for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) { - ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i); + for (i = 0; i < len; i++) { + ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, &val); if (ret) return ret; + buf[i] = val; } return regmap_write(wcove->regmap, USBC_RXSTATUS, diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 43faec794b95..d0b769333bd9 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1835,6 +1835,7 @@ static int tps6598x_probe(struct i2c_client *client) goto err_role_put; if (status & TPS_STATUS_PLUG_PRESENT) { + ret = -EINVAL; if (!tps6598x_read_power_status(tps)) goto err_unregister_port; if (!tps->data->read_data_status(tps)) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 8aae80b457d7..67a0991a7b76 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -240,6 +240,10 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt, dp->header |= VDO_CMDT(CMDT_RSP_ACK); break; case DP_CMD_CONFIGURE: + if (count < 2) { + dp->header |= VDO_CMDT(CMDT_RSP_NAK); + break; + } dp->data.conf = *data; if (ucsi_displayport_configure(dp)) { dp->header |= VDO_CMDT(CMDT_RSP_NAK); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 5b7ad9e99cb9..61cb24ed820f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1277,7 +1277,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) work); struct ucsi *ucsi = con->ucsi; u8 curr_scale, volt_scale; - enum typec_role role; + enum typec_role role, prev_role; u16 change; int ret; u32 val; @@ -1288,6 +1288,8 @@ static void ucsi_handle_connector_change(struct work_struct *work) dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", __func__); + prev_role = UCSI_CONSTAT(con, PWR_DIR); + ret = ucsi_get_connector_status(con, true); if (ret) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", @@ -1304,9 +1306,14 @@ static void ucsi_handle_connector_change(struct work_struct *work) change = UCSI_CONSTAT(con, CHANGE); role = UCSI_CONSTAT(con, PWR_DIR); - if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { + if ((change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) { typec_set_pwr_role(con->port, role); - ucsi_port_psy_changed(con); + + /* Some power_supply properties vary depending on the power direction when + * connected + */ + if (UCSI_CONSTAT(con, CONNECTED)) + ucsi_port_psy_changed(con); /* Complete pending power role swap */ if (!completion_done(&con->complete)) @@ -1380,13 +1387,22 @@ out_unlock: */ void ucsi_connector_change(struct ucsi *ucsi, u8 num) { - struct ucsi_connector *con = &ucsi->connector[num - 1]; + struct ucsi_connector *con; if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { dev_dbg(ucsi->dev, "Early connector change event\n"); return; } + if (!num || num > ucsi->cap.num_connectors) { + dev_warn_ratelimited(ucsi->dev, + "Bogus connector change on %u (max %u)\n", + num, ucsi->cap.num_connectors); + return; + } + + con = &ucsi->connector[num - 1]; + if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) schedule_work(&con->work); } diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 199799b319c2..4463c1ae96bd 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1243,6 +1243,11 @@ not_signed_fw: *****************************************************************/ p = strnchr(fw->data, fw->size, ':'); + if (!p) { + dev_err(dev, "Bad FW format: no ':' record header found\n"); + err = -EINVAL; + goto release_mem; + } while (p < eof) { s = strnchr(p + 1, eof - p - 1, ':'); diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 90383107b660..c5f079c5a1ea 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -632,6 +632,7 @@ void vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); + v_stop_timer(udc); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c index a4f02ea3e3ef..d4ce85c4c6a2 100644 --- a/drivers/usb/usbip/vudc_transfer.c +++ b/drivers/usb/usbip/vudc_transfer.c @@ -490,7 +490,8 @@ void v_stop_timer(struct vudc *udc) { struct transfer_timer *t = &udc->tr_timer; - /* timer itself will take care of stopping */ + /* Delete the timer synchronously before teardown frees udc. */ dev_dbg(&udc->pdev->dev, "timer stop"); + timer_delete_sync(&t->timer); t->state = VUDC_TR_STOPPED; } diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 12f54480f57f..9f81af3506da 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -23,21 +23,6 @@ config VGA_CONSOLE Say Y. -config MDA_CONSOLE - depends on VGA_CONSOLE && ISA - tristate "MDA text console (dual-headed)" - help - Say Y here if you have an old MDA or monochrome Hercules graphics - adapter in your system acting as a second head ( = video card). You - will then be able to use two monitors with your Linux system. Do not - say Y here if your MDA card is the primary card in your system; the - normal VGA driver will handle it. - - To compile this driver as a module, choose M here: the - module will be called mdacon. - - If unsure, say N. - config SGI_NEWPORT_CONSOLE tristate "SGI Newport Console support" depends on SGI_IP22 && HAS_IOMEM diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index fd79016a0d95..f1000605210c 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -7,4 +7,3 @@ obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o obj-$(CONFIG_STI_CONSOLE) += sticon.o obj-$(CONFIG_VGA_CONSOLE) += vgacon.o -obj-$(CONFIG_MDA_CONSOLE) += mdacon.o diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c deleted file mode 100644 index d52cd99cd18b..000000000000 --- a/drivers/video/console/mdacon.c +++ /dev/null @@ -1,566 +0,0 @@ -/* - * linux/drivers/video/mdacon.c -- Low level MDA based console driver - * - * (c) 1998 Andrew Apted <ajapted@netspace.net.au> - * - * including portions (c) 1995-1998 Patrick Caulfield. - * - * slight improvements (c) 2000 Edward Betts <edward@debian.org> - * - * This file is based on the VGA console driver (vgacon.c): - * - * Created 28 Sep 1997 by Geert Uytterhoeven - * - * Rewritten by Martin Mares <mj@ucw.cz>, July 1998 - * - * and on the old console.c, vga.c and vesa_blank.c drivers: - * - * Copyright (C) 1991, 1992 Linus Torvalds - * 1995 Jay Estabrook - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - * - * Changelog: - * Paul G. (03/2001) Fix mdacon= boot prompt to use __setup(). - */ - -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/console.h> -#include <linux/string.h> -#include <linux/kd.h> -#include <linux/vt_kern.h> -#include <linux/vt_buffer.h> -#include <linux/selection.h> -#include <linux/spinlock.h> -#include <linux/ioport.h> -#include <linux/delay.h> -#include <linux/init.h> - -#include <asm/io.h> -#include <asm/vga.h> - -static DEFINE_SPINLOCK(mda_lock); - -/* description of the hardware layout */ - -static u16 *mda_vram_base; /* Base of video memory */ -static unsigned long mda_vram_len; /* Size of video memory */ -static unsigned int mda_num_columns; /* Number of text columns */ -static unsigned int mda_num_lines; /* Number of text lines */ - -static unsigned int mda_index_port; /* Register select port */ -static unsigned int mda_value_port; /* Register value port */ -static unsigned int mda_mode_port; /* Mode control port */ -static unsigned int mda_status_port; /* Status and Config port */ -static unsigned int mda_gfx_port; /* Graphics control port */ - -/* current hardware state */ - -static int mda_cursor_loc=-1; -static int mda_cursor_size_from=-1; -static int mda_cursor_size_to=-1; - -static enum { TYPE_MDA, TYPE_HERC, TYPE_HERCPLUS, TYPE_HERCCOLOR } mda_type; -static char *mda_type_name; - -/* console information */ - -static int mda_first_vc = 13; -static int mda_last_vc = 16; - -static struct vc_data *mda_display_fg = NULL; - -module_param(mda_first_vc, int, 0); -MODULE_PARM_DESC(mda_first_vc, "First virtual console. Default: 13"); -module_param(mda_last_vc, int, 0); -MODULE_PARM_DESC(mda_last_vc, "Last virtual console. Default: 16"); - -/* MDA register values - */ - -#define MDA_CURSOR_BLINKING 0x00 -#define MDA_CURSOR_OFF 0x20 -#define MDA_CURSOR_SLOWBLINK 0x60 - -#define MDA_MODE_GRAPHICS 0x02 -#define MDA_MODE_VIDEO_EN 0x08 -#define MDA_MODE_BLINK_EN 0x20 -#define MDA_MODE_GFX_PAGE1 0x80 - -#define MDA_STATUS_HSYNC 0x01 -#define MDA_STATUS_VSYNC 0x80 -#define MDA_STATUS_VIDEO 0x08 - -#define MDA_CONFIG_COL132 0x08 -#define MDA_GFX_MODE_EN 0x01 -#define MDA_GFX_PAGE_EN 0x02 - - -/* - * MDA could easily be classified as "pre-dinosaur hardware". - */ - -static void write_mda_b(unsigned int val, unsigned char reg) -{ - unsigned long flags; - - spin_lock_irqsave(&mda_lock, flags); - - outb_p(reg, mda_index_port); - outb_p(val, mda_value_port); - - spin_unlock_irqrestore(&mda_lock, flags); -} - -static void write_mda_w(unsigned int val, unsigned char reg) -{ - unsigned long flags; - - spin_lock_irqsave(&mda_lock, flags); - - outb_p(reg, mda_index_port); outb_p(val >> 8, mda_value_port); - outb_p(reg+1, mda_index_port); outb_p(val & 0xff, mda_value_port); - - spin_unlock_irqrestore(&mda_lock, flags); -} - -#ifdef TEST_MDA_B -static int test_mda_b(unsigned char val, unsigned char reg) -{ - unsigned long flags; - - spin_lock_irqsave(&mda_lock, flags); - - outb_p(reg, mda_index_port); - outb (val, mda_value_port); - - udelay(20); val = (inb_p(mda_value_port) == val); - - spin_unlock_irqrestore(&mda_lock, flags); - return val; -} -#endif - -static inline void mda_set_cursor(unsigned int location) -{ - if (mda_cursor_loc == location) - return; - - write_mda_w(location >> 1, 0x0e); - - mda_cursor_loc = location; -} - -static inline void mda_set_cursor_size(int from, int to) -{ - if (mda_cursor_size_from==from && mda_cursor_size_to==to) - return; - - if (from > to) { - write_mda_b(MDA_CURSOR_OFF, 0x0a); /* disable cursor */ - } else { - write_mda_b(from, 0x0a); /* cursor start */ - write_mda_b(to, 0x0b); /* cursor end */ - } - - mda_cursor_size_from = from; - mda_cursor_size_to = to; -} - - -#ifndef MODULE -static int __init mdacon_setup(char *str) -{ - /* command line format: mdacon=<first>,<last> */ - - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - if (ints[0] < 2) - return 0; - - if (ints[1] < 1 || ints[1] > MAX_NR_CONSOLES || - ints[2] < 1 || ints[2] > MAX_NR_CONSOLES) - return 0; - - mda_first_vc = ints[1]; - mda_last_vc = ints[2]; - return 1; -} - -__setup("mdacon=", mdacon_setup); -#endif - -static int mda_detect(void) -{ - int count=0; - u16 *p, p_save; - u16 *q, q_save; - - /* do a memory check */ - - p = mda_vram_base; - q = mda_vram_base + 0x01000 / 2; - - p_save = scr_readw(p); - q_save = scr_readw(q); - - scr_writew(0xAA55, p); - if (scr_readw(p) == 0xAA55) - count++; - - scr_writew(0x55AA, p); - if (scr_readw(p) == 0x55AA) - count++; - - scr_writew(p_save, p); - - if (count != 2) { - return 0; - } - - /* check if we have 4K or 8K */ - - scr_writew(0xA55A, q); - scr_writew(0x0000, p); - if (scr_readw(q) == 0xA55A) - count++; - - scr_writew(0x5AA5, q); - scr_writew(0x0000, p); - if (scr_readw(q) == 0x5AA5) - count++; - - scr_writew(p_save, p); - scr_writew(q_save, q); - - if (count == 4) { - mda_vram_len = 0x02000; - } - - /* Ok, there is definitely a card registering at the correct - * memory location, so now we do an I/O port test. - */ - -#ifdef TEST_MDA_B - /* Edward: These two mess `tests' mess up my cursor on bootup */ - - /* cursor low register */ - if (!test_mda_b(0x66, 0x0f)) - return 0; - - /* cursor low register */ - if (!test_mda_b(0x99, 0x0f)) - return 0; -#endif - - /* See if the card is a Hercules, by checking whether the vsync - * bit of the status register is changing. This test lasts for - * approximately 1/10th of a second. - */ - - p_save = q_save = inb_p(mda_status_port) & MDA_STATUS_VSYNC; - - for (count = 0; count < 50000 && p_save == q_save; count++) { - q_save = inb(mda_status_port) & MDA_STATUS_VSYNC; - udelay(2); - } - - if (p_save != q_save) { - switch (inb_p(mda_status_port) & 0x70) { - case 0x10: - mda_type = TYPE_HERCPLUS; - mda_type_name = "HerculesPlus"; - break; - case 0x50: - mda_type = TYPE_HERCCOLOR; - mda_type_name = "HerculesColor"; - break; - default: - mda_type = TYPE_HERC; - mda_type_name = "Hercules"; - break; - } - } - - return 1; -} - -static void mda_initialize(void) -{ - write_mda_b(97, 0x00); /* horizontal total */ - write_mda_b(80, 0x01); /* horizontal displayed */ - write_mda_b(82, 0x02); /* horizontal sync pos */ - write_mda_b(15, 0x03); /* horizontal sync width */ - - write_mda_b(25, 0x04); /* vertical total */ - write_mda_b(6, 0x05); /* vertical total adjust */ - write_mda_b(25, 0x06); /* vertical displayed */ - write_mda_b(25, 0x07); /* vertical sync pos */ - - write_mda_b(2, 0x08); /* interlace mode */ - write_mda_b(13, 0x09); /* maximum scanline */ - write_mda_b(12, 0x0a); /* cursor start */ - write_mda_b(13, 0x0b); /* cursor end */ - - write_mda_w(0x0000, 0x0c); /* start address */ - write_mda_w(0x0000, 0x0e); /* cursor location */ - - outb_p(MDA_MODE_VIDEO_EN | MDA_MODE_BLINK_EN, mda_mode_port); - outb_p(0x00, mda_status_port); - outb_p(0x00, mda_gfx_port); -} - -static const char *mdacon_startup(void) -{ - mda_num_columns = 80; - mda_num_lines = 25; - - mda_vram_len = 0x01000; - mda_vram_base = (u16 *)VGA_MAP_MEM(0xb0000, mda_vram_len); - - mda_index_port = 0x3b4; - mda_value_port = 0x3b5; - mda_mode_port = 0x3b8; - mda_status_port = 0x3ba; - mda_gfx_port = 0x3bf; - - mda_type = TYPE_MDA; - mda_type_name = "MDA"; - - if (! mda_detect()) { - printk("mdacon: MDA card not detected.\n"); - return NULL; - } - - if (mda_type != TYPE_MDA) { - mda_initialize(); - } - - /* cursor looks ugly during boot-up, so turn it off */ - mda_set_cursor(mda_vram_len - 1); - - printk("mdacon: %s with %ldK of memory detected.\n", - mda_type_name, mda_vram_len/1024); - - return "MDA-2"; -} - -static void mdacon_init(struct vc_data *c, bool init) -{ - c->vc_complement_mask = 0x0800; /* reverse video */ - c->vc_display_fg = &mda_display_fg; - - if (init) { - c->vc_cols = mda_num_columns; - c->vc_rows = mda_num_lines; - } else - vc_resize(c, mda_num_columns, mda_num_lines); - - /* make the first MDA console visible */ - - if (mda_display_fg == NULL) - mda_display_fg = c; -} - -static void mdacon_deinit(struct vc_data *c) -{ - /* con_set_default_unimap(c->vc_num); */ - - if (mda_display_fg == c) - mda_display_fg = NULL; -} - -static inline u16 mda_convert_attr(u16 ch) -{ - u16 attr = 0x0700; - - /* Underline and reverse-video are mutually exclusive on MDA. - * Since reverse-video is used for cursors and selected areas, - * it takes precedence. - */ - - if (ch & 0x0800) attr = 0x7000; /* reverse */ - else if (ch & 0x0400) attr = 0x0100; /* underline */ - - return ((ch & 0x0200) << 2) | /* intensity */ - (ch & 0x8000) | /* blink */ - (ch & 0x00ff) | attr; -} - -static u8 mdacon_build_attr(struct vc_data *c, u8 color, - enum vc_intensity intensity, - bool blink, bool underline, bool reverse, - bool italic) -{ - /* The attribute is just a bit vector: - * - * Bit 0..1 : intensity (0..2) - * Bit 2 : underline - * Bit 3 : reverse - * Bit 7 : blink - */ - - return (intensity & VCI_MASK) | - (underline << 2) | - (reverse << 3) | - (italic << 4) | - (blink << 7); -} - -static void mdacon_invert_region(struct vc_data *c, u16 *p, int count) -{ - for (; count > 0; count--) { - scr_writew(scr_readw(p) ^ 0x0800, p); - p++; - } -} - -static inline u16 *mda_addr(unsigned int x, unsigned int y) -{ - return mda_vram_base + y * mda_num_columns + x; -} - -static void mdacon_putcs(struct vc_data *c, const u16 *s, unsigned int count, - unsigned int y, unsigned int x) -{ - u16 *dest = mda_addr(x, y); - - for (; count > 0; count--) { - scr_writew(mda_convert_attr(scr_readw(s++)), dest++); - } -} - -static void mdacon_clear(struct vc_data *c, unsigned int y, unsigned int x, - unsigned int width) -{ - u16 *dest = mda_addr(x, y); - u16 eattr = mda_convert_attr(c->vc_video_erase_char); - - scr_memsetw(dest, eattr, width * 2); -} - -static bool mdacon_switch(struct vc_data *c) -{ - return true; /* redrawing needed */ -} - -static bool mdacon_blank(struct vc_data *c, enum vesa_blank_mode blank, - bool mode_switch) -{ - if (mda_type == TYPE_MDA) { - if (blank) - scr_memsetw(mda_vram_base, - mda_convert_attr(c->vc_video_erase_char), - c->vc_screenbuf_size); - /* Tell console.c that it has to restore the screen itself */ - return true; - } else { - if (blank) - outb_p(0x00, mda_mode_port); /* disable video */ - else - outb_p(MDA_MODE_VIDEO_EN | MDA_MODE_BLINK_EN, - mda_mode_port); - return false; - } -} - -static void mdacon_cursor(struct vc_data *c, bool enable) -{ - if (!enable) { - mda_set_cursor(mda_vram_len - 1); - return; - } - - mda_set_cursor(c->state.y * mda_num_columns * 2 + c->state.x * 2); - - switch (CUR_SIZE(c->vc_cursor_type)) { - - case CUR_LOWER_THIRD: mda_set_cursor_size(10, 13); break; - case CUR_LOWER_HALF: mda_set_cursor_size(7, 13); break; - case CUR_TWO_THIRDS: mda_set_cursor_size(4, 13); break; - case CUR_BLOCK: mda_set_cursor_size(1, 13); break; - case CUR_NONE: mda_set_cursor_size(14, 13); break; - default: mda_set_cursor_size(12, 13); break; - } -} - -static bool mdacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, - enum con_scroll dir, unsigned int lines) -{ - u16 eattr = mda_convert_attr(c->vc_video_erase_char); - - if (!lines) - return false; - - if (lines > c->vc_rows) /* maximum realistic size */ - lines = c->vc_rows; - - switch (dir) { - - case SM_UP: - scr_memmovew(mda_addr(0, t), mda_addr(0, t + lines), - (b-t-lines)*mda_num_columns*2); - scr_memsetw(mda_addr(0, b - lines), eattr, - lines*mda_num_columns*2); - break; - - case SM_DOWN: - scr_memmovew(mda_addr(0, t + lines), mda_addr(0, t), - (b-t-lines)*mda_num_columns*2); - scr_memsetw(mda_addr(0, t), eattr, lines*mda_num_columns*2); - break; - } - - return false; -} - - -/* - * The console `switch' structure for the MDA based console - */ - -static const struct consw mda_con = { - .owner = THIS_MODULE, - .con_startup = mdacon_startup, - .con_init = mdacon_init, - .con_deinit = mdacon_deinit, - .con_clear = mdacon_clear, - .con_putcs = mdacon_putcs, - .con_cursor = mdacon_cursor, - .con_scroll = mdacon_scroll, - .con_switch = mdacon_switch, - .con_blank = mdacon_blank, - .con_build_attr = mdacon_build_attr, - .con_invert_region = mdacon_invert_region, -}; - -int __init mda_console_init(void) -{ - int err; - - if (mda_first_vc > mda_last_vc) - return 1; - console_lock(); - err = do_take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0); - console_unlock(); - return err; -} - -static void __exit mda_console_exit(void) -{ - give_up_console(&mda_con); -} - -module_init(mda_console_init); -module_exit(mda_console_exit); - -MODULE_DESCRIPTION("MDA based console driver"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 1c73d560f196..085d3a202148 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -453,19 +453,6 @@ config FB_N411 This enables support for the Apollo display controller in its Hecuba form using the n411 devkit. -config FB_HGA - tristate "Hercules mono graphics support" - depends on FB && X86 - select FB_IOMEM_FOPS - help - Say Y here if you have a Hercules mono graphics card. - - To compile this driver as a module, choose M here: the - module will be called hgafb. - - As this card technology is at least 25 years old, - most people will answer N here. - config FB_GBE bool "SGI Graphics Backend frame buffer support" depends on (FB = y) && HAS_IOMEM diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 36a18d958ba0..0b17c878154d 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -59,7 +59,6 @@ obj-$(CONFIG_FB_ATARI) += atafb.o c2p_iplan2.o atafb_mfb.o \ obj-$(CONFIG_FB_MAC) += macfb.o obj-$(CONFIG_FB_HECUBA) += hecubafb.o obj-$(CONFIG_FB_N411) += n411.o -obj-$(CONFIG_FB_HGA) += hgafb.o obj-$(CONFIG_FB_XVR500) += sunxvr500.o obj-$(CONFIG_FB_XVR2500) += sunxvr2500.o obj-$(CONFIG_FB_XVR1000) += sunxvr1000.o diff --git a/drivers/video/fbdev/arkfb.c b/drivers/video/fbdev/arkfb.c index 866c1165704e..195dbf4a5142 100644 --- a/drivers/video/fbdev/arkfb.c +++ b/drivers/video/fbdev/arkfb.c @@ -1167,8 +1167,8 @@ static const struct dev_pm_ops ark_pci_pm_ops = { /* List of boards that we are trying to support */ static const struct pci_device_id ark_devices[] = { - {PCI_DEVICE(0xEDD8, 0xA099)}, - {0, 0, 0, 0, 0, 0, 0} + { PCI_DEVICE(0xEDD8, 0xA099) }, + { } }; diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c index 9dfbc5310210..53f0156992e6 100644 --- a/drivers/video/fbdev/atmel_lcdfb.c +++ b/drivers/video/fbdev/atmel_lcdfb.c @@ -21,7 +21,6 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <video/of_videomode.h> #include <video/of_display_timing.h> #include <linux/regulator/consumer.h> @@ -56,7 +55,7 @@ struct atmel_lcdfb_info { struct atmel_lcdfb_pdata pdata; - struct atmel_lcdfb_config *config; + const struct atmel_lcdfb_config *config; struct regulator *reg_lcd; }; @@ -930,8 +929,7 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo) int ret; int i; - sinfo->config = (struct atmel_lcdfb_config*) - of_match_device(atmel_lcdfb_dt_ids, dev)->data; + sinfo->config = of_device_get_match_data(dev); display_np = of_parse_phandle(np, "display", 0); if (!display_np) { @@ -1062,7 +1060,7 @@ static int atmel_lcdfb_probe(struct platform_device *pdev) info->fbops = &atmel_lcdfb_ops; info->fix = atmel_lcdfb_fix; - strcpy(info->fix.id, sinfo->pdev->name); + strscpy(info->fix.id, sinfo->pdev->name); /* Enable LCDC Clocks */ sinfo->bus_clk = clk_get(dev, "hclk"); diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c index f55b4c7609a8..bcb10e66221c 100644 --- a/drivers/video/fbdev/aty/aty128fb.c +++ b/drivers/video/fbdev/aty/aty128fb.c @@ -180,101 +180,54 @@ static const struct dev_pm_ops aty128_pci_pm_ops = { /* supported Rage128 chipsets */ static const struct pci_device_id aty128_pci_tbl[] = { - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, - { 0, } + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_LE), .driver_data = rage_M3_pci }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_LF), .driver_data = rage_M3 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_MF), .driver_data = rage_M4 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_ML), .driver_data = rage_M4 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PA), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PB), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PC), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PD), .driver_data = rage_128_pro_pci }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PE), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PF), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PG), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PH), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PI), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PK), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PL), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PM), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PN), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PO), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PP), .driver_data = rage_128_pro_pci }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PR), .driver_data = rage_128_pro_pci }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PS), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PT), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PU), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PV), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PW), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PX), .driver_data = rage_128_pro }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RE), .driver_data = rage_128_pci }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RF), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RG), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RK), .driver_data = rage_128_pci }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RL), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SE), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SF), .driver_data = rage_128_pci }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SG), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SH), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SK), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SL), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SM), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SN), .driver_data = rage_128 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TF), .driver_data = rage_128_ultra }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TL), .driver_data = rage_128_ultra }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TR), .driver_data = rage_128_ultra }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TS), .driver_data = rage_128_ultra }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TT), .driver_data = rage_128_ultra }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TU), .driver_data = rage_128_ultra }, + { } }; MODULE_DEVICE_TABLE(pci, aty128_pci_tbl); diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index cb006484831b..b6b058cee751 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -95,7 +95,7 @@ #define MIN_MAPPED_VRAM (1024*768*1) #define CHIP_DEF(id, family, flags) \ - { PCI_VENDOR_ID_ATI, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (flags) | (CHIP_FAMILY_##family) } + { PCI_DEVICE(PCI_VENDOR_ID_ATI, id), .driver_data = (flags) | (CHIP_FAMILY_##family) } static const struct pci_device_id radeonfb_pci_table[] = { /* Radeon Xpress 200m */ @@ -2476,6 +2476,7 @@ static int radeonfb_pci_register(struct pci_dev *pdev, return 0; err_unmap_fb: iounmap(rinfo->fb_base); + fb_destroy_modelist(&info->modelist); err_unmap_rom: kfree(rinfo->mon1_EDID); kfree(rinfo->mon2_EDID); diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index c8ba098a8c42..582f1ee4c9b6 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -1072,12 +1072,14 @@ static int broadsheetfb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &broadsheetfb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_vfree; retval = fb_alloc_cmap(&info->cmap, 16, 0); if (retval < 0) { dev_err(&dev->dev, "Failed to allocate colormap\n"); - goto err_vfree; + goto err_fbdefio; } /* set cmap */ @@ -1121,6 +1123,8 @@ err_free_irq: board->cleanup(par); err_cmap: fb_dealloc_cmap(&info->cmap); +err_fbdefio: + fb_deferred_io_cleanup(info); err_vfree: vfree(videomemory); err_fb_rel: diff --git a/drivers/video/fbdev/carminefb.c b/drivers/video/fbdev/carminefb.c index bd4bff6a2484..fca50b7961eb 100644 --- a/drivers/video/fbdev/carminefb.c +++ b/drivers/video/fbdev/carminefb.c @@ -589,6 +589,7 @@ static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base, return 0; err_dealloc_cmap: + fb_destroy_modelist(&info->modelist); fb_dealloc_cmap(&info->cmap); err_free_fb: framebuffer_release(info); @@ -753,9 +754,8 @@ static void carminefb_remove(struct pci_dev *dev) #define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf static struct pci_device_id carmine_devices[] = { -{ - PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)}, - {0, 0, 0, 0, 0, 0, 0} + { PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b) }, + { } }; MODULE_DEVICE_TABLE(pci, carmine_devices); diff --git a/drivers/video/fbdev/chipsfb.c b/drivers/video/fbdev/chipsfb.c index 33caf0b99a45..946e30fcb6a4 100644 --- a/drivers/video/fbdev/chipsfb.c +++ b/drivers/video/fbdev/chipsfb.c @@ -526,4 +526,5 @@ static void __exit chipsfb_exit(void) pci_unregister_driver(&chipsfb_driver); } +MODULE_DESCRIPTION("Chips & Technologies 65550 frame buffer driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/cirrusfb.c b/drivers/video/fbdev/cirrusfb.c index e29217e476ea..2693b5cc053f 100644 --- a/drivers/video/fbdev/cirrusfb.c +++ b/drivers/video/fbdev/cirrusfb.c @@ -253,7 +253,7 @@ static const struct cirrusfb_board_info_rec { #ifdef CONFIG_PCI #define CHIP(id, btype) \ - { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) } + { PCI_VDEVICE(CIRRUS, id), .driver_data = (btype) } static struct pci_device_id cirrusfb_pci_table[] = { CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE), diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c index 4ebd16b7e3b8..ba1d0bc214c5 100644 --- a/drivers/video/fbdev/core/fb_chrdev.c +++ b/drivers/video/fbdev/core/fb_chrdev.c @@ -3,10 +3,10 @@ #include <linux/compat.h> #include <linux/console.h> #include <linux/fb.h> -#include <linux/fbcon.h> #include <linux/major.h> #include "fb_internal.h" +#include "fbcon.h" /* * We hold a reference to the fb_info in file->private_data, @@ -85,11 +85,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, var.activate &= ~FB_ACTIVATE_KD_TEXT; console_lock(); lock_fb_info(info); - ret = fbcon_modechange_possible(info, &var); - if (!ret) - ret = fb_set_var(info, &var); - if (!ret) - fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); + ret = fb_set_var_from_user(info, &var); unlock_fb_info(info); console_unlock(); if (!ret && copy_to_user(argp, &var, sizeof(var))) @@ -142,9 +138,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, return -EINVAL; console_lock(); lock_fb_info(info); - ret = fb_blank(info, arg); - /* might again call into fb_blank */ - fbcon_fb_blanked(info, arg); + ret = fb_blank_from_user(info, arg); unlock_fb_info(info); console_unlock(); break; diff --git a/drivers/video/fbdev/core/fb_internal.h b/drivers/video/fbdev/core/fb_internal.h index 613832d335fe..62e75bf15b9b 100644 --- a/drivers/video/fbdev/core/fb_internal.h +++ b/drivers/video/fbdev/core/fb_internal.h @@ -44,6 +44,7 @@ extern struct fb_info *registered_fb[FB_MAX]; extern int num_registered_fb; struct fb_info *get_fb_info(unsigned int idx); void put_fb_info(struct fb_info *fb_info); +int fb_blank_from_user(struct fb_info *info, int blank); /* fb_procfs.c */ #if defined(CONFIG_FB_DEVICE) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index b0e3e765360d..9077d3b99357 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -70,7 +70,6 @@ #include <linux/printk.h> #include <linux/slab.h> #include <linux/fb.h> -#include <linux/fbcon.h> #include <linux/vt_kern.h> #include <linux/selection.h> #include <linux/font.h> @@ -769,7 +768,7 @@ static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) return 0; } -#endif /* CONFIG_MISC_TILEBLITTING */ +#endif /* CONFIG_FB_TILEBLITTING */ static void fbcon_release(struct fb_info *info) { @@ -1440,8 +1439,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, struct vc_data **default_mode, *vc; struct vc_data *svc; struct fbcon_par *par = info->fbcon_par; - int rows, cols; - unsigned long ret = 0; + int rows, cols, ret; p = &fb_display[unit]; @@ -2602,8 +2600,9 @@ void fbcon_suspended(struct fb_info *info) return; vc = vc_cons[par->currcon].d; - /* Clear cursor, restore saved data */ - fbcon_cursor(vc, false); + /* Clear cursor, restore saved data when in text mode */ + if ((vc->vc_mode == KD_TEXT) && con_is_visible(vc)) + fbcon_cursor(vc, false); } void fbcon_resumed(struct fb_info *info) @@ -2615,7 +2614,9 @@ void fbcon_resumed(struct fb_info *info) return; vc = vc_cons[par->currcon].d; - update_screen(vc); + /* Update screen when in text mode only */ + if ((vc->vc_mode == KD_TEXT) && con_is_visible(vc)) + update_screen(vc); } static void fbcon_modechanged(struct fb_info *info) @@ -2699,7 +2700,6 @@ void fbcon_update_vcs(struct fb_info *info, bool all) else fbcon_modechanged(info); } -EXPORT_SYMBOL(fbcon_update_vcs); /* let fbcon check if it supports a new screen resolution */ int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var) @@ -2727,7 +2727,6 @@ int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *va return 0; } -EXPORT_SYMBOL_GPL(fbcon_modechange_possible); int fbcon_mode_deleted(struct fb_info *info, struct fb_videomode *mode) diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 321cc7f44baa..407d207b14f1 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -11,6 +11,7 @@ #ifndef _VIDEO_FBCON_H #define _VIDEO_FBCON_H +#include <linux/compiler_types.h> #include <linux/font.h> #include <linux/types.h> #include <linux/vt_buffer.h> @@ -19,6 +20,11 @@ #include <asm/io.h> +struct fb_blit_caps; +struct fb_info; +struct fb_var_screeninfo; +struct fb_videomode; + /* * This is the interface between the low-level console driver and the * low-level frame buffer device @@ -233,4 +239,48 @@ static inline int get_attribute(struct fb_info *info, u16 c) (void) (&_r == &_v); \ (i == FB_ROTATE_UR || i == FB_ROTATE_UD) ? _r : _v; }) +#ifdef CONFIG_FRAMEBUFFER_CONSOLE +void __init fb_console_init(void); +void __exit fb_console_exit(void); +int fbcon_fb_registered(struct fb_info *info); +void fbcon_fb_unregistered(struct fb_info *info); +void fbcon_fb_unbind(struct fb_info *info); +void fbcon_suspended(struct fb_info *info); +void fbcon_resumed(struct fb_info *info); +int fbcon_mode_deleted(struct fb_info *info, + struct fb_videomode *mode); +void fbcon_delete_modelist(struct list_head *head); +void fbcon_new_modelist(struct fb_info *info); +void fbcon_get_requirement(struct fb_info *info, + struct fb_blit_caps *caps); +void fbcon_fb_blanked(struct fb_info *info, int blank); +int fbcon_modechange_possible(struct fb_info *info, + struct fb_var_screeninfo *var); +void fbcon_update_vcs(struct fb_info *info, bool all); +void fbcon_remap_all(struct fb_info *info); +int fbcon_set_con2fb_map_ioctl(void __user *argp); +int fbcon_get_con2fb_map_ioctl(void __user *argp); +#else +static inline void fb_console_init(void) {} +static inline void fb_console_exit(void) {} +static inline int fbcon_fb_registered(struct fb_info *info) { return 0; } +static inline void fbcon_fb_unregistered(struct fb_info *info) {} +static inline void fbcon_fb_unbind(struct fb_info *info) {} +static inline void fbcon_suspended(struct fb_info *info) {} +static inline void fbcon_resumed(struct fb_info *info) {} +static inline int fbcon_mode_deleted(struct fb_info *info, + struct fb_videomode *mode) { return 0; } +static inline void fbcon_delete_modelist(struct list_head *head) {} +static inline void fbcon_new_modelist(struct fb_info *info) {} +static inline void fbcon_get_requirement(struct fb_info *info, + struct fb_blit_caps *caps) {} +static inline void fbcon_fb_blanked(struct fb_info *info, int blank) {} +static inline int fbcon_modechange_possible(struct fb_info *info, + struct fb_var_screeninfo *var) { return 0; } +static inline void fbcon_update_vcs(struct fb_info *info, bool all) {} +static inline void fbcon_remap_all(struct fb_info *info) {} +static inline int fbcon_set_con2fb_map_ioctl(void __user *argp) { return 0; } +static inline int fbcon_get_con2fb_map_ioctl(void __user *argp) { return 0; } +#endif + #endif /* _VIDEO_FBCON_H */ diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 30f2b59c47bf..e5221653ec2b 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -14,13 +14,13 @@ #include <linux/console.h> #include <linux/export.h> #include <linux/fb.h> -#include <linux/fbcon.h> #include <linux/lcd.h> #include <linux/leds.h> #include <video/nomodeset.h> #include "fb_internal.h" +#include "fbcon.h" /* * Frame buffer device initialization and setup routines @@ -346,6 +346,19 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) } EXPORT_SYMBOL(fb_set_var); +int fb_set_var_from_user(struct fb_info *info, struct fb_var_screeninfo *var) +{ + int ret = fbcon_modechange_possible(info, var); + + if (!ret) + ret = fb_set_var(info, var); + if (!ret) + fbcon_update_vcs(info, var->activate & FB_ACTIVATE_ALL); + + return ret; +} +EXPORT_SYMBOL(fb_set_var_from_user); + static void fb_lcd_notify_blank(struct fb_info *info) { int power; @@ -409,6 +422,16 @@ err: } EXPORT_SYMBOL(fb_blank); +int fb_blank_from_user(struct fb_info *info, int blank) +{ + int ret = fb_blank(info, blank); + + /* might again call into fb_blank */ + fbcon_fb_blanked(info, blank); + + return ret; +} + static int fb_check_foreignness(struct fb_info *fi) { const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN; @@ -661,6 +684,16 @@ void fb_set_suspend(struct fb_info *info, int state) } EXPORT_SYMBOL(fb_set_suspend); +/** + * fb_switch_outputs - framebuffer got the outputs from vga-switcheroo + * @info: framebuffer + */ +void fb_switch_outputs(struct fb_info *info) +{ + fbcon_remap_all(info); +} +EXPORT_SYMBOL(fb_switch_outputs); + static int __init fbmem_init(void) { int ret; diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index baa2bae0fb5b..d9743ef35355 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -7,7 +7,6 @@ #include <linux/console.h> #include <linux/fb.h> -#include <linux/fbcon.h> #include <linux/major.h> #include "fb_internal.h" @@ -19,9 +18,7 @@ static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) var->activate |= FB_ACTIVATE_FORCE; console_lock(); lock_fb_info(fb_info); - err = fb_set_var(fb_info, var); - if (!err) - fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL); + err = fb_set_var_from_user(fb_info, var); unlock_fb_info(fb_info); console_unlock(); if (err) @@ -231,9 +228,7 @@ static ssize_t store_blank(struct device *device, arg = simple_strtoul(buf, &last, 0); console_lock(); - err = fb_blank(fb_info, arg); - /* might again call into fb_blank */ - fbcon_fb_blanked(fb_info, arg); + err = fb_blank_from_user(fb_info, arg); console_unlock(); if (err < 0) return err; diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 703d0b7aec32..e1fd9298a702 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -259,7 +259,7 @@ static const struct fb_videomode modedb[] = { FB_VMODE_DOUBLE }, /* 1920x1080 @ 60 Hz, 67.3 kHz hsync */ - { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, 0, + { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, @@ -626,7 +626,7 @@ int fb_find_mode(struct fb_var_screeninfo *var, const struct fb_videomode *default_mode, unsigned int default_bpp) { - char *mode_option_buf = NULL; + char *mode_option_buf __free(kfree) = NULL; int i; /* Set up defaults */ @@ -724,7 +724,6 @@ int fb_find_mode(struct fb_var_screeninfo *var, res_specified = 1; } done: - kfree(mode_option_buf); if (cvt) { struct fb_videomode cvt_mode; int ret; diff --git a/drivers/video/fbdev/cyber2000fb.c b/drivers/video/fbdev/cyber2000fb.c index 2d12f8e96c7e..5c7349722125 100644 --- a/drivers/video/fbdev/cyber2000fb.c +++ b/drivers/video/fbdev/cyber2000fb.c @@ -1384,7 +1384,7 @@ static struct cfb_info *cyberpro_alloc_fb_info(unsigned int id, char *name) else cfb->divisors[3] = 6; - strcpy(cfb->fb.fix.id, name); + strscpy(cfb->fb.fix.id, name); cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; cfb->fb.fix.type_aux = 0; @@ -1796,16 +1796,22 @@ static int __maybe_unused cyberpro_pci_resume(struct device *dev) static struct pci_device_id cyberpro_pci_table[] = { /* Not yet - * { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682, - * PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_IGA_1682 }, + * { + * PCI_VDEVICE(INTERG, PCI_DEVICE_ID_INTERG_1682), + * .driver_data = ID_IGA_1682, + * }, */ - { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2000, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2000 }, - { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2010, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2010 }, - { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5000, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_5000 }, - { 0, } + { + PCI_VDEVICE(INTERG, PCI_DEVICE_ID_INTERG_2000), + .driver_data = ID_CYBERPRO_2000, + }, { + PCI_VDEVICE(INTERG, PCI_DEVICE_ID_INTERG_2010), + .driver_data = ID_CYBERPRO_2010, + }, { + PCI_VDEVICE(INTERG, PCI_DEVICE_ID_INTERG_5000), + .driver_data = ID_CYBERPRO_5000, + }, + { } }; MODULE_DEVICE_TABLE(pci, cyberpro_pci_table); diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index 47ebc0107209..ad8dec7807c3 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -377,6 +377,7 @@ static int efifb_probe(struct platform_device *dev) if (fb_get_options("efifb", &option)) return -ENODEV; efifb_setup(si, option); + kfree(option); /* We don't get linelength from UGA Draw Protocol, only from * EFI Graphics Protocol. So if it's not in DMI, and it's not diff --git a/drivers/video/fbdev/geode/gx1fb_core.c b/drivers/video/fbdev/geode/gx1fb_core.c index a1919c1934ac..7cca46891aef 100644 --- a/drivers/video/fbdev/geode/gx1fb_core.c +++ b/drivers/video/fbdev/geode/gx1fb_core.c @@ -423,10 +423,12 @@ static void __init gx1fb_setup(char *options) #endif static struct pci_device_id gx1fb_id_table[] = { - { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_VIDEO, - PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, - 0xff0000, 0 }, - { 0, } + { + PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_VIDEO), + .class = PCI_BASE_CLASS_DISPLAY << 16, + .class_mask = 0xff0000, + }, + { } }; MODULE_DEVICE_TABLE(pci, gx1fb_id_table); diff --git a/drivers/video/fbdev/grvga.c b/drivers/video/fbdev/grvga.c index de8ab817d406..a6594bcd74e8 100644 --- a/drivers/video/fbdev/grvga.c +++ b/drivers/video/fbdev/grvga.c @@ -33,7 +33,7 @@ struct grvga_regs { u32 line_length; /* 0x10 */ u32 fb_pos; /* 0x14 */ u32 clk_vector[4]; /* 0x18 */ - u32 clut; /* 0x20 */ + u32 clut; /* 0x28 */ }; struct grvga_par { diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c index 3547d58a29cf..dd2af980f3d8 100644 --- a/drivers/video/fbdev/hecubafb.c +++ b/drivers/video/fbdev/hecubafb.c @@ -192,7 +192,9 @@ static int hecubafb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &hecubafb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_fbdefio; retval = register_framebuffer(info); if (retval < 0) @@ -209,6 +211,8 @@ static int hecubafb_probe(struct platform_device *dev) return 0; err_fbreg: + fb_deferred_io_cleanup(info); +err_fbdefio: framebuffer_release(info); err_fballoc: vfree(videomemory); diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c deleted file mode 100644 index d32fd1c5217c..000000000000 --- a/drivers/video/fbdev/hgafb.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - * linux/drivers/video/hgafb.c -- Hercules graphics adaptor frame buffer device - * - * Created 25 Nov 1999 by Ferenc Bakonyi (fero@drama.obuda.kando.hu) - * Based on skeletonfb.c by Geert Uytterhoeven and - * mdacon.c by Andrew Apted - * - * History: - * - * - Revision 0.1.8 (23 Oct 2002): Ported to new framebuffer api. - * - * - Revision 0.1.7 (23 Jan 2001): fix crash resulting from MDA only cards - * being detected as Hercules. (Paul G.) - * - Revision 0.1.6 (17 Aug 2000): new style structs - * documentation - * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc - * minor fixes - * - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for - * HGA-only systems - * - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure - * screen is cleared after rmmod - * virtual resolutions - * module parameter 'nologo={0|1}' - * the most important: boot logo :) - * - Revision 0.1.0 (6 Dec 1999): faster scrolling and minor fixes - * - First release (25 Nov 1999) - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/spinlock.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/delay.h> -#include <linux/fb.h> -#include <linux/init.h> -#include <linux/ioport.h> -#include <linux/platform_device.h> -#include <asm/io.h> -#include <asm/vga.h> - -#if 0 -#define DPRINTK(args...) printk(KERN_DEBUG __FILE__": " ##args) -#else -#define DPRINTK(args...) -#endif - -#if 0 -#define CHKINFO(ret) if (info != &fb_info) { printk(KERN_DEBUG __FILE__": This should never happen, line:%d \n", __LINE__); return ret; } -#else -#define CHKINFO(ret) -#endif - -/* Description of the hardware layout */ - -static void __iomem *hga_vram; /* Base of video memory */ -static unsigned long hga_vram_len; /* Size of video memory */ - -#define HGA_ROWADDR(row) ((row%4)*8192 + (row>>2)*90) -#define HGA_TXT 0 -#define HGA_GFX 1 - -static inline u8 __iomem * rowaddr(struct fb_info *info, u_int row) -{ - return info->screen_base + HGA_ROWADDR(row); -} - -static int hga_mode = -1; /* 0 = txt, 1 = gfx mode */ - -static enum { TYPE_HERC, TYPE_HERCPLUS, TYPE_HERCCOLOR } hga_type; -static char *hga_type_name; - -#define HGA_INDEX_PORT 0x3b4 /* Register select port */ -#define HGA_VALUE_PORT 0x3b5 /* Register value port */ -#define HGA_MODE_PORT 0x3b8 /* Mode control port */ -#define HGA_STATUS_PORT 0x3ba /* Status and Config port */ -#define HGA_GFX_PORT 0x3bf /* Graphics control port */ - -/* HGA register values */ - -#define HGA_CURSOR_BLINKING 0x00 -#define HGA_CURSOR_OFF 0x20 -#define HGA_CURSOR_SLOWBLINK 0x60 - -#define HGA_MODE_GRAPHICS 0x02 -#define HGA_MODE_VIDEO_EN 0x08 -#define HGA_MODE_BLINK_EN 0x20 -#define HGA_MODE_GFX_PAGE1 0x80 - -#define HGA_STATUS_HSYNC 0x01 -#define HGA_STATUS_VSYNC 0x80 -#define HGA_STATUS_VIDEO 0x08 - -#define HGA_CONFIG_COL132 0x08 -#define HGA_GFX_MODE_EN 0x01 -#define HGA_GFX_PAGE_EN 0x02 - -/* Global locks */ - -static DEFINE_SPINLOCK(hga_reg_lock); - -/* Framebuffer driver structures */ - -static const struct fb_var_screeninfo hga_default_var = { - .xres = 720, - .yres = 348, - .xres_virtual = 720, - .yres_virtual = 348, - .bits_per_pixel = 1, - .red = {0, 1, 0}, - .green = {0, 1, 0}, - .blue = {0, 1, 0}, - .transp = {0, 0, 0}, - .height = -1, - .width = -1, -}; - -static struct fb_fix_screeninfo hga_fix = { - .id = "HGA", - .type = FB_TYPE_PACKED_PIXELS, /* (not sure) */ - .visual = FB_VISUAL_MONO10, - .xpanstep = 8, - .ypanstep = 8, - .line_length = 90, - .accel = FB_ACCEL_NONE -}; - -/* Don't assume that tty1 will be the initial current console. */ -static int release_io_port = 0; -static int release_io_ports = 0; -static bool nologo = 0; - -/* ------------------------------------------------------------------------- - * - * Low level hardware functions - * - * ------------------------------------------------------------------------- */ - -static void write_hga_b(unsigned int val, unsigned char reg) -{ - outb_p(reg, HGA_INDEX_PORT); - outb_p(val, HGA_VALUE_PORT); -} - -static void write_hga_w(unsigned int val, unsigned char reg) -{ - outb_p(reg, HGA_INDEX_PORT); outb_p(val >> 8, HGA_VALUE_PORT); - outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT); -} - -static int test_hga_b(unsigned char val, unsigned char reg) -{ - outb_p(reg, HGA_INDEX_PORT); - outb (val, HGA_VALUE_PORT); - udelay(20); val = (inb_p(HGA_VALUE_PORT) == val); - return val; -} - -static void hga_clear_screen(void) -{ - unsigned char fillchar = 0xbf; /* magic */ - unsigned long flags; - - spin_lock_irqsave(&hga_reg_lock, flags); - if (hga_mode == HGA_TXT) - fillchar = ' '; - else if (hga_mode == HGA_GFX) - fillchar = 0x00; - spin_unlock_irqrestore(&hga_reg_lock, flags); - if (fillchar != 0xbf) - memset_io(hga_vram, fillchar, hga_vram_len); -} - -static void hga_txt_mode(void) -{ - unsigned long flags; - - spin_lock_irqsave(&hga_reg_lock, flags); - outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT); - outb_p(0x00, HGA_GFX_PORT); - outb_p(0x00, HGA_STATUS_PORT); - - write_hga_b(0x61, 0x00); /* horizontal total */ - write_hga_b(0x50, 0x01); /* horizontal displayed */ - write_hga_b(0x52, 0x02); /* horizontal sync pos */ - write_hga_b(0x0f, 0x03); /* horizontal sync width */ - - write_hga_b(0x19, 0x04); /* vertical total */ - write_hga_b(0x06, 0x05); /* vertical total adjust */ - write_hga_b(0x19, 0x06); /* vertical displayed */ - write_hga_b(0x19, 0x07); /* vertical sync pos */ - - write_hga_b(0x02, 0x08); /* interlace mode */ - write_hga_b(0x0d, 0x09); /* maximum scanline */ - write_hga_b(0x0c, 0x0a); /* cursor start */ - write_hga_b(0x0d, 0x0b); /* cursor end */ - - write_hga_w(0x0000, 0x0c); /* start address */ - write_hga_w(0x0000, 0x0e); /* cursor location */ - - hga_mode = HGA_TXT; - spin_unlock_irqrestore(&hga_reg_lock, flags); -} - -static void hga_gfx_mode(void) -{ - unsigned long flags; - - spin_lock_irqsave(&hga_reg_lock, flags); - outb_p(0x00, HGA_STATUS_PORT); - outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT); - outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT); - - write_hga_b(0x35, 0x00); /* horizontal total */ - write_hga_b(0x2d, 0x01); /* horizontal displayed */ - write_hga_b(0x2e, 0x02); /* horizontal sync pos */ - write_hga_b(0x07, 0x03); /* horizontal sync width */ - - write_hga_b(0x5b, 0x04); /* vertical total */ - write_hga_b(0x02, 0x05); /* vertical total adjust */ - write_hga_b(0x57, 0x06); /* vertical displayed */ - write_hga_b(0x57, 0x07); /* vertical sync pos */ - - write_hga_b(0x02, 0x08); /* interlace mode */ - write_hga_b(0x03, 0x09); /* maximum scanline */ - write_hga_b(0x00, 0x0a); /* cursor start */ - write_hga_b(0x00, 0x0b); /* cursor end */ - - write_hga_w(0x0000, 0x0c); /* start address */ - write_hga_w(0x0000, 0x0e); /* cursor location */ - - hga_mode = HGA_GFX; - spin_unlock_irqrestore(&hga_reg_lock, flags); -} - -static void hga_show_logo(struct fb_info *info) -{ -/* - void __iomem *dest = hga_vram; - char *logo = linux_logo_bw; - int x, y; - - for (y = 134; y < 134 + 80 ; y++) * this needs some cleanup * - for (x = 0; x < 10 ; x++) - writeb(~*(logo++),(dest + HGA_ROWADDR(y) + x + 40)); -*/ -} - -static void hga_pan(unsigned int xoffset, unsigned int yoffset) -{ - unsigned int base; - unsigned long flags; - - base = (yoffset / 8) * 90 + xoffset; - spin_lock_irqsave(&hga_reg_lock, flags); - write_hga_w(base, 0x0c); /* start address */ - spin_unlock_irqrestore(&hga_reg_lock, flags); - DPRINTK("hga_pan: base:%d\n", base); -} - -static void hga_blank(int blank_mode) -{ - unsigned long flags; - - spin_lock_irqsave(&hga_reg_lock, flags); - if (blank_mode) { - outb_p(0x00, HGA_MODE_PORT); /* disable video */ - } else { - outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT); - } - spin_unlock_irqrestore(&hga_reg_lock, flags); -} - -static int hga_card_detect(struct platform_device *pdev) -{ - int count = 0; - void __iomem *p, *q; - unsigned short p_save, q_save; - - hga_vram_len = 0x08000; - - if (!devm_request_mem_region(&pdev->dev, 0xb0000, hga_vram_len, "hgafb")) { - dev_err(&pdev->dev, "cannot reserve video memory at 0xb0000\n"); - return -EBUSY; - } - - hga_vram = ioremap(0xb0000, hga_vram_len); - if (!hga_vram) - return -ENOMEM; - - if (request_region(0x3b0, 12, "hgafb")) - release_io_ports = 1; - if (request_region(0x3bf, 1, "hgafb")) - release_io_port = 1; - - /* do a memory check */ - - p = hga_vram; - q = hga_vram + 0x01000; - - p_save = readw(p); q_save = readw(q); - - writew(0xaa55, p); if (readw(p) == 0xaa55) count++; - writew(0x55aa, p); if (readw(p) == 0x55aa) count++; - writew(p_save, p); - - if (count != 2) - goto error; - - /* Ok, there is definitely a card registering at the correct - * memory location, so now we do an I/O port test. - */ - - if (!test_hga_b(0x66, 0x0f)) /* cursor low register */ - goto error; - - if (!test_hga_b(0x99, 0x0f)) /* cursor low register */ - goto error; - - /* See if the card is a Hercules, by checking whether the vsync - * bit of the status register is changing. This test lasts for - * approximately 1/10th of a second. - */ - - p_save = q_save = inb_p(HGA_STATUS_PORT) & HGA_STATUS_VSYNC; - - for (count=0; count < 50000 && p_save == q_save; count++) { - q_save = inb(HGA_STATUS_PORT) & HGA_STATUS_VSYNC; - udelay(2); - } - - if (p_save == q_save) - goto error; - - switch (inb_p(HGA_STATUS_PORT) & 0x70) { - case 0x10: - hga_type = TYPE_HERCPLUS; - hga_type_name = "HerculesPlus"; - break; - case 0x50: - hga_type = TYPE_HERCCOLOR; - hga_type_name = "HerculesColor"; - break; - default: - hga_type = TYPE_HERC; - hga_type_name = "Hercules"; - break; - } - return 0; -error: - if (release_io_ports) - release_region(0x3b0, 12); - if (release_io_port) - release_region(0x3bf, 1); - - iounmap(hga_vram); - - pr_err("hgafb: HGA card not detected.\n"); - - return -EINVAL; -} - -/** - * hgafb_open - open the framebuffer device - * @info: pointer to fb_info object containing info for current hga board - * @init: open by console system or userland. - * - * Returns: %0 - */ - -static int hgafb_open(struct fb_info *info, int init) -{ - hga_gfx_mode(); - hga_clear_screen(); - if (!nologo) hga_show_logo(info); - return 0; -} - -/** - * hgafb_release - open the framebuffer device - * @info: pointer to fb_info object containing info for current hga board - * @init: open by console system or userland. - * - * Returns: %0 - */ - -static int hgafb_release(struct fb_info *info, int init) -{ - hga_txt_mode(); - hga_clear_screen(); - return 0; -} - -/** - * hgafb_setcolreg - set color registers - * @regno:register index to set - * @red:red value, unused - * @green:green value, unused - * @blue:blue value, unused - * @transp:transparency value, unused - * @info:unused - * - * This callback function is used to set the color registers of a HGA - * board. Since we have only two fixed colors only @regno is checked. - * A zero is returned on success and 1 for failure. - * - * Returns: %0 - */ - -static int hgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info) -{ - if (regno > 1) - return 1; - return 0; -} - -/** - * hgafb_pan_display - pan or wrap the display - * @var:contains new xoffset, yoffset and vmode values - * @info:pointer to fb_info object containing info for current hga board - * - * This function looks only at xoffset, yoffset and the %FB_VMODE_YWRAP - * flag in @var. If input parameters are correct it calls hga_pan() to - * program the hardware. @info->var is updated to the new values. - * - * Returns: %0 on success or %-EINVAL for failure. - */ - -static int hgafb_pan_display(struct fb_var_screeninfo *var, - struct fb_info *info) -{ - if (var->vmode & FB_VMODE_YWRAP) { - if (var->yoffset >= info->var.yres_virtual || - var->xoffset) - return -EINVAL; - } else { - if (var->xoffset + info->var.xres > info->var.xres_virtual - || var->yoffset + info->var.yres > info->var.yres_virtual - || var->yoffset % 8) - return -EINVAL; - } - - hga_pan(var->xoffset, var->yoffset); - return 0; -} - -/** - * hgafb_blank - (un)blank the screen - * @blank_mode:blanking method to use - * @info:unused - * - * Blank the screen if blank_mode != 0, else unblank. - * Implements VESA suspend and powerdown modes on hardware that supports - * disabling hsync/vsync: - * @blank_mode == 2 means suspend vsync, - * @blank_mode == 3 means suspend hsync, - * @blank_mode == 4 means powerdown. - * - * Returns: %0 - */ - -static int hgafb_blank(int blank_mode, struct fb_info *info) -{ - hga_blank(blank_mode); - return 0; -} - -/* - * Accel functions - */ -static void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) -{ - u_int rows, y; - u8 __iomem *dest; - - y = rect->dy; - - for (rows = rect->height; rows--; y++) { - dest = rowaddr(info, y) + (rect->dx >> 3); - switch (rect->rop) { - case ROP_COPY: - memset_io(dest, rect->color, (rect->width >> 3)); - break; - case ROP_XOR: - fb_writeb(~(fb_readb(dest)), dest); - break; - } - } -} - -static void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) -{ - u_int rows, y1, y2; - u8 __iomem *src; - u8 __iomem *dest; - - if (area->dy <= area->sy) { - y1 = area->sy; - y2 = area->dy; - - for (rows = area->height; rows--; ) { - src = rowaddr(info, y1) + (area->sx >> 3); - dest = rowaddr(info, y2) + (area->dx >> 3); - memmove(dest, src, (area->width >> 3)); - y1++; - y2++; - } - } else { - y1 = area->sy + area->height - 1; - y2 = area->dy + area->height - 1; - - for (rows = area->height; rows--;) { - src = rowaddr(info, y1) + (area->sx >> 3); - dest = rowaddr(info, y2) + (area->dx >> 3); - memmove(dest, src, (area->width >> 3)); - y1--; - y2--; - } - } -} - -static void hgafb_imageblit(struct fb_info *info, const struct fb_image *image) -{ - u8 __iomem *dest; - u8 *cdat = (u8 *) image->data; - u_int rows, y = image->dy; - u_int x; - u8 d; - - for (rows = image->height; rows--; y++) { - for (x = 0; x < image->width; x+= 8) { - d = *cdat++; - dest = rowaddr(info, y) + ((image->dx + x)>> 3); - fb_writeb(d, dest); - } - } -} - -static const struct fb_ops hgafb_ops = { - .owner = THIS_MODULE, - .fb_open = hgafb_open, - .fb_release = hgafb_release, - __FB_DEFAULT_IOMEM_OPS_RDWR, - .fb_setcolreg = hgafb_setcolreg, - .fb_pan_display = hgafb_pan_display, - .fb_blank = hgafb_blank, - .fb_fillrect = hgafb_fillrect, - .fb_copyarea = hgafb_copyarea, - .fb_imageblit = hgafb_imageblit, - __FB_DEFAULT_IOMEM_OPS_MMAP, -}; - -/* ------------------------------------------------------------------------- * - * - * Functions in fb_info - * - * ------------------------------------------------------------------------- */ - -/* ------------------------------------------------------------------------- */ - - /* - * Initialization - */ - -static int hgafb_probe(struct platform_device *pdev) -{ - struct fb_info *info; - int ret; - - ret = hga_card_detect(pdev); - if (ret) - return ret; - - printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n", - hga_type_name, hga_vram_len/1024); - - info = framebuffer_alloc(0, &pdev->dev); - if (!info) { - iounmap(hga_vram); - return -ENOMEM; - } - - hga_fix.smem_start = (unsigned long)hga_vram; - hga_fix.smem_len = hga_vram_len; - - info->flags = FBINFO_HWACCEL_YPAN; - info->var = hga_default_var; - info->fix = hga_fix; - info->monspecs.hfmin = 0; - info->monspecs.hfmax = 0; - info->monspecs.vfmin = 10000; - info->monspecs.vfmax = 10000; - info->monspecs.dpms = 0; - info->fbops = &hgafb_ops; - info->screen_base = hga_vram; - - if (register_framebuffer(info) < 0) { - framebuffer_release(info); - iounmap(hga_vram); - return -EINVAL; - } - - fb_info(info, "%s frame buffer device\n", info->fix.id); - platform_set_drvdata(pdev, info); - return 0; -} - -static void hgafb_remove(struct platform_device *pdev) -{ - struct fb_info *info = platform_get_drvdata(pdev); - - hga_txt_mode(); - hga_clear_screen(); - - if (info) { - unregister_framebuffer(info); - framebuffer_release(info); - } - - iounmap(hga_vram); - - if (release_io_ports) - release_region(0x3b0, 12); - - if (release_io_port) - release_region(0x3bf, 1); -} - -static struct platform_driver hgafb_driver = { - .probe = hgafb_probe, - .remove = hgafb_remove, - .driver = { - .name = "hgafb", - }, -}; - -static struct platform_device *hgafb_device; - -static int __init hgafb_init(void) -{ - int ret; - - if (fb_get_options("hgafb", NULL)) - return -ENODEV; - - ret = platform_driver_register(&hgafb_driver); - - if (!ret) { - hgafb_device = platform_device_register_simple("hgafb", 0, NULL, 0); - - if (IS_ERR(hgafb_device)) { - platform_driver_unregister(&hgafb_driver); - ret = PTR_ERR(hgafb_device); - } - } - - return ret; -} - -static void __exit hgafb_exit(void) -{ - platform_device_unregister(hgafb_device); - platform_driver_unregister(&hgafb_driver); -} - -/* ------------------------------------------------------------------------- - * - * Modularization - * - * ------------------------------------------------------------------------- */ - -MODULE_AUTHOR("Ferenc Bakonyi <fero@drama.obuda.kando.hu>"); -MODULE_DESCRIPTION("FBDev driver for Hercules Graphics Adaptor"); -MODULE_LICENSE("GPL"); - -module_param(nologo, bool, 0); -MODULE_PARM_DESC(nologo, "Disables startup logo if != 0 (default=0)"); -module_init(hgafb_init); -module_exit(hgafb_exit); diff --git a/drivers/video/fbdev/i740fb.c b/drivers/video/fbdev/i740fb.c index 9b74dae71472..c14a19382769 100644 --- a/drivers/video/fbdev/i740fb.c +++ b/drivers/video/fbdev/i740fb.c @@ -1152,6 +1152,7 @@ err_reg_framebuffer: fb_dealloc_cmap(&info->cmap); err_alloc_cmap: err_find_mode: + fb_destroy_modelist(&info->modelist); if (par->ddc_registered) i2c_del_adapter(&par->ddc_adapter); pci_iounmap(dev, par->regs); diff --git a/drivers/video/fbdev/imxfb.c b/drivers/video/fbdev/imxfb.c index a077bf346bdf..7a021da0a32a 100644 --- a/drivers/video/fbdev/imxfb.c +++ b/drivers/video/fbdev/imxfb.c @@ -30,7 +30,6 @@ #include <linux/lcd.h> #include <linux/math64.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/bitfield.h> #include <linux/regulator/consumer.h> @@ -880,7 +879,6 @@ static int imxfb_probe(struct platform_device *pdev) struct lcd_device *lcd; struct fb_info *info; struct imx_fb_videomode *m; - const struct of_device_id *of_id; struct device_node *display_np; int ret, i; int bytes_per_pixel; @@ -891,9 +889,7 @@ static int imxfb_probe(struct platform_device *pdev) if (ret < 0) return ret; - of_id = of_match_device(imxfb_of_dev_id, &pdev->dev); - if (of_id) - pdev->id_entry = of_id->data; + pdev->id_entry = of_device_get_match_data(&pdev->dev); info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev); if (!info) diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c index c8b1dfa456a3..d756b3603fa6 100644 --- a/drivers/video/fbdev/kyro/fbdev.c +++ b/drivers/video/fbdev/kyro/fbdev.c @@ -645,9 +645,8 @@ static int kyrofb_ioctl(struct fb_info *info, } static const struct pci_device_id kyrofb_pci_tbl[] = { - { PCI_VENDOR_ID_ST, PCI_DEVICE_ID_STG4000, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, } + { PCI_DEVICE(PCI_VENDOR_ID_ST, PCI_DEVICE_ID_STG4000) }, + { } }; MODULE_DEVICE_TABLE(pci, kyrofb_pci_tbl); diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c index e1a4bc7c2318..ac04a19b6849 100644 --- a/drivers/video/fbdev/matrox/matroxfb_base.c +++ b/drivers/video/fbdev/matrox/matroxfb_base.c @@ -1642,8 +1642,8 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) int err; static const struct pci_device_id intel_82437[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437) }, - { }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82437) }, + { } }; DBG(__func__) @@ -2135,35 +2135,23 @@ static void pci_remove_matrox(struct pci_dev* pdev) { static const struct pci_device_id matroxfb_devices[] = { #ifdef CONFIG_FB_MATROX_MILLENIUM - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_MIL) }, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_MIL_2) }, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP) }, #endif #ifdef CONFIG_FB_MATROX_MYSTIQUE - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_MYS) }, #endif #ifdef CONFIG_FB_MATROX_G - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_PCI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, 0x0532, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_G100_MM) }, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_G100_AGP) }, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_G200_PCI) }, + { PCI_VDEVICE(MATROX, 0x0532) }, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_G200_AGP) }, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_G400) }, + { PCI_VDEVICE(MATROX, PCI_DEVICE_ID_MATROX_G550) }, #endif - {0, 0, - 0, 0, 0, 0, 0} + { } }; MODULE_DEVICE_TABLE(pci, matroxfb_devices); diff --git a/drivers/video/fbdev/matrox/matroxfb_maven.c b/drivers/video/fbdev/matrox/matroxfb_maven.c index 2ea65da6075c..fe057a0b57ec 100644 --- a/drivers/video/fbdev/matrox/matroxfb_maven.c +++ b/drivers/video/fbdev/matrox/matroxfb_maven.c @@ -1282,7 +1282,7 @@ static void maven_remove(struct i2c_client *client) } static const struct i2c_device_id maven_id[] = { - { "maven" }, + { .name = "maven" }, { } }; MODULE_DEVICE_TABLE(i2c, maven_id); diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index 6f0942c6e5f1..83c614963a0a 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -645,12 +645,14 @@ static int metronomefb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &metronomefb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_free_irq; retval = fb_alloc_cmap(&info->cmap, 8, 0); if (retval < 0) { dev_err(&dev->dev, "Failed to allocate colormap\n"); - goto err_free_irq; + goto err_fbdefio; } /* set cmap */ @@ -673,6 +675,8 @@ static int metronomefb_probe(struct platform_device *dev) err_cmap: fb_dealloc_cmap(&info->cmap); +err_fbdefio: + fb_deferred_io_cleanup(info); err_free_irq: board->cleanup(par); err_csum_table: diff --git a/drivers/video/fbdev/mmp/fb/mmpfb.c b/drivers/video/fbdev/mmp/fb/mmpfb.c index 694587256e06..fc09d3ec3487 100644 --- a/drivers/video/fbdev/mmp/fb/mmpfb.c +++ b/drivers/video/fbdev/mmp/fb/mmpfb.c @@ -502,7 +502,7 @@ static int fb_info_setup(struct fb_info *info, info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; info->node = -1; - strcpy(info->fix.id, fbi->name); + strscpy(info->fix.id, fbi->name); info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.type_aux = 0; info->fix.xpanstep = 0; diff --git a/drivers/video/fbdev/neofb.c b/drivers/video/fbdev/neofb.c index c1cd028b8991..e0b8d4d6ce79 100644 --- a/drivers/video/fbdev/neofb.c +++ b/drivers/video/fbdev/neofb.c @@ -2126,34 +2126,25 @@ static void neofb_remove(struct pci_dev *dev) } static const struct pci_device_id neofb_devices[] = { - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2070, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2070}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2070), .driver_data = FB_ACCEL_NEOMAGIC_NM2070 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2090, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2090}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2090), .driver_data = FB_ACCEL_NEOMAGIC_NM2090 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2093, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2093}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2093), .driver_data = FB_ACCEL_NEOMAGIC_NM2093 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2097, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2097}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2097), .driver_data = FB_ACCEL_NEOMAGIC_NM2097 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2160, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2160}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2160), .driver_data = FB_ACCEL_NEOMAGIC_NM2160 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2200}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2200), .driver_data = FB_ACCEL_NEOMAGIC_NM2200 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2230, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2230}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2230), .driver_data = FB_ACCEL_NEOMAGIC_NM2230 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2360, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2360}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2360), .driver_data = FB_ACCEL_NEOMAGIC_NM2360 }, - {PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2380, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2380}, + { PCI_VDEVICE(NEOMAGIC, PCI_CHIP_NM2380), .driver_data = FB_ACCEL_NEOMAGIC_NM2380 }, - {0, 0, 0, 0, 0, 0, 0} + { } }; MODULE_DEVICE_TABLE(pci, neofb_devices); diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c index 72b85f475605..7d20c4087aeb 100644 --- a/drivers/video/fbdev/nvidia/nvidia.c +++ b/drivers/video/fbdev/nvidia/nvidia.c @@ -58,9 +58,12 @@ #define MAX_CURS 32 static const struct pci_device_id nvidiafb_pci_tbl[] = { - {PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0}, - { 0, } + { + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), + .class = PCI_BASE_CLASS_DISPLAY << 16, + .class_mask = 0xff0000 + }, + { } }; MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl); @@ -1421,6 +1424,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) err_out_iounmap_fb: iounmap(info->screen_base); + fb_destroy_modelist(&info->modelist); err_out_free_base1: fb_destroy_modedb(info->monspecs.modedb); nvidia_delete_i2c_busses(par); diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index d70deb6a9150..046892682fc6 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -1099,7 +1099,11 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) rg = omapfb_get_mem_region(ofbi->region); - start = omapfb_get_region_paddr(ofbi); + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + start = rg->vrfb.paddr[0]; + else + start = rg->paddr; + len = fix->smem_len; DBG("user mmap region start %lx, len %d, off %lx\n", start, len, @@ -1109,6 +1113,8 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) vma->vm_ops = &mmap_user_ops; vma->vm_private_data = rg; + atomic_inc(&rg->map_count); + r = vm_iomap_memory(vma, start, len); if (r) goto error; @@ -1121,6 +1127,7 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) return 0; error: + atomic_dec(&rg->map_count); omapfb_put_mem_region(rg); return r; diff --git a/drivers/video/fbdev/pm2fb.c b/drivers/video/fbdev/pm2fb.c index f34429829b7d..412ff249b5c7 100644 --- a/drivers/video/fbdev/pm2fb.c +++ b/drivers/video/fbdev/pm2fb.c @@ -1748,13 +1748,10 @@ static void pm2fb_remove(struct pci_dev *pdev) } static const struct pci_device_id pm2fb_id_table[] = { - { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TVP4020, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2V, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, } + { PCI_VDEVICE(TI, PCI_DEVICE_ID_TI_TVP4020) }, + { PCI_VDEVICE(3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2) }, + { PCI_VDEVICE(3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2V) }, + { } }; static struct pci_driver pm2fb_driver = { diff --git a/drivers/video/fbdev/pm3fb.c b/drivers/video/fbdev/pm3fb.c index 6e55e42514d6..6f552ae36219 100644 --- a/drivers/video/fbdev/pm3fb.c +++ b/drivers/video/fbdev/pm3fb.c @@ -1486,9 +1486,8 @@ static void pm3fb_remove(struct pci_dev *dev) } static const struct pci_device_id pm3fb_id_table[] = { - { PCI_VENDOR_ID_3DLABS, 0x0a, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, } + { PCI_VDEVICE(3DLABS, 0x000a) }, + { } }; /* For PCI drivers */ diff --git a/drivers/video/fbdev/ps3fb.c b/drivers/video/fbdev/ps3fb.c index dbcda307f6a6..1376d19b19ae 100644 --- a/drivers/video/fbdev/ps3fb.c +++ b/drivers/video/fbdev/ps3fb.c @@ -29,7 +29,6 @@ #include <linux/freezer.h> #include <linux/uaccess.h> #include <linux/fb.h> -#include <linux/fbcon.h> #include <linux/init.h> #include <asm/cell-regs.h> @@ -830,9 +829,7 @@ static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd, /* Force, in case only special bits changed */ var.activate |= FB_ACTIVATE_FORCE; par->new_mode_id = val; - retval = fb_set_var(info, &var); - if (!retval) - fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); + retval = fb_set_var_from_user(info, &var); console_unlock(); } break; diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c index 3f6384e631b1..9428716e2dc4 100644 --- a/drivers/video/fbdev/pvr2fb.c +++ b/drivers/video/fbdev/pvr2fb.c @@ -993,9 +993,8 @@ static void pvr2fb_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id pvr2fb_pci_tbl[] = { - { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NEON250, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, }, + { PCI_VDEVICE(NEC, PCI_DEVICE_ID_NEC_NEON250) }, + { } }; MODULE_DEVICE_TABLE(pci, pvr2fb_pci_tbl); diff --git a/drivers/video/fbdev/pxa168fb.c b/drivers/video/fbdev/pxa168fb.c index ec602f7776eb..6784888d93c9 100644 --- a/drivers/video/fbdev/pxa168fb.c +++ b/drivers/video/fbdev/pxa168fb.c @@ -653,10 +653,9 @@ static int pxa168fb_probe(struct platform_device *pdev) /* * Map LCD controller registers. */ - fbi->reg_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (fbi->reg_base == NULL) { - ret = -ENOMEM; + fbi->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fbi->reg_base)) { + ret = PTR_ERR(fbi->reg_base); goto failed_free_info; } diff --git a/drivers/video/fbdev/riva/fbdev.c b/drivers/video/fbdev/riva/fbdev.c index 1e377b2ec089..2268fea4d807 100644 --- a/drivers/video/fbdev/riva/fbdev.c +++ b/drivers/video/fbdev/riva/fbdev.c @@ -103,92 +103,50 @@ static int rivafb_blank(int blank, struct fb_info *info); * ------------------------------------------------------------------------- */ static const struct pci_device_id rivafb_pci_tbl[] = { - { PCI_VENDOR_ID_NVIDIA_SGS, PCI_DEVICE_ID_NVIDIA_SGS_RIVA128, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UTNT2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_VTNT2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UVTNT2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_ITNT2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_DEVICE_ID_NVIDIA_SGS_RIVA128) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UTNT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_VTNT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UVTNT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_ITNT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440) }, // NF2/IGP version, GeForce 4 MX, NV18 - { PCI_VENDOR_ID_NVIDIA, 0x01f0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_IGEFORCE2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_DDC, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, } /* terminate list */ + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x01f0) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_200) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_IGEFORCE2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_1) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_2) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_DDC) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200) }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, rivafb_pci_tbl); diff --git a/drivers/video/fbdev/riva/rivafb-i2c.c b/drivers/video/fbdev/riva/rivafb-i2c.c index 6a183375ced1..5ee59be01850 100644 --- a/drivers/video/fbdev/riva/rivafb-i2c.c +++ b/drivers/video/fbdev/riva/rivafb-i2c.c @@ -91,7 +91,7 @@ static int riva_setup_i2c_bus(struct riva_i2c_chan *chan, const char *name, { int rc; - strcpy(chan->adapter.name, name); + strscpy(chan->adapter.name, name); chan->adapter.owner = THIS_MODULE; chan->adapter.class = i2c_class; chan->adapter.algo_data = &chan->algo; diff --git a/drivers/video/fbdev/s3fb.c b/drivers/video/fbdev/s3fb.c index ba30e5568cab..cecbac99c8e0 100644 --- a/drivers/video/fbdev/s3fb.c +++ b/drivers/video/fbdev/s3fb.c @@ -1333,7 +1333,7 @@ static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) vga_wcrt(par->state.vgabase, 0x38, cr38); vga_wcrt(par->state.vgabase, 0x39, cr39); - strcpy(info->fix.id, s3_names [par->chip]); + strscpy(info->fix.id, s3_names[par->chip]); info->fix.mmio_start = 0; info->fix.mmio_len = 0; info->fix.type = FB_TYPE_PACKED_PIXELS; @@ -1446,6 +1446,7 @@ err_reg_fb: err_alloc_cmap: err_find_mode: #ifdef CONFIG_FB_S3_DDC + fb_destroy_modelist(&info->modelist); if (par->ddc_registered) i2c_del_adapter(&par->ddc_adapter); if (par->mmio) @@ -1563,24 +1564,24 @@ static const struct dev_pm_ops s3_pci_pm_ops = { /* List of boards that we are trying to support */ static const struct pci_device_id s3_devices[] = { - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8810), .driver_data = CHIP_XXX_TRIO}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8811), .driver_data = CHIP_XXX_TRIO}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8812), .driver_data = CHIP_M65_AURORA64VP}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8814), .driver_data = CHIP_767_TRIO64UVP}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8901), .driver_data = CHIP_XXX_TRIO64V2_DXGX}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8902), .driver_data = CHIP_551_PLATO_PX}, - - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x5631), .driver_data = CHIP_325_VIRGE}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x883D), .driver_data = CHIP_988_VIRGE_VX}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A01), .driver_data = CHIP_XXX_VIRGE_DXGX}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A10), .driver_data = CHIP_357_VIRGE_GX2}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A11), .driver_data = CHIP_359_VIRGE_GX2P}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A12), .driver_data = CHIP_359_VIRGE_GX2P}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A13), .driver_data = CHIP_36X_TRIO3D_1X_2X}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8904), .driver_data = CHIP_365_TRIO3D}, - {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8C01), .driver_data = CHIP_260_VIRGE_MX}, - - {0, 0, 0, 0, 0, 0, 0} + { PCI_VDEVICE(S3, 0x8810), .driver_data = CHIP_XXX_TRIO }, + { PCI_VDEVICE(S3, 0x8811), .driver_data = CHIP_XXX_TRIO }, + { PCI_VDEVICE(S3, 0x8812), .driver_data = CHIP_M65_AURORA64VP }, + { PCI_VDEVICE(S3, 0x8814), .driver_data = CHIP_767_TRIO64UVP }, + { PCI_VDEVICE(S3, 0x8901), .driver_data = CHIP_XXX_TRIO64V2_DXGX }, + { PCI_VDEVICE(S3, 0x8902), .driver_data = CHIP_551_PLATO_PX }, + + { PCI_VDEVICE(S3, 0x5631), .driver_data = CHIP_325_VIRGE }, + { PCI_VDEVICE(S3, 0x883D), .driver_data = CHIP_988_VIRGE_VX }, + { PCI_VDEVICE(S3, 0x8A01), .driver_data = CHIP_XXX_VIRGE_DXGX }, + { PCI_VDEVICE(S3, 0x8A10), .driver_data = CHIP_357_VIRGE_GX2 }, + { PCI_VDEVICE(S3, 0x8A11), .driver_data = CHIP_359_VIRGE_GX2P }, + { PCI_VDEVICE(S3, 0x8A12), .driver_data = CHIP_359_VIRGE_GX2P }, + { PCI_VDEVICE(S3, 0x8A13), .driver_data = CHIP_36X_TRIO3D_1X_2X }, + { PCI_VDEVICE(S3, 0x8904), .driver_data = CHIP_365_TRIO3D }, + { PCI_VDEVICE(S3, 0x8C01), .driver_data = CHIP_260_VIRGE_MX }, + + { } }; diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c index c2f79357c8da..7789196d2eb5 100644 --- a/drivers/video/fbdev/savage/savagefb_driver.c +++ b/drivers/video/fbdev/savage/savagefb_driver.c @@ -2449,76 +2449,78 @@ static const struct dev_pm_ops savagefb_pm_ops = { }; static const struct pci_device_id savagefb_devices[] = { - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR}, - - {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK}, - - {0, 0, 0, 0, 0, 0, 0} + { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_MX128), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_MX64), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_MX64C), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_IX128SDR), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_IX128DDR), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_IX64SDR), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_IX64DDR), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_IXCSDR), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SUPSAV_IXCDDR), + .driver_data = FB_ACCEL_SUPERSAVAGE, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE4), + .driver_data = FB_ACCEL_SAVAGE4, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE3D), + .driver_data = FB_ACCEL_SAVAGE3D, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE3D_MV), + .driver_data = FB_ACCEL_SAVAGE3D_MV, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE2000), + .driver_data = FB_ACCEL_SAVAGE2000, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE_MX_MV), + .driver_data = FB_ACCEL_SAVAGE_MX_MV, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE_MX), + .driver_data = FB_ACCEL_SAVAGE_MX, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE_IX_MV), + .driver_data = FB_ACCEL_SAVAGE_IX_MV, + }, { + PCI_VDEVICE(S3, PCI_CHIP_SAVAGE_IX), + .driver_data = FB_ACCEL_SAVAGE_IX, + }, { + PCI_VDEVICE(S3, PCI_CHIP_PROSAVAGE_PM), + .driver_data = FB_ACCEL_PROSAVAGE_PM, + }, { + PCI_VDEVICE(S3, PCI_CHIP_PROSAVAGE_KM), + .driver_data = FB_ACCEL_PROSAVAGE_KM, + }, { + PCI_VDEVICE(S3, PCI_CHIP_S3TWISTER_P), + .driver_data = FB_ACCEL_S3TWISTER_P, + }, { + PCI_VDEVICE(S3, PCI_CHIP_S3TWISTER_K), + .driver_data = FB_ACCEL_S3TWISTER_K, + }, { + PCI_VDEVICE(S3, PCI_CHIP_PROSAVAGE_DDR), + .driver_data = FB_ACCEL_PROSAVAGE_DDR, + }, { + PCI_VDEVICE(S3, PCI_CHIP_PROSAVAGE_DDRK), + .driver_data = FB_ACCEL_PROSAVAGE_DDRK, + }, + + { } }; MODULE_DEVICE_TABLE(pci, savagefb_devices); diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c index 72969fe8e513..e8324b01700f 100644 --- a/drivers/video/fbdev/sh_mobile_lcdcfb.c +++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c @@ -15,7 +15,6 @@ #include <linux/ctype.h> #include <linux/dma-mapping.h> #include <linux/delay.h> -#include <linux/fbcon.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/ioctl.h> @@ -1768,11 +1767,9 @@ static void sh_mobile_fb_reconfig(struct fb_info *info) var.height = ch->display.height; var.activate = FB_ACTIVATE_NOW; - if (fb_set_var(info, &var) < 0) + if (fb_set_var_from_user(info, &var) < 0) /* Couldn't reconfigure, hopefully, can continue as before */ return; - - fbcon_update_vcs(info, true); } /* diff --git a/drivers/video/fbdev/sis/sis_main.c b/drivers/video/fbdev/sis/sis_main.c index 84567d67f71d..95f976b49143 100644 --- a/drivers/video/fbdev/sis/sis_main.c +++ b/drivers/video/fbdev/sis/sis_main.c @@ -204,8 +204,7 @@ static void sisfb_search_mode(char *name, bool quiet) return; } - if(strlen(name) <= 19) { - strcpy(strbuf1, name); + if (strscpy(strbuf1, name) > 0) { for(i = 0; i < strlen(strbuf1); i++) { if(strbuf1[i] < '0' || strbuf1[i] > '9') strbuf1[i] = ' '; } diff --git a/drivers/video/fbdev/sis/sis_main.h b/drivers/video/fbdev/sis/sis_main.h index 0965db9fad6a..4ca487f48205 100644 --- a/drivers/video/fbdev/sis/sis_main.h +++ b/drivers/video/fbdev/sis/sis_main.h @@ -102,22 +102,22 @@ static struct sisfb_chip_info { static struct pci_device_id sisfb_pci_table[] = { #ifdef CONFIG_FB_SIS_300 - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_540_VGA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630_VGA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_300), .driver_data = 0 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_540_VGA), .driver_data = 1 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_630_VGA), .driver_data = 2 }, #endif #ifdef CONFIG_FB_SIS_315 - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_315H, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_315PRO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_550_VGA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_650_VGA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_330, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8}, - { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_660_VGA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9}, - { PCI_VENDOR_ID_XGI,PCI_DEVICE_ID_XGI_20, PCI_ANY_ID, PCI_ANY_ID, 0, 0,10}, - { PCI_VENDOR_ID_XGI,PCI_DEVICE_ID_XGI_40, PCI_ANY_ID, PCI_ANY_ID, 0, 0,11}, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_315H), .driver_data = 3 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_315), .driver_data = 4 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_315PRO), .driver_data = 5 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_550_VGA), .driver_data = 6 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_650_VGA), .driver_data = 7 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_330), .driver_data = 8 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_660_VGA), .driver_data = 9 }, + { PCI_VDEVICE(XGI, PCI_DEVICE_ID_XGI_20), .driver_data = 10 }, + { PCI_VDEVICE(XGI, PCI_DEVICE_ID_XGI_40), .driver_data = 11 }, #endif - { 0 } + { } }; MODULE_DEVICE_TABLE(pci, sisfb_pci_table); diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c index fee4b9f84592..ea5375ed4ea6 100644 --- a/drivers/video/fbdev/sm501fb.c +++ b/drivers/video/fbdev/sm501fb.c @@ -96,6 +96,7 @@ struct sm501fb_info { void __iomem *fbmem; /* remapped framebuffer */ size_t fbmem_len; /* length of remapped region */ u8 *edid_data; + char *fb_mode; }; /* per-framebuffer private data */ @@ -1793,12 +1794,11 @@ static int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head, fb->var.yres_virtual = fb->var.yres; } else { if (info->edid_data) { - ret = fb_find_mode(&fb->var, fb, fb_mode, + ret = fb_find_mode(&fb->var, fb, + info->fb_mode ?: fb_mode, fb->monspecs.modedb, fb->monspecs.modedb_len, &sm501_default_mode, default_bpp); - /* edid_data is no longer needed, free it */ - kfree(info->edid_data); } else { ret = fb_find_mode(&fb->var, fb, NULL, NULL, 0, NULL, 8); @@ -1974,7 +1974,7 @@ static int sm501fb_probe(struct platform_device *pdev) /* Get EDID */ cp = of_get_property(np, "mode", &len); if (cp) - strcpy(fb_mode, cp); + info->fb_mode = kstrdup(cp, GFP_KERNEL); prop = of_get_property(np, "edid", &len); if (prop && len == EDID_LENGTH) { info->edid_data = kmemdup(prop, EDID_LENGTH, @@ -2031,6 +2031,12 @@ static int sm501fb_probe(struct platform_device *pdev) goto err_started_crt; } + /* These aren't needed any more */ + kfree(info->edid_data); + kfree(info->fb_mode); + info->edid_data = NULL; + info->fb_mode = NULL; + /* we registered, return ok */ return 0; @@ -2048,6 +2054,8 @@ err_probed_crt: framebuffer_release(info->fb[HEAD_CRT]); err_alloc: + kfree(info->edid_data); + kfree(info->fb_mode); kfree(info); return ret; diff --git a/drivers/video/fbdev/sm712.h b/drivers/video/fbdev/sm712.h index c7ebf03b8d53..83fe25fc61f2 100644 --- a/drivers/video/fbdev/sm712.h +++ b/drivers/video/fbdev/sm712.h @@ -101,7 +101,7 @@ struct modeinit { #define mmio_addr 0x00800000 #define seqw17() smtc_seqw(0x17, 0x30) #define big_pixel_depth(p, d) {if (p == 24) {p = 32; d = 32; } } -#define big_swap(p) ((p & 0xff00ff00 >> 8) | (p & 0x00ff00ff << 8)) +#define big_swap(p) (((p & 0xff00ff00) >> 8) | ((p & 0x00ff00ff) << 8)) #else #define pal_rgb(r, g, b, val) val #define big_addr 0 diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 83dd31fa1fab..644b8d97b381 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -784,10 +784,10 @@ static void ssd1307fb_remove(struct i2c_client *client) } static const struct i2c_device_id ssd1307fb_i2c_id[] = { - { "ssd1305fb" }, - { "ssd1306fb" }, - { "ssd1307fb" }, - { "ssd1309fb" }, + { .name = "ssd1305fb" }, + { .name = "ssd1306fb" }, + { .name = "ssd1307fb" }, + { .name = "ssd1309fb" }, { } }; MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); diff --git a/drivers/video/fbdev/sunxvr2500.c b/drivers/video/fbdev/sunxvr2500.c index 42426d09b935..b7587ff4df85 100644 --- a/drivers/video/fbdev/sunxvr2500.c +++ b/drivers/video/fbdev/sunxvr2500.c @@ -38,8 +38,7 @@ static int s3d_get_props(struct s3d_info *sp) sp->depth = of_getintprop_default(sp->of_node, "depth", 8); if (!sp->width || !sp->height) { - printk(KERN_ERR "s3d: Critical properties missing for %s\n", - pci_name(sp->pdev)); + pci_err(sp->pdev, "Critical properties missing\n"); return -EINVAL; } @@ -107,7 +106,7 @@ static int s3d_set_fbinfo(struct s3d_info *sp) var->transp.length = 0; if (fb_alloc_cmap(&info->cmap, 256, 0)) { - printk(KERN_ERR "s3d: Cannot allocate color map.\n"); + pci_err(sp->pdev, "Cannot allocate color map\n"); return -ENOMEM; } @@ -127,8 +126,7 @@ static int s3d_pci_register(struct pci_dev *pdev, err = pci_enable_device(pdev); if (err < 0) { - printk(KERN_ERR "s3d: Cannot enable PCI device %s\n", - pci_name(pdev)); + pci_err(pdev, "Cannot enable PCI device\n"); goto err_out; } @@ -143,8 +141,7 @@ static int s3d_pci_register(struct pci_dev *pdev, sp->pdev = pdev; sp->of_node = pci_device_to_OF_node(pdev); if (!sp->of_node) { - printk(KERN_ERR "s3d: Cannot find OF node of %s\n", - pci_name(pdev)); + pci_err(pdev, "Cannot find OF node\n"); err = -ENODEV; goto err_release_fb; } @@ -153,8 +150,7 @@ static int s3d_pci_register(struct pci_dev *pdev, err = pci_request_region(pdev, 1, "s3d framebuffer"); if (err < 0) { - printk("s3d: Cannot request region 1 for %s\n", - pci_name(pdev)); + pci_err(pdev, "Cannot request region 1\n"); goto err_release_fb; } @@ -194,12 +190,11 @@ static int s3d_pci_register(struct pci_dev *pdev, pci_set_drvdata(pdev, info); - printk("s3d: Found device at %s\n", pci_name(pdev)); + pci_info(pdev, "Found device\n"); err = register_framebuffer(info); if (err < 0) { - printk(KERN_ERR "s3d: Could not register framebuffer %s\n", - pci_name(pdev)); + pci_err(pdev, "Could not register framebuffer\n"); goto err_unmap_fb; } diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index 4c4e53aaea3a..cc6a074f3165 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -124,16 +124,17 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id); static void tdfxfb_remove(struct pci_dev *pdev); static const struct pci_device_id tdfxfb_id_table[] = { - { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE, - PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, - 0xff0000, 0 }, - { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3, - PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, - 0xff0000, 0 }, - { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO5, - PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, - 0xff0000, 0 }, - { 0, } + { + PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE), + .class = PCI_BASE_CLASS_DISPLAY << 16, .class_mask = 0xff0000, + }, { + PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3), + .class = PCI_BASE_CLASS_DISPLAY << 16, .class_mask = 0xff0000, + }, { + PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO5), + .class = PCI_BASE_CLASS_DISPLAY << 16, .class_mask = 0xff0000, + }, + { } }; static struct pci_driver tdfxfb_driver = { @@ -1551,6 +1552,7 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_err_iobase: #ifdef CONFIG_FB_3DFX_I2C + fb_destroy_modelist(&info->modelist); tdfxfb_delete_i2c_busses(default_par); #endif arch_phys_wc_del(default_par->wc_cookie); diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c index 17b7253b8fbe..9f055ba776c8 100644 --- a/drivers/video/fbdev/tridentfb.c +++ b/drivers/video/fbdev/tridentfb.c @@ -1706,6 +1706,7 @@ static int trident_pci_probe(struct pci_dev *dev, return 0; out_unmap2: + fb_destroy_modelist(&info->modelist); if (default_par->ddc_registered) i2c_del_adapter(&default_par->ddc_adapter); kfree(info->pixmap.addr); @@ -1736,28 +1737,28 @@ static void trident_pci_remove(struct pci_dev *dev) /* List of boards that we are trying to support */ static const struct pci_device_id trident_devices[] = { - {PCI_VENDOR_ID_TRIDENT, BLADE3D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, TGUI9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBER9320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBER9388, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBER9520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBER9525DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBER9397, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBER9397DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm16, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0,} + { PCI_VDEVICE(TRIDENT, BLADE3D) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEi7) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEi7D) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEi1) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEi1D) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEAi1) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEAi1D) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEE4) }, + { PCI_VDEVICE(TRIDENT, TGUI9440) }, + { PCI_VDEVICE(TRIDENT, TGUI9660) }, + { PCI_VDEVICE(TRIDENT, IMAGE975) }, + { PCI_VDEVICE(TRIDENT, IMAGE985) }, + { PCI_VDEVICE(TRIDENT, CYBER9320) }, + { PCI_VDEVICE(TRIDENT, CYBER9388) }, + { PCI_VDEVICE(TRIDENT, CYBER9520) }, + { PCI_VDEVICE(TRIDENT, CYBER9525DVD) }, + { PCI_VDEVICE(TRIDENT, CYBER9397) }, + { PCI_VDEVICE(TRIDENT, CYBER9397DVD) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEXPAi1) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEXPm8) }, + { PCI_VDEVICE(TRIDENT, CYBERBLADEXPm16) }, + { } }; MODULE_DEVICE_TABLE(pci, trident_devices); diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 88667fccc27b..9d82326c744f 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -1694,14 +1694,14 @@ static int uvesafb_probe(struct platform_device *dev) i = uvesafb_vbe_init_mode(info); if (i < 0) { err = -EINVAL; - goto out; + goto out_mode; } else { mode = &par->vbe_modes[i]; } if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { err = -ENXIO; - goto out; + goto out_mode; } uvesafb_init_info(info, mode); diff --git a/drivers/video/fbdev/vesafb.c b/drivers/video/fbdev/vesafb.c index f84f4db244bf..f1902056bd73 100644 --- a/drivers/video/fbdev/vesafb.c +++ b/drivers/video/fbdev/vesafb.c @@ -269,6 +269,7 @@ static int vesafb_probe(struct platform_device *dev) /* ignore error return of fb_get_options */ fb_get_options("vesafb", &option); vesafb_setup(option); + kfree(option); if (si->orig_video_isVGA != VIDEO_TYPE_VLFB) return -ENODEV; diff --git a/drivers/video/fbdev/vt8623fb.c b/drivers/video/fbdev/vt8623fb.c index df984f3a7ff6..9708d968970a 100644 --- a/drivers/video/fbdev/vt8623fb.c +++ b/drivers/video/fbdev/vt8623fb.c @@ -900,8 +900,8 @@ static const struct dev_pm_ops vt8623_pci_pm_ops = { /* List of boards that we are trying to support */ static const struct pci_device_id vt8623_devices[] = { - {PCI_DEVICE(PCI_VENDOR_ID_VIA, 0x3122)}, - {0, 0, 0, 0, 0, 0, 0} + { PCI_DEVICE(PCI_VENDOR_ID_VIA, 0x3122) }, + { } }; MODULE_DEVICE_TABLE(pci, vt8623_devices); diff --git a/drivers/virt/acrn/hsm.c b/drivers/virt/acrn/hsm.c index 74f2086fa59f..f170ff4617fd 100644 --- a/drivers/virt/acrn/hsm.c +++ b/drivers/virt/acrn/hsm.c @@ -16,6 +16,7 @@ #include <linux/slab.h> #include <asm/acrn.h> +#include <asm/cpuid/api.h> #include <asm/hypervisor.h> #include "acrn_drv.h" diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig index df1cfaf26c65..f7691f64fbe3 100644 --- a/drivers/virt/coco/Kconfig +++ b/drivers/virt/coco/Kconfig @@ -17,5 +17,7 @@ source "drivers/virt/coco/arm-cca-guest/Kconfig" source "drivers/virt/coco/guest/Kconfig" endif +source "drivers/virt/coco/tdx-host/Kconfig" + config TSM bool diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile index cb52021912b3..b323b0ae4f82 100644 --- a/drivers/virt/coco/Makefile +++ b/drivers/virt/coco/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_EFI_SECRET) += efi_secret/ obj-$(CONFIG_ARM_PKVM_GUEST) += pkvm-guest/ obj-$(CONFIG_SEV_GUEST) += sev-guest/ obj-$(CONFIG_INTEL_TDX_GUEST) += tdx-guest/ +obj-$(CONFIG_INTEL_TDX_HOST) += tdx-host/ obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest/ obj-$(CONFIG_TSM) += tsm-core.o obj-$(CONFIG_TSM_GUEST) += guest/ diff --git a/drivers/virt/coco/tdx-host/Kconfig b/drivers/virt/coco/tdx-host/Kconfig new file mode 100644 index 000000000000..57d0c01a4357 --- /dev/null +++ b/drivers/virt/coco/tdx-host/Kconfig @@ -0,0 +1,6 @@ +config TDX_HOST_SERVICES + tristate + depends on INTEL_TDX_HOST + select FW_LOADER + select FW_UPLOAD + default m diff --git a/drivers/virt/coco/tdx-host/Makefile b/drivers/virt/coco/tdx-host/Makefile new file mode 100644 index 000000000000..e61e749a8dff --- /dev/null +++ b/drivers/virt/coco/tdx-host/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TDX_HOST_SERVICES) += tdx-host.o diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c new file mode 100644 index 000000000000..d48952968e86 --- /dev/null +++ b/drivers/virt/coco/tdx-host/tdx-host.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TDX host user interface driver + * + * Copyright (C) 2025 Intel Corporation + */ + +#include <linux/device/faux.h> +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/sysfs.h> + +#include <asm/cpu_device_id.h> +#include <asm/seamldr.h> +#include <asm/tdx.h> + +static const struct x86_cpu_id tdx_host_ids[] = { + X86_MATCH_FEATURE(X86_FEATURE_TDX_HOST_PLATFORM, NULL), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, tdx_host_ids); + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo(); + const struct tdx_sys_info_version *ver; + int ret; + + if (!tdx_sysinfo) + return -ENXIO; + + /* + * The version number can change during an update. + * Lock out updates while printing the version. + */ + seamldr_lock_module_update(); + + ver = &tdx_sysinfo->version; + ret = sysfs_emit(buf, TDX_VERSION_FMT "\n", ver->major_version, + ver->minor_version, + ver->update_version); + seamldr_unlock_module_update(); + + return ret; +} +static DEVICE_ATTR_RO(version); + +static struct attribute *tdx_host_attrs[] = { + &dev_attr_version.attr, + NULL, +}; + +static const struct attribute_group tdx_host_group = { + .attrs = tdx_host_attrs, +}; + +static ssize_t seamldr_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct seamldr_info info; + int ret; + + ret = seamldr_get_info(&info); + if (ret) + return ret; + + return sysfs_emit(buf, TDX_VERSION_FMT "\n", info.major_version, + info.minor_version, + info.update_version); +} + +static ssize_t num_remaining_updates_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct seamldr_info info; + int ret; + + ret = seamldr_get_info(&info); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", info.num_remaining_updates); +} + +/* + * These attributes are intended for managing TDX module updates. Reading + * them issues a slow, serialized P-SEAMLDR query, so keep them admin-only. + */ +static DEVICE_ATTR_ADMIN_RO(seamldr_version); +static DEVICE_ATTR_ADMIN_RO(num_remaining_updates); + +static struct attribute *seamldr_attrs[] = { + &dev_attr_seamldr_version.attr, + &dev_attr_num_remaining_updates.attr, + NULL, +}; + +static bool supports_runtime_update(void) +{ + const struct tdx_sys_info *sysinfo = tdx_get_sysinfo(); + + if (!sysinfo) + return false; + + if (!tdx_supports_runtime_update(sysinfo)) + return false; + + /* + * This bug makes P-SEAMLDR calls clobber the current VMCS + * which breaks KVM. Avoid P-SEAMLDR calls by hiding all + * attributes if the CPU has this bug. + */ + if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS)) + return false; + + return true; +} + +static umode_t seamldr_group_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + if (!supports_runtime_update()) + return 0; + + return attr->mode; +} + +static const struct attribute_group seamldr_group = { + .attrs = seamldr_attrs, + .is_visible = seamldr_group_visible, +}; + +static const struct attribute_group *tdx_host_groups[] = { + &tdx_host_group, + &seamldr_group, + NULL, +}; + +static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl, + const u8 *data, u32 data_len) +{ + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err tdx_fw_write(struct fw_upload *fwl, const u8 *data, + u32 offset, u32 data_len, u32 *written) +{ + int ret; + + ret = seamldr_install_module(data, data_len); + switch (ret) { + case 0: + *written = data_len; + return FW_UPLOAD_ERR_NONE; + default: + return FW_UPLOAD_ERR_FW_INVALID; + } +} + +static enum fw_upload_err tdx_fw_poll_complete(struct fw_upload *fwl) +{ + /* + * The upload completed during tdx_fw_write(). + * Never poll for completion. + */ + return FW_UPLOAD_ERR_NONE; +} + +static void tdx_fw_cancel(struct fw_upload *fwl) +{ + /* + * TDX module updates are not cancellable. + * Provide a no-op callback to satisfy fw_upload_ops. + */ +} + +static const struct fw_upload_ops tdx_fw_ops = { + .prepare = tdx_fw_prepare, + .write = tdx_fw_write, + .poll_complete = tdx_fw_poll_complete, + .cancel = tdx_fw_cancel, +}; + +static void seamldr_deinit(void *tdx_fwl) +{ + firmware_upload_unregister(tdx_fwl); +} + +static int seamldr_init(struct device *dev) +{ + struct fw_upload *tdx_fwl; + + if (!supports_runtime_update()) + return 0; + + tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "tdx_module", + &tdx_fw_ops, NULL); + if (IS_ERR(tdx_fwl)) + return PTR_ERR(tdx_fwl); + + return devm_add_action_or_reset(dev, seamldr_deinit, tdx_fwl); +} + +static int tdx_host_probe(struct faux_device *fdev) +{ + return seamldr_init(&fdev->dev); +} + +static const struct faux_device_ops tdx_host_ops = { + .probe = tdx_host_probe, +}; + +static struct faux_device *fdev; + +static int __init tdx_host_init(void) +{ + if (!x86_match_cpu(tdx_host_ids) || !tdx_get_sysinfo()) + return -ENODEV; + + fdev = faux_device_create_with_groups(KBUILD_MODNAME, NULL, + &tdx_host_ops, + tdx_host_groups); + if (!fdev) + return -ENODEV; + + return 0; +} +module_init(tdx_host_init); + +static void __exit tdx_host_exit(void) +{ + faux_device_destroy(fdev); +} +module_exit(tdx_host_exit); + +MODULE_DESCRIPTION("TDX Host Services"); +MODULE_LICENSE("GPL"); diff --git a/drivers/virtio/virtio_rtc_ptp.c b/drivers/virtio/virtio_rtc_ptp.c index f84599950cd4..ff8d834493dc 100644 --- a/drivers/virtio/virtio_rtc_ptp.c +++ b/drivers/virtio/virtio_rtc_ptp.c @@ -139,7 +139,7 @@ static int viortc_ptp_getcrosststamp(struct ptp_clock_info *ptp, if (ret) return ret; - ktime_get_snapshot(&history_begin); + ktime_get_snapshot_id(xtstamp->clock_id, &history_begin); if (history_begin.cs_id != cs_id) return -EOPNOTSUPP; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index dc78729ba2a5..08cb8612d41f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -250,7 +250,7 @@ config DA9062_WATCHDOG config GPIO_WATCHDOG tristate "Watchdog device controlled through GPIO-line" - depends on OF_GPIO + depends on GPIOLIB && (ACPI || OF) select WATCHDOG_CORE help If you say yes here you get support for watchdog device @@ -882,7 +882,7 @@ config TEGRA_WATCHDOG module will be called tegra_wdt. config QCOM_WDT - tristate "QCOM watchdog" + tristate "Qualcomm watchdog" depends on HAS_IOMEM depends on ARCH_QCOM || COMPILE_TEST select WATCHDOG_CORE @@ -1093,7 +1093,7 @@ config SPRD_WATCHDOG by Spreadtrum system. config PM8916_WATCHDOG - tristate "QCOM PM8916 pmic watchdog" + tristate "Qualcomm PM8916 pmic watchdog" depends on OF && MFD_SPMI_PMIC select WATCHDOG_CORE help @@ -1270,19 +1270,6 @@ config GEODE_WDT You can compile this driver directly into the kernel, or use it as a module. The module will be called geodewdt. -config SC520_WDT - tristate "AMD Elan SC520 processor Watchdog" - depends on MELAN || COMPILE_TEST - help - This is the driver for the hardware watchdog built in to the - AMD "Elan" SC520 microcomputer commonly used in embedded systems. - This watchdog simply watches your kernel to make sure it doesn't - freeze, and if it does, it reboots your computer after a certain - amount of time. - - You can compile this driver directly into the kernel, or use - it as a module. The module will be called sc520_wdt. - config SBC_FITPC2_WATCHDOG tristate "Compulab SBC-FITPC2 watchdog" depends on (X86 || COMPILE_TEST) && HAS_IOPORT @@ -1547,7 +1534,7 @@ config NV_TCO config RDC321X_WDT tristate "RDC R-321x SoC watchdog" - depends on X86_RDC321X || COMPILE_TEST + depends on X86_32 || COMPILE_TEST depends on PCI help This is the driver for the built in hardware watchdog @@ -1712,19 +1699,6 @@ config W83977F_WDT To compile this driver as a module, choose M here: the module will be called w83977f_wdt. -config MACHZ_WDT - tristate "ZF MachZ Watchdog" - depends on (X86 || COMPILE_TEST) && HAS_IOPORT - help - If you are using a ZF Micro MachZ processor, say Y here, otherwise - N. This is the driver for the watchdog timer built-in on that - processor using ZF-Logic interface. This watchdog simply watches - your kernel to make sure it doesn't freeze, and if it does, it - reboots your computer after a certain amount of time. - - To compile this driver as a module, choose M here: the - module will be called machzwd. - config SBC_EPX_C3_WATCHDOG tristate "Winsystems SBC EPX-C3 watchdog" depends on (X86 || COMPILE_TEST) && HAS_IOPORT @@ -2011,7 +1985,7 @@ config MT7621_WDT config PIC32_WDT tristate "Microchip PIC32 hardware watchdog" select WATCHDOG_CORE - depends on MACH_PIC32 || (MIPS && COMPILE_TEST) + depends on MACH_PIC32 || COMPILE_TEST help Watchdog driver for the built in watchdog hardware in a PIC32. @@ -2024,7 +1998,7 @@ config PIC32_WDT config PIC32_DMT tristate "Microchip PIC32 Deadman Timer" select WATCHDOG_CORE - depends on MACH_PIC32 || (MIPS && COMPILE_TEST) + depends on MACH_PIC32 || COMPILE_TEST help Watchdog driver for PIC32 instruction fetch counting timer. This specific timer is typically be used in mission critical and safety @@ -2132,6 +2106,15 @@ config WATCHDOG_RTAS # RISC-V Architecture +config ATCWDT200_WATCHDOG + tristate "Andes ATCWDT200 Watchdog support" + depends on ARCH_ANDES || COMPILE_TEST + help + Driver for the Andes ATCWDT200 watchdog timer. It provides access to + programmable reset and interrupt counters with clock-source dependent + timing. The driver automatically detects the supported IntTime bit-width + and is fully integrated with the Linux Watchdog Framework. + config STARFIVE_WATCHDOG tristate "StarFive Watchdog support" depends on ARCH_STARFIVE || COMPILE_TEST @@ -2270,10 +2253,11 @@ config MIXCOMWD Most people will say N. config WDT - tristate "WDT Watchdog timer" + tristate "ICS WDT500P/501P Watchdog timer" depends on ISA help - If you have a WDT500P or WDT501P watchdog board, say Y here, + If you have an Industrial Computer Source (ICS) WDT500P or WDT501P + watchdog board, say Y here, otherwise N. It is not possible to probe for this board, which means that you have to inform the kernel about the IO port and IRQ that is needed (you can do this via the io and irq parameters) @@ -2304,10 +2288,11 @@ config PCIPCWATCHDOG Most people will say N. config WDTPCI - tristate "PCI-WDT500/501 Watchdog timer" + tristate "ICS PCI-WDT500/501 Watchdog timer" depends on PCI && HAS_IOPORT help - If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. + If you have an Industrial Computer Source (ICS) PCI-WDT500/501 watchdog + board, say Y here, otherwise N. If you have a PCI-WDT501 watchdog board then you can enable the temperature sensor by setting the type parameter to 501. @@ -2354,4 +2339,17 @@ config KEEMBAY_WATCHDOG To compile this driver as a module, choose M here: the module will be called keembay_wdt. +config GUNYAH_WATCHDOG + tristate "Qualcomm Gunyah Watchdog" + depends on ARCH_QCOM || COMPILE_TEST + depends on HAVE_ARM_SMCCC + select WATCHDOG_CORE + help + Say Y here to include support for watchdog timer provided by the + Gunyah hypervisor. The driver uses ARM SMC Calling Convention (SMCCC) + to interact with Gunyah Watchdog. + + To compile this driver as a module, choose M here: the + module will be called gunyah_wdt. + endif # WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d2fb16b9f9ce..bc1d52220f22 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o +obj-$(CONFIG_GUNYAH_WATCHDOG) += gunyah_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o @@ -116,7 +117,6 @@ obj-$(CONFIG_EXAR_WDT) += exar_wdt.o obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o obj-$(CONFIG_GEODE_WDT) += geodewdt.o -obj-$(CONFIG_SC520_WDT) += sc520_wdt.o obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_IB700_WDT) += ib700wdt.o @@ -146,7 +146,6 @@ obj-$(CONFIG_VIA_WDT) += via_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o -obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o obj-$(CONFIG_INTEL_OC_WATCHDOG) += intel_oc_wdt.o @@ -198,6 +197,7 @@ obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o # RISC-V Architecture +obj-$(CONFIG_ATCWDT200_WATCHDOG) += atcwdt200_wdt.o obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o # S390 Architecture diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index 66a158f67a71..6b9b0f9b05ce 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -218,6 +218,7 @@ static int apple_wdt_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(apple_wdt_pm_ops, apple_wdt_suspend, apple_wdt_resume); static const struct of_device_id apple_wdt_of_match[] = { + { .compatible = "apple,t8103-wdt" }, { .compatible = "apple,wdt" }, {}, }; diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h index 298d545df1a1..2020694f8f6f 100644 --- a/drivers/watchdog/at91sam9_wdt.h +++ b/drivers/watchdog/at91sam9_wdt.h @@ -9,6 +9,8 @@ * Watchdog Timer (WDT) - System peripherals regsters. * Based on AT91SAM9261 datasheet revision D. * Based on SAM9X60 datasheet. + * Based on SAMA7G5 datasheet. + * Based on SAM9X75 datasheet. * */ @@ -27,10 +29,10 @@ #define AT91_SAM9X60_PERIODRST BIT(4) /* Period Reset */ #define AT91_SAM9X60_RPTHRST BIT(5) /* Minimum Restart Period */ #define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */ -#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable */ +#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable (SAM9X60, SAMA7G5, SAM9X75) */ #define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */ #define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */ -#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */ +#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable (SAMA5, AT91SAM9261) */ #define AT91_WDT_WDD (0xfffUL << 16) /* Delta Value */ #define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) #define AT91_WDT_WDDBGHLT BIT(28) /* Debug Halt */ diff --git a/drivers/watchdog/atcwdt200_wdt.c b/drivers/watchdog/atcwdt200_wdt.c new file mode 100644 index 000000000000..8e3b18aea368 --- /dev/null +++ b/drivers/watchdog/atcwdt200_wdt.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Andes ATCWDT200 watchdog timer driver. + * + * Copyright (C) 2025 Andes Technology Corporation + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/moduleparam.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/watchdog.h> + +/* Register definitions */ +#define REG_CTRL 0x10 +#define REG_RESTART 0x14 +#define REG_WRITE_EN 0x18 +#define REG_STATUS 0x1C + +/* Control Register */ +#define CTRL_RST_TIME_MSK GENMASK(10, 8) +#define CTRL_RST_TIME_SET(x) FIELD_PREP(CTRL_RST_TIME_MSK, x) +#define CTRL_INT_TIME_MSK GENMASK(7, 4) +#define CTRL_INT_TIME_SET(x) FIELD_PREP(CTRL_INT_TIME_MSK, x) +#define CTRL_INT_TIME_GET(x) FIELD_GET(CTRL_INT_TIME_MSK, x) +#define CTRL_RST_EN BIT(3) +#define CTRL_CLK_SEL BIT(1) +#define CTRL_CLK_SEL_PCLK 1 +#define CTRL_CLK_SEL_SET(x) FIELD_PREP(CTRL_CLK_SEL, x) +#define CTRL_WDT_EN BIT(0) + +/* Restart Register */ +#define RESTART_MAGIC 0xCAFE + +/* Write Enable Register */ +#define WRITE_EN_MAGIC 0x5AA5 + +/* Status Register */ +#define STATUS_INT_EXPIRED BIT(1) + +/* The default timeout value in seconds */ +#define ATCWDT_TIMEOUT 4 + +/* Define the array size for each timer type */ +#define TMR_SZ_RST 8 +#define TMR_SZ_INT_16 8 +#define TMR_SZ_INT_32 16 + +#define DRV_NAME "atcwdt200" +/** + * enum timer_type - Supported timer types for ATCWDT200 watchdog driver + * @TMR_RST: Reset timer (non-interrupt). + * @TMR_INT_16: 16-bit interrupt timer supported by hardware. + * @TMR_INT_32: 32-bit interrupt timer supported by hardware. + * @TMR_UNKNOWN: Timer type cannot be determined. + */ +enum timer_type { + TMR_RST, + TMR_INT_16, + TMR_INT_32, + TMR_UNKNOWN +}; + +static unsigned int timeout = ATCWDT_TIMEOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; + +/** + * struct atcwdt_drv - ATCWDT200 watchdog driver private data + * @wdt_dev: Watchdog device used by the watchdog framework. + * @regmap: Register map for accessing hardware registers. + * @clk: Hardware clock used by the watchdog timer. + * @lock: Spinlock protecting register accesses and driver state. + * @clk_freq: Input clock frequency of the ATCWDT200. + * @clk_src: Selected clock source for the watchdog timer. + * @int_timer_type: Detected interrupt timer type (16-bit, 32-bit, or unknown). + */ +struct atcwdt_drv { + struct watchdog_device wdt_dev; + struct regmap *regmap; + struct clk *clk; + spinlock_t lock; + unsigned int clk_freq; + unsigned char clk_src; + unsigned char int_timer_type; +}; + +static const struct watchdog_info atcwdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +/** + * atcwdt_get_index - Get the interval value for the specified timer type + * @index: The index of the interval in the array + * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or + * TMR_INT_32. + * + * This function retrieves the interval value based on the timer type and + * ensures the index stays within the valid range for the given timer type. + * For TMR_RST: + * - The maximum array size is 8 (index range: 0-7). + * For TMR_INT_16: + * - The maximum array size is 8 (index range: 0-7). + * For TMR_INT_32: + * - The maximum array size is 16 (index range: 0-15). + * + * If the index exceeds the maximum array size, the function will return + * the last element of the respective array. + */ +static inline unsigned char atcwdt_get_index(unsigned char index, + enum timer_type timer_type) +{ + static const unsigned char rst_timer_interval[TMR_SZ_RST] = { + 7, 8, 9, 10, 11, 12, 13, 14}; + static const unsigned char int_timer_interval[TMR_SZ_INT_32] = { + 6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}; + unsigned char array_index; + + if (timer_type == TMR_RST) { + array_index = min(index, TMR_SZ_RST - 1); + return rst_timer_interval[array_index]; + } + + if (timer_type == TMR_INT_32) + array_index = min(index, TMR_SZ_INT_32 - 1); + else + array_index = min(index, TMR_SZ_INT_16 - 1); + + return int_timer_interval[array_index]; +} + +/** + * atcwdt_get_clock_period - Calculate the closest clock period based on a + * given tick count + * @tick: The target tick count to match + * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or + * TMR_INT_32. + * @index: Pointer to store the index of the selected parameter + * + * This function calculates the closest clock period to the given tick count + * by iterating through the timer parameters and selecting the one that + * minimizes the difference between the target tick count and the calculated + * clock period. The function determines the index of the closest parameter + * and returns the difference between the target tick count and the selected + * clock period. + * + * Return: The difference between the target tick count and the selected + * clock period. + */ +static long long atcwdt_get_clock_period(long long tick, + enum timer_type timer_type, + unsigned char *index) +{ + long long result; + unsigned char size; + char i; + + if (timer_type == TMR_RST) + size = TMR_SZ_RST; + else if (timer_type == TMR_INT_32) + size = TMR_SZ_INT_32; + else + size = TMR_SZ_INT_16; + + *index = size - 1; + for (i = 0; i < size; i++) { + result = tick - (1LL << atcwdt_get_index(i, timer_type)); + + if (result <= 1) { + *index = i; + break; + } + } + + return result; +} + +/** + * atcwdt_get_timeout_params - Calculate optimal parameters for Watchdog Timer + * @drv_data: Pointer to the Watchdog driver data structure + * @timeout: Desired timeout value (in seconds) + * @int_timer_params: Pointer to store the calculated interrupt timer + * parameter index + * @rst_timer_params: Pointer to store the calculated reset timer parameter + * index + * + * This function calculates the optimal parameter combination for the + * interrupt timer and reset timer of the Watchdog Timer to achieve a + * timeout value closest to, but not less than the specified timeout. + * + * Algorithm: + * 1. The parameters for both the interrupt timer and reset timer are + * predefined as a series of options represented as powers of 2. + * 2. The function first determines the interrupt timer's parameter index + * that provides a time closest to and not exceeding the desired timeout. + * 3. Based on the selected interrupt timer, it calculates the required + * reset timer parameter to ensure the total timeout matches the target. + * + * Return: The calculated parameter indices are stored in the provided + * pointers. + */ +static void atcwdt_get_timeout_params(struct atcwdt_drv *drv_data, + unsigned int timeout, + unsigned char *int_timer_params, + unsigned char *rst_timer_params) +{ + long long rest_time_ms; + long long result; + long long tick; + unsigned char rst_index; + unsigned char int_index; + unsigned char above; + unsigned char below; + + tick = (long long)timeout * drv_data->clk_freq; + result = atcwdt_get_clock_period(tick, + drv_data->int_timer_type, + &above); + if (result == 0 || above == 0) { + *int_timer_params = above; + *rst_timer_params = 0; + return; + } + below = above - 1; + + int_index = atcwdt_get_index(below, drv_data->int_timer_type); + rest_time_ms = timeout * 1000LL + - div64_s64(1000LL << int_index, drv_data->clk_freq); + + result = atcwdt_get_clock_period(rest_time_ms * drv_data->clk_freq, + TMR_RST, + &rst_index); + + if (result > 1) { + *int_timer_params = above; + *rst_timer_params = 0; + } else { + *int_timer_params = below; + *rst_timer_params = rst_index; + } +} + +/** + * atcwdt_get_int_timer_type - Get the supported interrupt timer type. + * @drv_data: Pointer to the watchdog driver data structure. + * + * This function tests the writable bits in the IntTime field of the control + * register to determine the interrupt timer type supported by the hardware. + * + * Note: This function must only be called when the ATCWDT200 watchdog is + * disabled. If the watchdog is enabled, this function returns TMR_UNKNOWN. + * + * Returns: The interrupt timer type supported by the hardware. + */ +static int atcwdt_get_int_timer_type(struct atcwdt_drv *drv_data) +{ + struct device *dev = drv_data->wdt_dev.parent; + unsigned int val; + int ret = 0; + + spin_lock(&drv_data->lock); + regmap_read(drv_data->regmap, REG_CTRL, &val); + if (val & CTRL_WDT_EN) { + spin_unlock(&drv_data->lock); + return TMR_UNKNOWN; + } + + /* + * Configures the IntTime field with the maximum mask value + * (CTRL_INT_TIME_MSK), reads its value from the control register + * to identify the maximum writable bits. + */ + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_write(drv_data->regmap, REG_CTRL, CTRL_INT_TIME_MSK); + regmap_read(drv_data->regmap, REG_CTRL, &val); + spin_unlock(&drv_data->lock); + + val = CTRL_INT_TIME_GET(val); + switch (val) { + case 7: + drv_data->int_timer_type = TMR_INT_16; + break; + case 15: + drv_data->int_timer_type = TMR_INT_32; + break; + default: + drv_data->int_timer_type = TMR_UNKNOWN; + ret = dev_err_probe(dev, -ENODEV, + "Failed to detect interrupt timer type\n"); + } + + return ret; +} + +static int atcwdt_ping(struct watchdog_device *wdt_dev) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_write(drv_data->regmap, REG_RESTART, RESTART_MAGIC); + regmap_update_bits(drv_data->regmap, REG_STATUS, STATUS_INT_EXPIRED, + STATUS_INT_EXPIRED); + spin_unlock(&drv_data->lock); + + return 0; +} + +static int atcwdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + unsigned int value; + unsigned char rst_val; + unsigned char int_val; + + wdt_dev->timeout = timeout; + atcwdt_get_timeout_params(drv_data, timeout, &int_val, &rst_val); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + + value = CTRL_RST_TIME_SET(rst_val) | + CTRL_INT_TIME_SET(int_val) | + CTRL_CLK_SEL_SET(drv_data->clk_src); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_TIME_MSK | + CTRL_INT_TIME_MSK | + CTRL_CLK_SEL, + value); + + spin_unlock(&drv_data->lock); + atcwdt_ping(wdt_dev); + + return 0; +} + +static int atcwdt_start(struct watchdog_device *wdt_dev) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + atcwdt_set_timeout(wdt_dev, wdt_dev->timeout); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_EN | CTRL_WDT_EN, + CTRL_RST_EN | CTRL_WDT_EN); + + spin_unlock(&drv_data->lock); + + return 0; +} + +static int atcwdt_stop(struct watchdog_device *wdt_dev) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_EN | CTRL_WDT_EN, + 0); + spin_unlock(&drv_data->lock); + + return 0; +} + +static int atcwdt_restart(struct watchdog_device *wdt_dev, + unsigned long action, void *data) +{ + struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev); + + atcwdt_set_timeout(wdt_dev, 0); + + spin_lock(&drv_data->lock); + regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC); + regmap_update_bits(drv_data->regmap, + REG_CTRL, + CTRL_RST_EN | CTRL_WDT_EN, + CTRL_RST_EN | CTRL_WDT_EN); + spin_unlock(&drv_data->lock); + + return 0; +} + +static const struct watchdog_ops atcwdt_ops = { + .owner = THIS_MODULE, + .start = atcwdt_start, + .stop = atcwdt_stop, + .ping = atcwdt_ping, + .set_timeout = atcwdt_set_timeout, + .restart = atcwdt_restart, +}; + +static int atcwdt_init_resource(struct platform_device *pdev, + struct atcwdt_drv *drv_data) +{ + struct device *dev = &pdev->dev; + void __iomem *base; + const struct regmap_config cfg = { + .name = "atcwdt", + .reg_bits = 32, + .val_bits = 32, + .cache_type = REGCACHE_NONE, + .reg_stride = 4, + .max_register = REG_STATUS, + }; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "Failed to ioremap I/O resource\n"); + + drv_data->regmap = devm_regmap_init_mmio(dev, base, &cfg); + if (IS_ERR(drv_data->regmap)) + return dev_err_probe(dev, PTR_ERR(drv_data->regmap), + "Failed to create regmap\n"); + + return 0; +} + +static int atcwdt_enable_clk(struct atcwdt_drv *drv_data) +{ + struct device *dev = drv_data->wdt_dev.parent; + unsigned int val; + int clk_src; + + drv_data->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(drv_data->clk)) + return dev_err_probe(dev, PTR_ERR(drv_data->clk), + "Failed to get watchdog clock\n"); + + drv_data->clk_freq = clk_get_rate(drv_data->clk); + if (!drv_data->clk_freq) + return dev_err_probe(dev, -EINVAL, + "Failed to get clock rate\n"); + + clk_src = device_property_read_u32(dev, "andestech,clock-source", &val); + drv_data->clk_src = (!clk_src && val != 0) ? CTRL_CLK_SEL_PCLK : 0; + + return 0; +} + +static int atcwdt_init_wdt_device(struct device *dev, + struct atcwdt_drv *drv_data) +{ + struct watchdog_device *wdd = &drv_data->wdt_dev; + + wdd->parent = dev; + wdd->info = &atcwdt_info; + wdd->ops = &atcwdt_ops; + wdd->timeout = ATCWDT_TIMEOUT; + wdd->min_timeout = 1; + + watchdog_set_nowayout(wdd, nowayout); + watchdog_set_drvdata(wdd, drv_data); + + return 0; +} + +static void atcwdt_calc_max_timeout(struct atcwdt_drv *drv_data) +{ + unsigned char rst_idx = atcwdt_get_index(0xFF, TMR_RST); + unsigned char int_idx = atcwdt_get_index(0xFF, + drv_data->int_timer_type); + + drv_data->wdt_dev.max_timeout = + ((1U << rst_idx) + (1U << int_idx)) / drv_data->clk_freq; +} + +static int atcwdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atcwdt_drv *drv_data; + int ret; + + drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + platform_set_drvdata(pdev, drv_data); + spin_lock_init(&drv_data->lock); + + ret = atcwdt_init_wdt_device(dev, drv_data); + if (ret) + return ret; + + ret = atcwdt_init_resource(pdev, drv_data); + if (ret) + return ret; + + ret = atcwdt_enable_clk(drv_data); + if (ret) + return ret; + + ret = atcwdt_get_int_timer_type(drv_data); + if (ret) + return ret; + + atcwdt_calc_max_timeout(drv_data); + + ret = devm_watchdog_register_device(dev, &drv_data->wdt_dev); + + return ret; +} + +static int atcwdt_suspend(struct device *dev) +{ + struct atcwdt_drv *drv_data = dev_get_drvdata(dev); + + if (watchdog_active(&drv_data->wdt_dev)) { + atcwdt_stop(&drv_data->wdt_dev); + clk_disable_unprepare(drv_data->clk); + } + + return 0; +} + +static int atcwdt_resume(struct device *dev) +{ + struct atcwdt_drv *drv_data = dev_get_drvdata(dev); + int ret = 0; + + if (watchdog_active(&drv_data->wdt_dev)) { + ret = clk_prepare_enable(drv_data->clk); + if (ret) + return ret; + atcwdt_start(&drv_data->wdt_dev); + atcwdt_ping(&drv_data->wdt_dev); + } + + return ret; +} + +static const struct of_device_id atcwdt_match[] = { + { .compatible = "andestech,ae350-wdt" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, atcwdt_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(atcwdt_pm_ops, atcwdt_suspend, atcwdt_resume); + +static struct platform_driver atcwdt_driver = { + .probe = atcwdt_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = atcwdt_match, + .pm = pm_sleep_ptr(&atcwdt_pm_ops), + }, +}; + +module_platform_driver(atcwdt_driver); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" + __MODULE_STRING(ATCWDT_TIMEOUT) ")"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CL Wang <cl634@andestech.com>"); +MODULE_DESCRIPTION("Andes ATCWDT200 Watchdog timer driver"); diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 9fcfee63905b..6fd8b1b8e386 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/of_address.h> #include <linux/of_platform.h> +#include <linux/reboot.h> #define PM_RSTC 0x1c #define PM_RSTS 0x20 @@ -49,8 +50,6 @@ struct bcm2835_wdt { spinlock_t lock; }; -static struct bcm2835_wdt *bcm2835_power_off_wdt; - static unsigned int heartbeat; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -150,9 +149,9 @@ static struct watchdog_device bcm2835_wdt_wdd = { * indicate to bootcode.bin not to reboot, then most of the chip will be * powered off. */ -static void bcm2835_power_off(void) +static int bcm2835_power_off(struct sys_off_data *data) { - struct bcm2835_wdt *wdt = bcm2835_power_off_wdt; + struct bcm2835_wdt *wdt = data->cb_data; u32 val; /* @@ -166,6 +165,8 @@ static void bcm2835_power_off(void) /* Continue with normal reset mechanism */ __bcm2835_restart(wdt); + + return NOTIFY_DONE; } static int bcm2835_wdt_probe(struct platform_device *pdev) @@ -206,28 +207,17 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) if (err) return err; - if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) { - if (!pm_power_off) { - pm_power_off = bcm2835_power_off; - bcm2835_power_off_wdt = wdt; - } else { - dev_info(dev, "Poweroff handler already present!\n"); - } - } + if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) + devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, + bcm2835_power_off, wdt); dev_info(dev, "Broadcom BCM2835 watchdog timer"); return 0; } -static void bcm2835_wdt_remove(struct platform_device *pdev) -{ - if (pm_power_off == bcm2835_power_off) - pm_power_off = NULL; -} - static struct platform_driver bcm2835_wdt_driver = { .probe = bcm2835_wdt_probe, - .remove = bcm2835_wdt_remove, .driver = { .name = "bcm2835-wdt", }, diff --git a/drivers/watchdog/gunyah_wdt.c b/drivers/watchdog/gunyah_wdt.c new file mode 100644 index 000000000000..49dfef459e84 --- /dev/null +++ b/drivers/watchdog/gunyah_wdt.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/arm-smccc.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#define GUNYAH_WDT_SMCCC_CALL_VAL(func_id) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\ + ARM_SMCCC_OWNER_VENDOR_HYP, func_id) + +/* SMCCC function IDs for watchdog operations */ +#define GUNYAH_WDT_CONTROL GUNYAH_WDT_SMCCC_CALL_VAL(0x0005) +#define GUNYAH_WDT_STATUS GUNYAH_WDT_SMCCC_CALL_VAL(0x0006) +#define GUNYAH_WDT_PING GUNYAH_WDT_SMCCC_CALL_VAL(0x0007) +#define GUNYAH_WDT_SET_TIME GUNYAH_WDT_SMCCC_CALL_VAL(0x0008) + +/* + * Control values for GUNYAH_WDT_CONTROL. + * Bit 0 is used to enable or disable the watchdog. If this bit is set, + * then the watchdog is enabled and vice versa. + * Bit 1 should always be set to 1 as this bit is reserved in Gunyah and + * it's expected to be 1. + */ +#define WDT_CTRL_ENABLE (BIT(1) | BIT(0)) +#define WDT_CTRL_DISABLE BIT(1) + +enum gunyah_error { + GUNYAH_ERROR_OK = 0, + GUNYAH_ERROR_UNIMPLEMENTED = -1, + GUNYAH_ERROR_ARG_INVAL = 1, +}; + +/** + * gunyah_error_remap() - Remap Gunyah hypervisor errors into a Linux error code + * @gunyah_error: Gunyah hypercall return value + */ +static inline int gunyah_error_remap(enum gunyah_error gunyah_error) +{ + switch (gunyah_error) { + case GUNYAH_ERROR_OK: + return 0; + case GUNYAH_ERROR_UNIMPLEMENTED: + return -EOPNOTSUPP; + default: + return -EINVAL; + } +} + +static int gunyah_wdt_call(unsigned long func_id, unsigned long arg1, + unsigned long arg2) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(func_id, arg1, arg2, &res); + return gunyah_error_remap(res.a0); +} + +static int gunyah_wdt_start(struct watchdog_device *wdd) +{ + unsigned int timeout_ms; + struct device *dev = wdd->parent; + int ret; + + ret = gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0); + if (ret && watchdog_active(wdd)) { + dev_err(dev, "%s: Failed to stop gunyah wdt %d\n", __func__, ret); + return ret; + } + + timeout_ms = wdd->timeout * 1000; + ret = gunyah_wdt_call(GUNYAH_WDT_SET_TIME, timeout_ms, timeout_ms); + if (ret) { + dev_err(dev, "%s: Failed to set timeout for gunyah wdt %d\n", + __func__, ret); + return ret; + } + + ret = gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_ENABLE, 0); + if (ret) + dev_err(dev, "%s: Failed to start gunyah wdt %d\n", __func__, ret); + + return ret; +} + +static int gunyah_wdt_stop(struct watchdog_device *wdd) +{ + return gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0); +} + +static int gunyah_wdt_ping(struct watchdog_device *wdd) +{ + return gunyah_wdt_call(GUNYAH_WDT_PING, 0, 0); +} + +static int gunyah_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout_sec) +{ + wdd->timeout = timeout_sec; + + if (watchdog_active(wdd)) + return gunyah_wdt_start(wdd); + + return 0; +} + +static int gunyah_wdt_get_time_since_last_ping(void) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(GUNYAH_WDT_STATUS, 0, 0, &res); + if (res.a0) + return gunyah_error_remap(res.a0); + + return res.a2 / 1000; +} + +static unsigned int gunyah_wdt_get_timeleft(struct watchdog_device *wdd) +{ + int seconds_since_last_ping; + + seconds_since_last_ping = gunyah_wdt_get_time_since_last_ping(); + if (seconds_since_last_ping < 0 || + seconds_since_last_ping > wdd->timeout) + return 0; + + return wdd->timeout - seconds_since_last_ping; +} + +static int gunyah_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) +{ + /* Set timeout to 1ms and send a ping */ + gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0); + gunyah_wdt_call(GUNYAH_WDT_SET_TIME, 1, 1); + gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_ENABLE, 0); + gunyah_wdt_call(GUNYAH_WDT_PING, 0, 0); + + /* Wait to make sure reset occurs */ + mdelay(100); + + return 0; +} + +static const struct watchdog_info gunyah_wdt_info = { + .identity = "Gunyah Watchdog", + .options = WDIOF_SETTIMEOUT + | WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops gunyah_wdt_ops = { + .owner = THIS_MODULE, + .start = gunyah_wdt_start, + .stop = gunyah_wdt_stop, + .ping = gunyah_wdt_ping, + .set_timeout = gunyah_wdt_set_timeout, + .get_timeleft = gunyah_wdt_get_timeleft, + .restart = gunyah_wdt_restart +}; + +static int gunyah_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct device *dev = &pdev->dev; + int ret; + + ret = gunyah_wdt_call(GUNYAH_WDT_STATUS, 0, 0); + if (ret == -EOPNOTSUPP) + return -ENODEV; + + if (ret) + return dev_err_probe(dev, ret, "status check failed\n"); + + wdd = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL); + if (!wdd) + return -ENOMEM; + + wdd->info = &gunyah_wdt_info; + wdd->ops = &gunyah_wdt_ops; + wdd->parent = dev; + + /* + * Although Gunyah expects 16-bit unsigned int values as timeout values + * in milliseconds, values above 0x8000 are reserved. This limits the + * max timeout value to 32 seconds. + */ + wdd->max_timeout = 32; /* seconds */ + wdd->min_timeout = 1; /* seconds */ + wdd->timeout = wdd->max_timeout; + + gunyah_wdt_stop(wdd); + platform_set_drvdata(pdev, wdd); + watchdog_set_restart_priority(wdd, 0); + + return devm_watchdog_register_device(dev, wdd); +} + +static void gunyah_wdt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdd = platform_get_drvdata(pdev); + + gunyah_wdt_stop(wdd); +} + +static int gunyah_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + gunyah_wdt_stop(wdd); + + return 0; +} + +static int gunyah_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + gunyah_wdt_start(wdd); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(gunyah_wdt_pm_ops, gunyah_wdt_suspend, gunyah_wdt_resume); + +/* + * Gunyah watchdog is a vendor-specific hypervisor interface provided by the + * Gunyah hypervisor. Using QCOM SCM driver to detect Gunyah watchdog SMCCC + * hypervisor service and register platform device when the service is available + * allows this driver to operate independently of the devicetree and avoids + * adding the non-hardware nodes to the devicetree. + */ +static const struct platform_device_id gunyah_wdt_id[] = { + { .name = "gunyah-wdt" }, + {} +}; +MODULE_DEVICE_TABLE(platform, gunyah_wdt_id); + +static struct platform_driver gunyah_wdt_driver = { + .driver = { + .name = "gunyah-wdt", + .pm = pm_sleep_ptr(&gunyah_wdt_pm_ops), + }, + .id_table = gunyah_wdt_id, + .probe = gunyah_wdt_probe, + .remove = gunyah_wdt_remove, +}; + +module_platform_driver(gunyah_wdt_driver); + +MODULE_DESCRIPTION("Gunyah Watchdog Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 2a848c35c14d..8af1fad2de0b 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -23,6 +23,7 @@ #include <linux/watchdog.h> #ifdef CONFIG_HPWDT_NMI_DECODING #include <asm/nmi.h> +#include <asm/uv/uv.h> #endif #include <linux/crash_dump.h> @@ -159,24 +160,31 @@ static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req) return 0; } -static int hpwdt_my_nmi(void) -{ - return ioread8(hpwdt_nmistat) & 0x6; -} +#define NMISTAT_EASR BIT(0) +#define NMISTAT_EWDOG BIT(1) +#define NMISTAT_RTRAP BIT(2) +#define NMISTAT_DBELL BIT(3) +#define NMISTAT_EMSWDG BIT(4) +#define NMISTAT_GPI BIT(5) +#define NMISTAT_NMIOUT BIT(7) /* * NMI Handler */ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) { - unsigned int mynmi = hpwdt_my_nmi(); - static char panic_msg[] = + u8 nmistat = ioread8(hpwdt_nmistat); + bool mynmi = (nmistat & (NMISTAT_EWDOG | NMISTAT_RTRAP)) != 0; + static char panic_msg_default[] = "00: An NMI occurred. Depending on your system the reason " "for the NMI is logged in any one of the following resources:\n" "1. Integrated Management Log (IML)\n" "2. OA Syslog\n" "3. OA Forward Progress Log\n" "4. iLO Event Log"; + static char panic_msg_uv[] = + "00: A watchdog NMI occurred."; + char *panic_msg = is_uv_system() ? panic_msg_uv : panic_msg_default; if (ulReason == NMI_UNKNOWN && !mynmi) return NMI_DONE; @@ -190,7 +198,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) hpwdt_ping_ticks(SECS_TO_TICKS(val)); } - hex_byte_pack(panic_msg, mynmi); + hex_byte_pack(panic_msg, nmistat); nmi_panic(regs, panic_msg); return NMI_HANDLED; diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index 03479110453c..855dc9d5083a 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -56,6 +56,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" struct imx_wdt_hw_feature { bool prescaler_enable; bool post_rcs_wait; + bool cpu_lpm_auto_cg; u32 wdog_clock_rate; }; @@ -360,7 +361,7 @@ static int __maybe_unused imx7ulp_wdt_suspend_noirq(struct device *dev) { struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); - if (watchdog_active(&imx7ulp_wdt->wdd)) + if (watchdog_active(&imx7ulp_wdt->wdd) && !imx7ulp_wdt->hw->cpu_lpm_auto_cg) imx7ulp_wdt_stop(&imx7ulp_wdt->wdd); clk_disable_unprepare(imx7ulp_wdt->clk); @@ -408,10 +409,17 @@ static const struct imx_wdt_hw_feature imx93_wdt_hw = { .wdog_clock_rate = 125, }; +static const struct imx_wdt_hw_feature imx94_wdt_hw = { + .prescaler_enable = true, + .wdog_clock_rate = 125, + .cpu_lpm_auto_cg = true, +}; + static const struct of_device_id imx7ulp_wdt_dt_ids[] = { { .compatible = "fsl,imx7ulp-wdt", .data = &imx7ulp_wdt_hw, }, { .compatible = "fsl,imx8ulp-wdt", .data = &imx8ulp_wdt_hw, }, { .compatible = "fsl,imx93-wdt", .data = &imx93_wdt_hw, }, + { .compatible = "fsl,imx94-wdt", .data = &imx94_wdt_hw, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids); diff --git a/drivers/watchdog/lenovo_se10_wdt.c b/drivers/watchdog/lenovo_se10_wdt.c index cd0500e5080b..503e220263f9 100644 --- a/drivers/watchdog/lenovo_se10_wdt.c +++ b/drivers/watchdog/lenovo_se10_wdt.c @@ -178,7 +178,7 @@ static int se10_wdt_probe(struct platform_device *pdev) return -EBUSY; chip_id = get_chipID(); - if (chip_id != 0x5632) { + if (chip_id != 0x5632 && chip_id != 0x5652) { release_region(CFG_PORT, CFG_SIZE); return -ENODEV; } @@ -224,7 +224,7 @@ static struct platform_driver se10_wdt_driver = { .probe = se10_wdt_probe, }; -static int se10_create_platform_device(const struct dmi_system_id *id) +static int se10_create_platform_device(void) { int err; @@ -233,9 +233,10 @@ static int se10_create_platform_device(const struct dmi_system_id *id) return -ENOMEM; err = platform_device_add(se10_pdev); - if (err) + if (err) { platform_device_put(se10_pdev); - + se10_pdev = NULL; + } return err; } @@ -246,7 +247,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NH"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -254,7 +254,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NJ"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -262,7 +261,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NK"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -270,7 +268,6 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NL"), }, - .callback = se10_create_platform_device, }, { .ident = "LENOVO-SE10", @@ -278,7 +275,62 @@ static const struct dmi_system_id se10_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "12NM"), }, - .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13LJ"), + }, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13LK"), + }, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S1"), + }, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S2"), + }, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S3"), + }, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S4"), + }, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S5"), + }, + }, + { + .ident = "LENOVO-SE10-G2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "13S6"), + }, }, {} }; @@ -286,10 +338,20 @@ MODULE_DEVICE_TABLE(dmi, se10_dmi_table); static int __init se10_wdt_init(void) { + int err; + if (!dmi_check_system(se10_dmi_table)) return -ENODEV; - return platform_driver_register(&se10_wdt_driver); + err = platform_driver_register(&se10_wdt_driver); + if (err) + return err; + + err = se10_create_platform_device(); + if (err) + platform_driver_unregister(&se10_wdt_driver); + + return err; } static void __exit se10_wdt_exit(void) diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c deleted file mode 100644 index 0ae8e5bc10ae..000000000000 --- a/drivers/watchdog/machzwd.c +++ /dev/null @@ -1,452 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * MachZ ZF-Logic Watchdog Timer driver for Linux - * - * The author does NOT admit liability nor provide warranty for - * any of this software. This material is provided "AS-IS" in - * the hope that it may be useful for others. - * - * Author: Fernando Fuganti <fuganti@conectiva.com.br> - * - * Based on sbc60xxwdt.c by Jakob Oestergaard - * - * We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the - * following periods: - * wd#1 - 2 seconds; - * wd#2 - 7.2 ms; - * After the expiration of wd#1, it can generate a NMI, SCI, SMI, or - * a system RESET and it starts wd#2 that unconditionally will RESET - * the system when the counter reaches zero. - * - * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/types.h> -#include <linux/timer.h> -#include <linux/jiffies.h> -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/fs.h> -#include <linux/ioport.h> -#include <linux/notifier.h> -#include <linux/reboot.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/uaccess.h> - - -/* ports */ -#define ZF_IOBASE 0x218 -#define INDEX 0x218 -#define DATA_B 0x219 -#define DATA_W 0x21A -#define DATA_D 0x21A - -/* indexes */ /* size */ -#define ZFL_VERSION 0x02 /* 16 */ -#define CONTROL 0x10 /* 16 */ -#define STATUS 0x12 /* 8 */ -#define COUNTER_1 0x0C /* 16 */ -#define COUNTER_2 0x0E /* 8 */ -#define PULSE_LEN 0x0F /* 8 */ - -/* controls */ -#define ENABLE_WD1 0x0001 -#define ENABLE_WD2 0x0002 -#define RESET_WD1 0x0010 -#define RESET_WD2 0x0020 -#define GEN_SCI 0x0100 -#define GEN_NMI 0x0200 -#define GEN_SMI 0x0400 -#define GEN_RESET 0x0800 - - -/* utilities */ - -#define WD1 0 -#define WD2 1 - -#define zf_writew(port, data) { outb(port, INDEX); outw(data, DATA_W); } -#define zf_writeb(port, data) { outb(port, INDEX); outb(data, DATA_B); } -#define zf_get_ZFL_version() zf_readw(ZFL_VERSION) - - -static unsigned short zf_readw(unsigned char port) -{ - outb(port, INDEX); - return inw(DATA_W); -} - - -MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>"); -MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver"); -MODULE_LICENSE("GPL"); - -static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, - "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -#define PFX "machzwd" - -static const struct watchdog_info zf_info = { - .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "ZF-Logic watchdog", -}; - - -/* - * action refers to action taken when watchdog resets - * 0 = GEN_RESET - * 1 = GEN_SMI - * 2 = GEN_NMI - * 3 = GEN_SCI - * defaults to GEN_RESET (0) - */ -static int action; -module_param(action, int, 0); -MODULE_PARM_DESC(action, "after watchdog resets, generate: " - "0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI"); - -static void zf_ping(struct timer_list *unused); - -static int zf_action = GEN_RESET; -static unsigned long zf_is_open; -static char zf_expect_close; -static DEFINE_SPINLOCK(zf_port_lock); -static DEFINE_TIMER(zf_timer, zf_ping); -static unsigned long next_heartbeat; - - -/* timeout for user land heart beat (10 seconds) */ -#define ZF_USER_TIMEO (HZ*10) - -/* timeout for hardware watchdog (~500ms) */ -#define ZF_HW_TIMEO (HZ/2) - -/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */ -#define ZF_CTIMEOUT 0xffff - -#ifndef ZF_DEBUG -#define dprintk(format, args...) -#else -#define dprintk(format, args...) \ - pr_debug(":%s:%d: " format, __func__, __LINE__ , ## args) -#endif - - -static inline void zf_set_status(unsigned char new) -{ - zf_writeb(STATUS, new); -} - - -/* CONTROL register functions */ - -static inline unsigned short zf_get_control(void) -{ - return zf_readw(CONTROL); -} - -static inline void zf_set_control(unsigned short new) -{ - zf_writew(CONTROL, new); -} - - -/* WD#? counter functions */ -/* - * Just set counter value - */ - -static inline void zf_set_timer(unsigned short new, unsigned char n) -{ - switch (n) { - case WD1: - zf_writew(COUNTER_1, new); - fallthrough; - case WD2: - zf_writeb(COUNTER_2, new > 0xff ? 0xff : new); - fallthrough; - default: - return; - } -} - -/* - * stop hardware timer - */ -static void zf_timer_off(void) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - /* stop internal ping */ - timer_delete_sync(&zf_timer); - - spin_lock_irqsave(&zf_port_lock, flags); - /* stop watchdog timer */ - ctrl_reg = zf_get_control(); - ctrl_reg |= (ENABLE_WD1|ENABLE_WD2); /* disable wd1 and wd2 */ - ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - pr_info("Watchdog timer is now disabled\n"); -} - - -/* - * start hardware timer - */ -static void zf_timer_on(void) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - spin_lock_irqsave(&zf_port_lock, flags); - - zf_writeb(PULSE_LEN, 0xff); - - zf_set_timer(ZF_CTIMEOUT, WD1); - - /* user land ping */ - next_heartbeat = jiffies + ZF_USER_TIMEO; - - /* start the timer for internal ping */ - mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); - - /* start watchdog timer */ - ctrl_reg = zf_get_control(); - ctrl_reg |= (ENABLE_WD1|zf_action); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - pr_info("Watchdog timer is now enabled\n"); -} - - -static void zf_ping(struct timer_list *unused) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - zf_writeb(COUNTER_2, 0xff); - - if (time_before(jiffies, next_heartbeat)) { - dprintk("time_before: %ld\n", next_heartbeat - jiffies); - /* - * reset event is activated by transition from 0 to 1 on - * RESET_WD1 bit and we assume that it is already zero... - */ - - spin_lock_irqsave(&zf_port_lock, flags); - ctrl_reg = zf_get_control(); - ctrl_reg |= RESET_WD1; - zf_set_control(ctrl_reg); - - /* ...and nothing changes until here */ - ctrl_reg &= ~(RESET_WD1); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); - } else - pr_crit("I will reset your machine\n"); -} - -static ssize_t zf_write(struct file *file, const char __user *buf, size_t count, - loff_t *ppos) -{ - /* See if we got the magic character */ - if (count) { - /* - * no need to check for close confirmation - * no way to disable watchdog ;) - */ - if (!nowayout) { - size_t ofs; - /* - * note: just in case someone wrote the magic character - * five months ago... - */ - zf_expect_close = 0; - - /* now scan */ - for (ofs = 0; ofs != count; ofs++) { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == 'V') { - zf_expect_close = 42; - dprintk("zf_expect_close = 42\n"); - } - } - } - - /* - * Well, anyhow someone wrote to us, - * we should return that favour - */ - next_heartbeat = jiffies + ZF_USER_TIMEO; - dprintk("user ping at %ld\n", jiffies); - } - return count; -} - -static long zf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &zf_info, sizeof(zf_info))) - return -EFAULT; - break; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_KEEPALIVE: - zf_ping(NULL); - break; - default: - return -ENOTTY; - } - return 0; -} - -static int zf_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &zf_is_open)) - return -EBUSY; - if (nowayout) - __module_get(THIS_MODULE); - zf_timer_on(); - return stream_open(inode, file); -} - -static int zf_close(struct inode *inode, struct file *file) -{ - if (zf_expect_close == 42) - zf_timer_off(); - else { - timer_delete(&zf_timer); - pr_err("device file closed unexpectedly. Will not stop the WDT!\n"); - } - clear_bit(0, &zf_is_open); - zf_expect_close = 0; - return 0; -} - -/* - * Notifier for system down - */ - -static int zf_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - zf_timer_off(); - return NOTIFY_DONE; -} - -static const struct file_operations zf_fops = { - .owner = THIS_MODULE, - .write = zf_write, - .unlocked_ioctl = zf_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = zf_open, - .release = zf_close, -}; - -static struct miscdevice zf_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &zf_fops, -}; - - -/* - * The device needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ -static struct notifier_block zf_notifier = { - .notifier_call = zf_notify_sys, -}; - -static void __init zf_show_action(int act) -{ - static const char * const str[] = { "RESET", "SMI", "NMI", "SCI" }; - - pr_info("Watchdog using action = %s\n", str[act]); -} - -static int __init zf_init(void) -{ - int ret; - - pr_info("MachZ ZF-Logic Watchdog driver initializing\n"); - - ret = zf_get_ZFL_version(); - if (!ret || ret == 0xffff) { - pr_warn("no ZF-Logic found\n"); - return -ENODEV; - } - - if (action <= 3 && action >= 0) - zf_action = zf_action >> action; - else - action = 0; - - zf_show_action(action); - - if (!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")) { - pr_err("cannot reserve I/O ports at %d\n", ZF_IOBASE); - ret = -EBUSY; - goto no_region; - } - - ret = register_reboot_notifier(&zf_notifier); - if (ret) { - pr_err("can't register reboot notifier (err=%d)\n", ret); - goto no_reboot; - } - - ret = misc_register(&zf_miscdev); - if (ret) { - pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR); - goto no_misc; - } - - zf_set_status(0); - zf_set_control(0); - - return 0; - -no_misc: - unregister_reboot_notifier(&zf_notifier); -no_reboot: - release_region(ZF_IOBASE, 3); -no_region: - return ret; -} - - -static void __exit zf_exit(void) -{ - zf_timer_off(); - - misc_deregister(&zf_miscdev); - unregister_reboot_notifier(&zf_notifier); - release_region(ZF_IOBASE, 3); -} - -module_init(zf_init); -module_exit(zf_exit); diff --git a/drivers/watchdog/menz69_wdt.c b/drivers/watchdog/menz69_wdt.c index 6e5e4e5c0b56..3fe23451135d 100644 --- a/drivers/watchdog/menz69_wdt.c +++ b/drivers/watchdog/menz69_wdt.c @@ -163,5 +163,4 @@ module_mcb_driver(men_z069_driver); MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>"); MODULE_DESCRIPTION("Watchdog driver for the MEN z069 IP-Core"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("mcb:16z069"); MODULE_IMPORT_NS("MCB"); diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index dfaac5995c84..49bd04841f0c 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/watchdog.h> @@ -42,6 +43,7 @@ struct qcom_wdt_match_data { const u32 *offset; bool pretimeout; u32 max_tick_count; + u32 wdt_reason_val; }; struct qcom_wdt { @@ -185,6 +187,7 @@ static const struct qcom_wdt_match_data match_data_ipq5424 = { .offset = reg_offset_data_kpss, .pretimeout = true, .max_tick_count = 0xFFFFFU, + .wdt_reason_val = 5, }; static const struct qcom_wdt_match_data match_data_kpss = { @@ -193,6 +196,40 @@ static const struct qcom_wdt_match_data match_data_kpss = { .max_tick_count = 0xFFFFFU, }; +static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt, + u32 val) +{ + struct device_node *imem; + struct resource res; + void __iomem *addr; + int ret; + + imem = of_parse_phandle(dev->of_node, "sram", 0); + if (!imem) { + /* Read the EXPIRED_STATUS bit as a fallback */ + if (readl(wdt_addr(wdt, WDT_STS)) & 1) + wdt->wdd.bootstatus = WDIOF_CARDRESET; + + return 0; + } + + ret = of_address_to_resource(imem, 0, &res); + of_node_put(imem); + if (ret) + return ret; + + addr = ioremap(res.start, resource_size(&res)); + if (!addr) + return -ENOMEM; + + if (readl(addr) == val) + wdt->wdd.bootstatus = WDIOF_CARDRESET; + + iounmap(addr); + + return 0; +} + static int qcom_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -273,8 +310,9 @@ static int qcom_wdt_probe(struct platform_device *pdev) wdt->wdd.parent = dev; wdt->layout = data->offset; - if (readl(wdt_addr(wdt, WDT_STS)) & 1) - wdt->wdd.bootstatus = WDIOF_CARDRESET; + ret = qcom_wdt_get_bootstatus(dev, wdt, data->wdt_reason_val); + if (ret) + dev_err(dev, "failed to get the bootstatus, %d\n", ret); /* * If 'timeout-sec' unspecified in devicetree, assume a 30 second diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c index 2c30ddd574c5..01b3ef89bacf 100644 --- a/drivers/watchdog/realtek_otto_wdt.c +++ b/drivers/watchdog/realtek_otto_wdt.c @@ -114,12 +114,6 @@ static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale) * the value stored in those fields. This means each phase will run for at least * one tick, so small values need to be clamped to correctly reflect the timeout. */ -static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration, - unsigned int min_ticks) -{ - return max(min_ticks, DIV_ROUND_UP(val, tick_duration)); -} - static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout, unsigned int pretimeout) { @@ -140,9 +134,9 @@ static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned in return -EINVAL; tick_ms = otto_wdt_tick_ms(ctrl, prescale); - total_ticks = div_round_ticks(timeout_ms, tick_ms, 2); - phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1); - phase2_ticks = total_ticks - phase1_ticks; + total_ticks = max(2, DIV_ROUND_UP(timeout_ms, tick_ms)); + phase2_ticks = max(1, pretimeout_ms / tick_ms); + phase1_ticks = total_ticks - phase2_ticks; prescale_next++; } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX @@ -302,15 +296,15 @@ static int otto_wdt_probe(struct platform_device *pdev) if (IS_ERR(ctrl->base)) return PTR_ERR(ctrl->base); + ret = otto_wdt_probe_clk(ctrl); + if (ret) + return ret; + /* Clear any old interrupts and reset initial state */ iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2, ctrl->base + OTTO_WDT_REG_INTR); iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL); - ret = otto_wdt_probe_clk(ctrl); - if (ret) - return ret; - ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1"); if (ctrl->irq_phase1 < 0) return ctrl->irq_phase1; diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index be7d7db47591..c3c7715140ea 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -35,7 +35,8 @@ #define RTIWWDRXCTRL 0xa4 #define RTIWWDSIZECTRL 0xa8 -#define RTIWWDRX_NMI 0xa +#define RTIWWDRXN_RST 0x5 +#define RTIWWDRXN_NMI 0xa #define RTIWWDSIZE_50P 0x50 #define RTIWWDSIZE_25P 0x500 @@ -63,22 +64,29 @@ static int heartbeat; +struct rti_wdt_data { + bool nmi; +}; + /* * struct to hold data for each WDT device * @base - base io address of WD device * @freq - source clock frequency of WDT * @wdd - hold watchdog device as is in WDT core + * @nmi - Set if this WDT instance supports generating NMI */ struct rti_wdt_device { void __iomem *base; unsigned long freq; struct watchdog_device wdd; + bool nmi; }; static int rti_wdt_start(struct watchdog_device *wdd) { u32 timer_margin; struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd); + u8 reaction; int ret; ret = pm_runtime_resume_and_get(wdd->parent); @@ -101,8 +109,13 @@ static int rti_wdt_start(struct watchdog_device *wdd) */ wdd->min_hw_heartbeat_ms = 520 * wdd->timeout + MAX_HW_ERROR; - /* Generate NMI when wdt expires */ - writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL); + /* When WDT expires, generate NMI or reset if NMI not supported */ + if (wdt->nmi) + reaction = RTIWWDRXN_NMI; + else + reaction = RTIWWDRXN_RST; + + writel_relaxed(reaction, wdt->base + RTIWWDRXCTRL); /* Open window size 50%; this is the largest window size available */ writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL); @@ -210,6 +223,7 @@ static int rti_wdt_probe(struct platform_device *pdev) { int ret = 0; struct device *dev = &pdev->dev; + const struct rti_wdt_data *data; struct watchdog_device *wdd; struct rti_wdt_device *wdt; struct clk *clk; @@ -254,6 +268,14 @@ static int rti_wdt_probe(struct platform_device *pdev) wdd->timeout = DEFAULT_HEARTBEAT; wdd->parent = dev; + data = device_get_match_data(dev); + if (!data) { + ret = -ENODEV; + goto err_iomap; + } + + wdt->nmi = data->nmi; + watchdog_set_drvdata(wdd, wdt); watchdog_set_nowayout(wdd, 1); watchdog_set_restart_priority(wdd, 128); @@ -361,8 +383,17 @@ static void rti_wdt_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } +static const struct rti_wdt_data rti_wdt_j7_data = { + .nmi = true, +}; + +static const struct rti_wdt_data rti_wdt_am62l_data = { + .nmi = false, +}; + static const struct of_device_id rti_wdt_of_match[] = { - { .compatible = "ti,j7-rti-wdt", }, + { .compatible = "ti,j7-rti-wdt", .data = &rti_wdt_j7_data }, + { .compatible = "ti,am62l-rti-wdt", .data = &rti_wdt_am62l_data }, {}, }; MODULE_DEVICE_TABLE(of, rti_wdt_of_match); diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c index 96fd04fbc2a2..4fdc5363ba98 100644 --- a/drivers/watchdog/rzn1_wdt.c +++ b/drivers/watchdog/rzn1_wdt.c @@ -79,14 +79,6 @@ static int rzn1_wdt_start(struct watchdog_device *w) return 0; } -static irqreturn_t rzn1_wdt_irq(int irq, void *_wdt) -{ - pr_crit("RZN1 Watchdog. Initiating system reboot\n"); - emergency_restart(); - - return IRQ_HANDLED; -} - static struct watchdog_info rzn1_wdt_info = { .identity = "RZ/N1 Watchdog", .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, @@ -102,11 +94,9 @@ static int rzn1_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rzn1_watchdog *wdt; - struct device_node *np = dev->of_node; - struct clk *clk; unsigned long clk_rate; + struct clk *clk; int ret; - int irq; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) @@ -116,28 +106,13 @@ static int rzn1_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0, - np->name, wdt); - if (ret) { - dev_err(dev, "failed to request irq %d\n", irq); - return ret; - } - clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) { - dev_err(dev, "failed to get the clock\n"); - return PTR_ERR(clk); - } + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get the clock\n"); clk_rate = clk_get_rate(clk); - if (!clk_rate) { - dev_err(dev, "failed to get the clock rate\n"); - return -EINVAL; - } + if (!clk_rate) + return dev_err_probe(dev, -EINVAL, "failed to get the clock rate\n"); wdt->clk_rate_khz = clk_rate / 1000; wdt->wdtdev.info = &rzn1_wdt_info; diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 13e72918338a..704b786cc2ec 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -30,6 +30,7 @@ struct sama5d4_wdt { void __iomem *reg_base; u32 mr; u32 ir; + u32 wddis_mask; unsigned long last_ping; bool need_irq; bool sam9x60_support; @@ -48,7 +49,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS)) +static inline bool wdt_enabled(struct sama5d4_wdt *wdt) +{ + return !(wdt->mr & wdt->wddis_mask); +} #define wdt_read(wdt, field) \ readl_relaxed((wdt)->reg_base + (field)) @@ -81,12 +85,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER); - wdt->mr &= ~AT91_SAM9X60_WDDIS; - } else { - wdt->mr &= ~AT91_WDT_WDDIS; - } + wdt->mr &= ~wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -96,12 +97,9 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR); - wdt->mr |= AT91_SAM9X60_WDDIS; - } else { - wdt->mr |= AT91_WDT_WDDIS; - } + wdt->mr |= wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -117,7 +115,7 @@ static int sama5d4_wdt_ping(struct watchdog_device *wdd) } static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, - unsigned int timeout) + unsigned int timeout) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); u32 value = WDT_SEC2TICKS(timeout); @@ -140,8 +138,8 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, * If the watchdog is enabled, then the timeout can be updated. Else, * wait that the user enables it. */ - if (wdt_enabled) - wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); + if (wdt_enabled(wdt)) + wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~wdt->wddis_mask); wdd->timeout = timeout; @@ -184,10 +182,7 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - if (wdt->sam9x60_support) - wdt->mr = AT91_SAM9X60_WDDIS; - else - wdt->mr = AT91_WDT_WDDIS; + wdt->mr = wdt->wddis_mask; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) @@ -213,15 +208,11 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) * If the watchdog is already running, we can safely update it. * Else, we have to disable it properly. */ - if (!wdt_enabled) { + if (!wdt_enabled(wdt)) { reg = wdt_read(wdt, AT91_WDT_MR); - if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS))) - wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_SAM9X60_WDDIS); - else if (!wdt->sam9x60_support && - (!(reg & AT91_WDT_WDDIS))) + if (!(reg & wdt->wddis_mask)) wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_WDT_WDDIS); + reg | wdt->wddis_mask); } if (wdt->sam9x60_support) { @@ -273,6 +264,9 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt")) wdt->sam9x60_support = true; + wdt->wddis_mask = wdt->sam9x60_support ? AT91_SAM9X60_WDDIS + : AT91_WDT_WDDIS; + watchdog_set_drvdata(wdd, wdt); regs = devm_platform_ioremap_resource(pdev, 0); @@ -306,8 +300,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, wdt_timeout, dev); reg = wdt_read(wdt, AT91_WDT_MR); - if (!(reg & AT91_WDT_WDDIS)) { - wdt->mr &= ~AT91_WDT_WDDIS; + if (!(reg & wdt->wddis_mask)) { + wdt->mr &= ~wdt->wddis_mask; set_bit(WDOG_HW_RUNNING, &wdd->status); } diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c index 76a58715f665..1d3300c98d43 100644 --- a/drivers/watchdog/sc1200wdt.c +++ b/drivers/watchdog/sc1200wdt.c @@ -339,8 +339,8 @@ static int __init sc1200wdt_probe(void) static const struct pnp_device_id scl200wdt_pnp_devices[] = { /* National Semiconductor PC87307/PC97307 watchdog component */ - {.id = "NSC0800", .driver_data = 0}, - {.id = ""}, + { .id = "NSC0800" }, + { } }; static int scl200wdt_pnp_probe(struct pnp_dev *dev, diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c deleted file mode 100644 index 005f62e4a4fb..000000000000 --- a/drivers/watchdog/sc520_wdt.c +++ /dev/null @@ -1,430 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * AMD Elan SC520 processor Watchdog Timer driver - * - * Based on acquirewdt.c by Alan Cox, - * and sbc60xxwdt.c by Jakob Oestergaard <jakob@unthought.net> - * - * The authors do NOT admit liability nor provide warranty for - * any of this software. This material is provided "AS-IS" in - * the hope that it may be useful for others. - * - * (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net> - * 9/27 - 2001 [Initial release] - * - * Additional fixes Alan Cox - * - Fixed formatting - * - Removed debug printks - * - Fixed SMP built kernel deadlock - * - Switched to private locks not lock_kernel - * - Used ioremap/writew/readw - * - Added NOWAYOUT support - * 4/12 - 2002 Changes by Rob Radez <rob@osinvestor.com> - * - Change comments - * - Eliminate fop_llseek - * - Change CONFIG_WATCHDOG_NOWAYOUT semantics - * - Add KERN_* tags to printks - * - fix possible wdt_is_open race - * - Report proper capabilities in watchdog_info - * - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT, - * GETTIMEOUT, SETOPTIONS} ioctls - * 09/8 - 2003 Changes by Wim Van Sebroeck <wim@iguana.be> - * - cleanup of trailing spaces - * - added extra printk's for startup problems - * - use module_param - * - made timeout (the emulated heartbeat) a module_param - * - made the keepalive ping an internal subroutine - * 3/27 - 2004 Changes by Sean Young <sean@mess.org> - * - set MMCR_BASE to 0xfffef000 - * - CBAR does not need to be read - * - removed debugging printks - * - * This WDT driver is different from most other Linux WDT - * drivers in that the driver will ping the watchdog by itself, - * because this particular WDT has a very short timeout (1.6 - * seconds) and it would be insane to count on any userspace - * daemon always getting scheduled within that time frame. - * - * This driver uses memory mapped IO, and spinlock. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/types.h> -#include <linux/timer.h> -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/fs.h> -#include <linux/ioport.h> -#include <linux/notifier.h> -#include <linux/reboot.h> -#include <linux/init.h> -#include <linux/jiffies.h> -#include <linux/io.h> -#include <linux/uaccess.h> - - -/* - * The AMD Elan SC520 timeout value is 492us times a power of 2 (0-7) - * - * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s - * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s - * - * We will program the SC520 watchdog for a timeout of 2.01s. - * If we reset the watchdog every ~250ms we should be safe. - */ - -#define WDT_INTERVAL (HZ/4+1) - -/* - * We must not require too good response from the userspace daemon. - * Here we require the userspace daemon to send us a heartbeat - * char to /dev/watchdog every 30 seconds. - */ - -#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ -/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ -static int timeout = WATCHDOG_TIMEOUT; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, - "Watchdog timeout in seconds. (1 <= timeout <= 3600, default=" - __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); - -static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, - "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -/* - * AMD Elan SC520 - Watchdog Timer Registers - */ -#define MMCR_BASE 0xfffef000 /* The default base address */ -#define OFFS_WDTMRCTL 0xCB0 /* Watchdog Timer Control Register */ - -/* WDT Control Register bit definitions */ -#define WDT_EXP_SEL_01 0x0001 /* [01] Time-out = 496 us (with 33 Mhz clk). */ -#define WDT_EXP_SEL_02 0x0002 /* [02] Time-out = 508 ms (with 33 Mhz clk). */ -#define WDT_EXP_SEL_03 0x0004 /* [03] Time-out = 1.02 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_04 0x0008 /* [04] Time-out = 2.03 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_05 0x0010 /* [05] Time-out = 4.07 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_06 0x0020 /* [06] Time-out = 8.13 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_07 0x0040 /* [07] Time-out = 16.27s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_08 0x0080 /* [08] Time-out = 32.54s (with 33 Mhz clk). */ -#define WDT_IRQ_FLG 0x1000 /* [12] Interrupt Request Flag */ -#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */ -#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */ - -static __u16 __iomem *wdtmrctl; - -static void wdt_timer_ping(struct timer_list *); -static DEFINE_TIMER(timer, wdt_timer_ping); -static unsigned long next_heartbeat; -static unsigned long wdt_is_open; -static char wdt_expect_close; -static DEFINE_SPINLOCK(wdt_spinlock); - -/* - * Whack the dog - */ - -static void wdt_timer_ping(struct timer_list *unused) -{ - /* If we got a heartbeat pulse within the WDT_US_INTERVAL - * we agree to ping the WDT - */ - if (time_before(jiffies, next_heartbeat)) { - /* Ping the WDT */ - spin_lock(&wdt_spinlock); - writew(0xAAAA, wdtmrctl); - writew(0x5555, wdtmrctl); - spin_unlock(&wdt_spinlock); - - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - } else - pr_warn("Heartbeat lost! Will not ping the watchdog\n"); -} - -/* - * Utility routines - */ - -static void wdt_config(int writeval) -{ - unsigned long flags; - - /* buy some time (ping) */ - spin_lock_irqsave(&wdt_spinlock, flags); - readw(wdtmrctl); /* ensure write synchronization */ - writew(0xAAAA, wdtmrctl); - writew(0x5555, wdtmrctl); - /* unlock WDT = make WDT configuration register writable one time */ - writew(0x3333, wdtmrctl); - writew(0xCCCC, wdtmrctl); - /* write WDT configuration register */ - writew(writeval, wdtmrctl); - spin_unlock_irqrestore(&wdt_spinlock, flags); -} - -static int wdt_startup(void) -{ - next_heartbeat = jiffies + (timeout * HZ); - - /* Start the timer */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - - /* Start the watchdog */ - wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04); - - pr_info("Watchdog timer is now enabled\n"); - return 0; -} - -static int wdt_turnoff(void) -{ - /* Stop the timer */ - timer_delete_sync(&timer); - - /* Stop the watchdog */ - wdt_config(0); - - pr_info("Watchdog timer is now disabled...\n"); - return 0; -} - -static int wdt_keepalive(void) -{ - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); - return 0; -} - -static int wdt_set_heartbeat(int t) -{ - if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ - return -EINVAL; - - timeout = t; - return 0; -} - -/* - * /dev/watchdog handling - */ - -static ssize_t fop_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (count) { - if (!nowayout) { - size_t ofs; - - /* note: just in case someone wrote the magic character - * five months ago... */ - wdt_expect_close = 0; - - /* now scan */ - for (ofs = 0; ofs != count; ofs++) { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == 'V') - wdt_expect_close = 42; - } - } - - /* Well, anyhow someone wrote to us, we should - return that favour */ - wdt_keepalive(); - } - return count; -} - -static int fop_open(struct inode *inode, struct file *file) -{ - /* Just in case we're already talking to someone... */ - if (test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - if (nowayout) - __module_get(THIS_MODULE); - - /* Good, fire up the show */ - wdt_startup(); - return stream_open(inode, file); -} - -static int fop_close(struct inode *inode, struct file *file) -{ - if (wdt_expect_close == 42) - wdt_turnoff(); - else { - pr_crit("Unexpected close, not stopping watchdog!\n"); - wdt_keepalive(); - } - clear_bit(0, &wdt_is_open); - wdt_expect_close = 0; - return 0; -} - -static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static const struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT - | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "SC520", - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if (get_user(new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - wdt_turnoff(); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - wdt_startup(); - retval = 0; - } - - return retval; - } - case WDIOC_KEEPALIVE: - wdt_keepalive(); - return 0; - case WDIOC_SETTIMEOUT: - { - int new_timeout; - - if (get_user(new_timeout, p)) - return -EFAULT; - - if (wdt_set_heartbeat(new_timeout)) - return -EINVAL; - - wdt_keepalive(); - } - fallthrough; - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - default: - return -ENOTTY; - } -} - -static const struct file_operations wdt_fops = { - .owner = THIS_MODULE, - .write = fop_write, - .open = fop_open, - .release = fop_close, - .unlocked_ioctl = fop_ioctl, - .compat_ioctl = compat_ptr_ioctl, -}; - -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - -/* - * Notifier for system down - */ - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - wdt_turnoff(); - return NOTIFY_DONE; -} - -/* - * The WDT needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wdt_notifier = { - .notifier_call = wdt_notify_sys, -}; - -static void __exit sc520_wdt_unload(void) -{ - if (!nowayout) - wdt_turnoff(); - - /* Deregister */ - misc_deregister(&wdt_miscdev); - unregister_reboot_notifier(&wdt_notifier); - iounmap(wdtmrctl); -} - -static int __init sc520_wdt_init(void) -{ - int rc = -EBUSY; - - /* Check that the timeout value is within it's range ; - if not reset to the default */ - if (wdt_set_heartbeat(timeout)) { - wdt_set_heartbeat(WATCHDOG_TIMEOUT); - pr_info("timeout value must be 1 <= timeout <= 3600, using %d\n", - WATCHDOG_TIMEOUT); - } - - wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2); - if (!wdtmrctl) { - pr_err("Unable to remap memory\n"); - rc = -ENOMEM; - goto err_out_region2; - } - - rc = register_reboot_notifier(&wdt_notifier); - if (rc) { - pr_err("cannot register reboot notifier (err=%d)\n", rc); - goto err_out_ioremap; - } - - rc = misc_register(&wdt_miscdev); - if (rc) { - pr_err("cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, rc); - goto err_out_notifier; - } - - pr_info("WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - - return 0; - -err_out_notifier: - unregister_reboot_notifier(&wdt_notifier); -err_out_ioremap: - iounmap(wdtmrctl); -err_out_region2: - return rc; -} - -module_init(sc520_wdt_init); -module_exit(sc520_wdt_unload); - -MODULE_AUTHOR("Scott and Bill Jennings"); -MODULE_DESCRIPTION( - "Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor"); -MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index 3011e1af00f9..9f2c2ab4e2f1 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -36,7 +36,7 @@ * mknod /dev/watchdog c 10 130 * * For an example userspace keep-alive daemon, see: - * Documentation/watchdog/wdt.rst + * samples/watchdog/watchdog-simple.c */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 2bd3dc25cb03..7e99c3b1f367 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -92,7 +92,8 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && dev->revision < 0x40) { return sp5100; - } else if (dev->vendor == PCI_VENDOR_ID_AMD && + } else if ((dev->vendor == PCI_VENDOR_ID_AMD || + dev->vendor == PCI_VENDOR_ID_HYGON) && sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) { return efch_mmio; diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index 4e689b6ff141..aacf04616fef 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -320,10 +320,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&wdt->wdd, 0, dev); ret = devm_watchdog_register_device(dev, &wdt->wdd); - if (ret) { - sprd_wdt_disable(wdt); + if (ret) return ret; - } + platform_set_drvdata(pdev, wdt); return 0; diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 8300520688d0..f4097aefaf49 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -55,9 +55,9 @@ MODULE_PARM_DESC(stop_on_reboot, "Stop watchdogs on reboot (0=keep watching, 1=s * for example when it's impossible to disable it. To do so, * raising the initcall level of the watchdog driver is a solution. * But in such case, the miscdev is maybe not ready (subsys_initcall), and - * watchdog_core need miscdev to register the watchdog as a char device. + * watchdog_core needs miscdev to register the watchdog as a char device. * - * The deferred registration infrastructure offer a way for the watchdog + * The deferred registration infrastructure offers a way for the watchdog * subsystem to register a watchdog properly, even before miscdev is ready. */ @@ -222,11 +222,11 @@ static int watchdog_pm_notifier(struct notifier_block *nb, unsigned long mode, * watchdog_set_restart_priority - Change priority of restart handler * @wdd: watchdog device * @priority: priority of the restart handler, should follow these guidelines: - * 0: use watchdog's restart function as last resort, has limited restart - * capabilies - * 128: default restart handler, use if no other handler is expected to be + * * 0: use watchdog's restart function as last resort, has limited restart + * capabilities + * * 128: default restart handler, use if no other handler is expected to be * available and/or if restart is sufficient to restart the entire system - * 255: preempt all other handlers + * * 255: preempt all other handlers * * If a wdd->ops->restart function is provided when watchdog_register_device is * called, it will be registered as a restart handler with the priority given @@ -391,6 +391,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) unregister_reboot_notifier(&wdd->reboot_nb); + if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) + unregister_pm_notifier(&wdd->pm_nb); + watchdog_dev_unregister(wdd); ida_free(&watchdog_ida, wdd->id); } diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 834f65f4b59a..d7895009a2de 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -176,7 +176,7 @@ static int __watchdog_ping(struct watchdog_device *wdd) return err; } -/* +/** * watchdog_ping - ping the watchdog * @wdd: The watchdog device to ping * @@ -236,7 +236,7 @@ static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer) return HRTIMER_NORESTART; } -/* +/** * watchdog_start - wrapper to start the watchdog * @wdd: The watchdog device to start * @@ -279,7 +279,7 @@ static int watchdog_start(struct watchdog_device *wdd) return err; } -/* +/** * watchdog_stop - wrapper to stop the watchdog * @wdd: The watchdog device to stop * @@ -319,7 +319,7 @@ static int watchdog_stop(struct watchdog_device *wdd) return err; } -/* +/** * watchdog_get_status - wrapper to get the watchdog status * @wdd: The watchdog device to get the status from * @@ -356,7 +356,7 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd) return status; } -/* +/** * watchdog_set_timeout - set the watchdog timer timeout * @wdd: The watchdog device to set the timeout for * @timeout: Timeout to set in seconds @@ -391,7 +391,7 @@ static int watchdog_set_timeout(struct watchdog_device *wdd, return err; } -/* +/** * watchdog_set_pretimeout - set the watchdog timer pretimeout * @wdd: The watchdog device to set the timeout for * @timeout: pretimeout to set in seconds @@ -417,7 +417,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd, return err; } -/* +/** * watchdog_get_timeleft - wrapper to get the time left before a reboot * @wdd: The watchdog device to get the remaining time from * @timeleft: The time that's left @@ -659,7 +659,7 @@ __ATTRIBUTE_GROUPS(wdt); #define wdt_groups NULL #endif -/* +/** * watchdog_ioctl_op - call the watchdog drivers ioctl op if defined * @wdd: The watchdog device to do the ioctl on * @cmd: Watchdog command @@ -678,7 +678,7 @@ static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, return wdd->ops->ioctl(wdd, cmd, arg); } -/* +/** * watchdog_write - writes to the watchdog * @file: File from VFS * @data: User address of data @@ -732,7 +732,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, return len; } -/* +/** * watchdog_ioctl - handle the different ioctl's for the watchdog device * @file: File handle to the device * @cmd: Watchdog command @@ -845,7 +845,7 @@ out_ioctl: return err; } -/* +/** * watchdog_open - open the /dev/watchdog* devices * @inode: Inode of device * @file: File handle to device @@ -923,7 +923,7 @@ static void watchdog_core_data_release(struct device *dev) kfree(wd_data); } -/* +/** * watchdog_release - release the watchdog device * @inode: Inode of device * @file: File handle to device @@ -932,7 +932,7 @@ static void watchdog_core_data_release(struct device *dev) * stop the watchdog when we have received the magic char (and nowayout * was not set), else the watchdog will keep running. * - * Always returns 0. + * Returns: Always 0. */ static int watchdog_release(struct inode *inode, struct file *file) { @@ -1004,7 +1004,7 @@ static const struct class watchdog_class = { .dev_groups = wdt_groups, }; -/* +/** * watchdog_cdev_register - register watchdog character device * @wdd: Watchdog device * @@ -1105,7 +1105,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) return 0; } -/* +/** * watchdog_cdev_unregister - unregister watchdog character device * @wdd: Watchdog device * diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index 5c6e3fa001d8..f3bb935c08c2 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -718,7 +718,7 @@ static void ziirave_wdt_remove(struct i2c_client *client) } static const struct i2c_device_id ziirave_wdt_id[] = { - { "rave-wdt" }, + { .name = "rave-wdt" }, { } }; MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index bc9a41662efc..6ea945508a89 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -40,6 +40,7 @@ #include <linux/ktime.h> #ifdef CONFIG_X86 +#include <asm/cpuid/api.h> #include <asm/desc.h> #include <asm/ptrace.h> #include <asm/idtentry.h> diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index a6abf1ccd54c..35f879dc5dfb 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -59,6 +59,7 @@ #include <xen/swiotlb-xen.h> #include <xen/balloon.h> #ifdef CONFIG_X86 +#include <asm/cpuid/api.h> #include <asm/xen/cpuid.h> #endif #include <xen/mem-reservation.h> diff --git a/drivers/xen/mcelog.c b/drivers/xen/mcelog.c index 53a8720f5cae..32ab419bb503 100644 --- a/drivers/xen/mcelog.c +++ b/drivers/xen/mcelog.c @@ -54,8 +54,8 @@ #include <asm/xen/hypervisor.h> static struct mc_info g_mi; -static struct mcinfo_logical_cpu *g_physinfo; -static uint32_t ncpus; +static struct mcinfo_logical_cpu *g_physinfo __ro_after_init; +static uint32_t ncpus __ro_after_init; static DEFINE_MUTEX(mcelog_lock); @@ -182,7 +182,7 @@ static const struct file_operations xen_mce_chrdev_ops = { .unlocked_ioctl = xen_mce_chrdev_ioctl, }; -static struct miscdevice xen_mce_chrdev_device = { +static struct miscdevice xen_mce_chrdev_device __ro_after_init = { MISC_MCELOG_MINOR, "mcelog", &xen_mce_chrdev_ops, diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c index 1db82da56db6..f2438232518c 100644 --- a/drivers/xen/platform-pci.c +++ b/drivers/xen/platform-pci.c @@ -174,11 +174,9 @@ pci_out: } static const struct pci_device_id platform_pci_tbl[] = { - {PCI_VENDOR_ID_XEN, PCI_DEVICE_ID_XEN_PLATFORM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_XEN, PCI_DEVICE_ID_XEN_PLATFORM_XS61, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0,} + { PCI_VDEVICE(XEN, PCI_DEVICE_ID_XEN_PLATFORM) }, + { PCI_VDEVICE(XEN, PCI_DEVICE_ID_XEN_PLATFORM_XS61) }, + { } }; static const struct dev_pm_ops platform_pm_ops = { diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index b293d7652f15..67b0e2dbe84a 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -138,7 +138,7 @@ EXPORT_SYMBOL_GPL(xen_balloon_init); struct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, format, ##args); \ + return sysfs_emit(buf, format, ##args); \ } \ static DEVICE_ATTR_RO(name) @@ -155,7 +155,7 @@ static DEVICE_BOOL_ATTR(scrub_pages, 0644, xen_scrub_pages); static ssize_t target_kb_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages)); + return sysfs_emit(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages)); } static ssize_t target_kb_store(struct device *dev, @@ -180,7 +180,7 @@ static DEVICE_ATTR_RW(target_kb); static ssize_t target_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%llu\n", + return sysfs_emit(buf, "%llu\n", (unsigned long long)balloon_stats.target_pages << PAGE_SHIFT); } diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index eb260eceb4d2..fafb2b84fa5c 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -514,7 +514,7 @@ int xenbus_probe_node(struct xen_bus_type *bus, char devname[XEN_BUS_ID_SIZE]; int err; struct xenbus_device *xendev; - size_t stringlen; + size_t name_len, type_len; char *tmpstring; enum xenbus_state state = xenbus_read_driver_state(NULL, nodename); @@ -525,8 +525,9 @@ int xenbus_probe_node(struct xen_bus_type *bus, return 0; } - stringlen = strlen(nodename) + 1 + strlen(type) + 1; - xendev = kzalloc(sizeof(*xendev) + stringlen, GFP_KERNEL); + name_len = strlen(nodename); + type_len = strlen(type); + xendev = kzalloc(sizeof(*xendev) + name_len + 1 + type_len + 1, GFP_KERNEL); if (!xendev) return -ENOMEM; @@ -535,11 +536,11 @@ int xenbus_probe_node(struct xen_bus_type *bus, /* Copy the strings into the extra space. */ tmpstring = (char *)(xendev + 1); - strcpy(tmpstring, nodename); + memcpy(tmpstring, nodename, name_len); xendev->nodename = tmpstring; - tmpstring += strlen(tmpstring) + 1; - strcpy(tmpstring, type); + tmpstring += name_len + 1; + memcpy(tmpstring, type, type_len); xendev->devicetype = tmpstring; init_completion(&xendev->down); diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 82b0a34ded70..c202e7c553a6 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -47,6 +47,9 @@ #include <linux/rwsem.h> #include <linux/mutex.h> #include <asm/xen/hypervisor.h> +#ifdef CONFIG_X86 +#include <asm/cpuid/api.h> +#endif #include <xen/xenbus.h> #include <xen/xen.h> #include "xenbus.h" diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index 4e967754d8ad..9989240fb76b 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -15,6 +15,7 @@ #include <linux/zorro.h> #include <linux/stat.h> #include <linux/string.h> +#include <linux/sysfs.h> #include <asm/byteorder.h> @@ -29,7 +30,7 @@ static ssize_t name##_show(struct device *dev, \ struct zorro_dev *z; \ \ z = to_zorro_dev(dev); \ - return sprintf(buf, format_string, z->field); \ + return sysfs_emit(buf, format_string, z->field); \ } \ static DEVICE_ATTR_RO(name); @@ -44,7 +45,7 @@ static ssize_t serial_show(struct device *dev, struct device_attribute *attr, struct zorro_dev *z; z = to_zorro_dev(dev); - return sprintf(buf, "0x%08x\n", be32_to_cpu(z->rom.er_SerialNumber)); + return sysfs_emit(buf, "0x%08x\n", be32_to_cpu(z->rom.er_SerialNumber)); } static DEVICE_ATTR_RO(serial); @@ -53,10 +54,10 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, { struct zorro_dev *z = to_zorro_dev(dev); - return sprintf(buf, "0x%08lx 0x%08lx 0x%08lx\n", - (unsigned long)zorro_resource_start(z), - (unsigned long)zorro_resource_end(z), - zorro_resource_flags(z)); + return sysfs_emit(buf, "0x%08lx 0x%08lx 0x%08lx\n", + (unsigned long)zorro_resource_start(z), + (unsigned long)zorro_resource_end(z), + zorro_resource_flags(z)); } static DEVICE_ATTR_RO(resource); @@ -65,7 +66,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, { struct zorro_dev *z = to_zorro_dev(dev); - return sprintf(buf, ZORRO_DEVICE_MODALIAS_FMT "\n", z->id); + return sysfs_emit(buf, ZORRO_DEVICE_MODALIAS_FMT "\n", z->id); } static DEVICE_ATTR_RO(modalias); |
