diff options
Diffstat (limited to 'drivers')
541 files changed, 19437 insertions, 3583 deletions
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index af7a9b2fd5bc..34412cd697d8 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -789,11 +789,9 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 id, return irq_find_matching_fwnode(handle, bus_token); } -struct fwnode_handle *iort_iwb_handle(u32 iwb_id) +acpi_handle iort_iwb_handle(u32 iwb_id) { - struct fwnode_handle *fwnode; struct acpi_iort_node *node; - struct acpi_device *device; struct acpi_iort_iwb *iwb; acpi_status status; acpi_handle handle; @@ -808,6 +806,19 @@ struct fwnode_handle *iort_iwb_handle(u32 iwb_id) if (ACPI_FAILURE(status)) return NULL; + return handle; +} + +struct fwnode_handle *iort_iwb_handle_fwnode(u32 iwb_id) +{ + struct fwnode_handle *fwnode; + struct acpi_device *device; + acpi_handle handle; + + handle = iort_iwb_handle(iwb_id); + if (!handle) + return NULL; + device = acpi_get_acpi_dev(handle); if (!device) return NULL; @@ -2090,6 +2101,11 @@ static void __init iort_init_platform_devices(void) } } +u32 arch_acpi_add_auto_dep(acpi_handle handle) +{ + return acpi_irq_add_auto_dep(handle); +} + void __init acpi_iort_init(void) { acpi_status status; diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c index d1595156c86a..e4293458bf61 100644 --- a/drivers/acpi/irq.c +++ b/drivers/acpi/irq.c @@ -13,6 +13,7 @@ enum acpi_irq_model_id acpi_irq_model; static acpi_gsi_domain_disp_fn acpi_get_gsi_domain_id; +static acpi_gsi_handle_disp_fn acpi_get_gsi_handle; static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi); /** @@ -321,15 +322,19 @@ const struct cpumask *acpi_irq_get_affinity(acpi_handle handle, /** * acpi_set_irq_model - Setup the GSI irqdomain information - * @model: the value assigned to acpi_irq_model - * @fn: a dispatcher function that will return the domain fwnode - * for a given GSI + * @model: the value assigned to acpi_irq_model + * @fn: a dispatcher function that will return the domain fwnode + * for a given GSI + * @gsi_dep_fn: a function to retrieve the acpi_handle a GSI interrupt is + * dependent on + * */ void __init acpi_set_irq_model(enum acpi_irq_model_id model, - acpi_gsi_domain_disp_fn fn) + acpi_gsi_domain_disp_fn fn, acpi_gsi_handle_disp_fn gsi_dep_fn) { acpi_irq_model = model; acpi_get_gsi_domain_id = fn; + acpi_get_gsi_handle = gsi_dep_fn; } /* @@ -385,3 +390,162 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, host_data); } EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy); + +struct acpi_irq_dep_ctx { + int rc; + unsigned int index; + acpi_handle handle; +}; + +static acpi_status acpi_irq_get_parent(struct acpi_resource *ares, void *context) +{ + struct acpi_irq_dep_ctx *ctx = context; + struct acpi_resource_irq *irq; + struct acpi_resource_extended_irq *eirq; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_IRQ: + irq = &ares->data.irq; + if (ctx->index >= irq->interrupt_count) { + ctx->index -= irq->interrupt_count; + return AE_OK; + } + ctx->handle = acpi_get_gsi_handle(irq->interrupts[ctx->index]); + ctx->rc = 0; + return AE_CTRL_TERMINATE; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + eirq = &ares->data.extended_irq; + if (eirq->producer_consumer == ACPI_PRODUCER) + return AE_OK; + + if (ctx->index >= eirq->interrupt_count) { + ctx->index -= eirq->interrupt_count; + return AE_OK; + } + + /* Support GSIs only */ + if (eirq->resource_source.string_length) + return AE_OK; + + ctx->handle = acpi_get_gsi_handle(eirq->interrupts[ctx->index]); + ctx->rc = 0; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +static int acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle) +{ + struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL}; + + if (!gsi_handle) + return -EINVAL; + + acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_get_parent, &ctx); + *gsi_handle = ctx.handle; + + return ctx.rc; +} + +static bool acpi_prt_entry_valid(void *prt_entry) +{ + struct acpi_pci_routing_table *entry = prt_entry; + + return entry && entry->length > 0; +} + +static void *acpi_prt_next_entry(void *prt_entry) +{ + struct acpi_pci_routing_table *entry = prt_entry; + + return prt_entry + entry->length; +} + +static u32 acpi_add_prt_dep(acpi_handle handle) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_pci_routing_table *entry; + struct acpi_handle_list dep_devices; + acpi_handle gsi_handle; + acpi_handle link_handle; + acpi_status status; + u32 count = 0; + + status = acpi_get_irq_routing_table(handle, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "failed to get IRQ routing table\n"); + kfree(buffer.pointer); + return 0; + } + + entry = buffer.pointer; + for (; acpi_prt_entry_valid(entry); entry = acpi_prt_next_entry(entry)) { + if (entry->source[0]) { + status = acpi_get_handle(handle, entry->source, &link_handle); + if (ACPI_FAILURE(status)) + continue; + dep_devices.count = 1; + dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + if (!dep_devices.handles) { + acpi_handle_err(handle, "failed to allocate memory\n"); + continue; + } + + dep_devices.handles[0] = link_handle; + count += acpi_scan_add_dep(handle, &dep_devices); + } else { + gsi_handle = acpi_get_gsi_handle(entry->source_index); + if (!gsi_handle) + continue; + dep_devices.count = 1; + dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + if (!dep_devices.handles) { + acpi_handle_err(handle, "failed to allocate memory\n"); + continue; + } + + dep_devices.handles[0] = gsi_handle; + count += acpi_scan_add_dep(handle, &dep_devices); + } + } + + kfree(buffer.pointer); + return count; +} + +static u32 acpi_add_irq_dep(acpi_handle handle) +{ + struct acpi_handle_list dep_devices; + acpi_handle gsi_handle; + u32 count = 0; + int i; + + for (i = 0; !acpi_irq_get_dep(handle, i, &gsi_handle); i++) { + if (!gsi_handle) + continue; + + dep_devices.count = 1; + dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + if (!dep_devices.handles) { + acpi_handle_err(handle, "failed to allocate memory\n"); + continue; + } + + dep_devices.handles[0] = gsi_handle; + count += acpi_scan_add_dep(handle, &dep_devices); + } + + return count; +} + +u32 acpi_irq_add_auto_dep(acpi_handle handle) +{ + if (!acpi_get_gsi_handle) + return 0; + + if (acpi_has_method(handle, "_PRT")) + return acpi_add_prt_dep(handle); + + return acpi_add_irq_dep(handle); +} diff --git a/drivers/acpi/riscv/irq.c b/drivers/acpi/riscv/irq.c index 9b88d0993e88..da2c42e0ebfd 100644 --- a/drivers/acpi/riscv/irq.c +++ b/drivers/acpi/riscv/irq.c @@ -23,12 +23,6 @@ struct riscv_ext_intc_list { struct list_head list; }; -struct acpi_irq_dep_ctx { - int rc; - unsigned int index; - acpi_handle handle; -}; - LIST_HEAD(ext_intc_list); static int irqchip_cmp_func(const void *in0, const void *in1) @@ -254,7 +248,7 @@ void __init riscv_acpi_init_gsi_mapping(void) acpi_get_devices("RSCV0006", riscv_acpi_create_gsi_map_smsi, NULL, NULL); } -static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi) +acpi_handle acpi_get_riscv_gsi_handle(u32 gsi) { struct riscv_ext_intc_list *ext_intc_element; struct list_head *i; @@ -269,138 +263,7 @@ static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi) return NULL; } -static acpi_status riscv_acpi_irq_get_parent(struct acpi_resource *ares, void *context) -{ - struct acpi_irq_dep_ctx *ctx = context; - struct acpi_resource_irq *irq; - struct acpi_resource_extended_irq *eirq; - - switch (ares->type) { - case ACPI_RESOURCE_TYPE_IRQ: - irq = &ares->data.irq; - if (ctx->index >= irq->interrupt_count) { - ctx->index -= irq->interrupt_count; - return AE_OK; - } - ctx->handle = riscv_acpi_get_gsi_handle(irq->interrupts[ctx->index]); - return AE_CTRL_TERMINATE; - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - eirq = &ares->data.extended_irq; - if (eirq->producer_consumer == ACPI_PRODUCER) - return AE_OK; - - if (ctx->index >= eirq->interrupt_count) { - ctx->index -= eirq->interrupt_count; - return AE_OK; - } - - /* Support GSIs only */ - if (eirq->resource_source.string_length) - return AE_OK; - - ctx->handle = riscv_acpi_get_gsi_handle(eirq->interrupts[ctx->index]); - return AE_CTRL_TERMINATE; - } - - return AE_OK; -} - -static int riscv_acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle) -{ - struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL}; - - if (!gsi_handle) - return 0; - - acpi_walk_resources(handle, METHOD_NAME__CRS, riscv_acpi_irq_get_parent, &ctx); - *gsi_handle = ctx.handle; - if (*gsi_handle) - return 1; - - return 0; -} - -static u32 riscv_acpi_add_prt_dep(acpi_handle handle) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_pci_routing_table *entry; - struct acpi_handle_list dep_devices; - acpi_handle gsi_handle; - acpi_handle link_handle; - acpi_status status; - u32 count = 0; - - status = acpi_get_irq_routing_table(handle, &buffer); - if (ACPI_FAILURE(status)) { - acpi_handle_err(handle, "failed to get IRQ routing table\n"); - kfree(buffer.pointer); - return 0; - } - - entry = buffer.pointer; - while (entry && (entry->length > 0)) { - if (entry->source[0]) { - acpi_get_handle(handle, entry->source, &link_handle); - dep_devices.count = 1; - dep_devices.handles = kzalloc_objs(*dep_devices.handles, - 1); - if (!dep_devices.handles) { - acpi_handle_err(handle, "failed to allocate memory\n"); - continue; - } - - dep_devices.handles[0] = link_handle; - count += acpi_scan_add_dep(handle, &dep_devices); - } else { - gsi_handle = riscv_acpi_get_gsi_handle(entry->source_index); - dep_devices.count = 1; - dep_devices.handles = kzalloc_objs(*dep_devices.handles, - 1); - if (!dep_devices.handles) { - acpi_handle_err(handle, "failed to allocate memory\n"); - continue; - } - - dep_devices.handles[0] = gsi_handle; - count += acpi_scan_add_dep(handle, &dep_devices); - } - - entry = (struct acpi_pci_routing_table *) - ((unsigned long)entry + entry->length); - } - - kfree(buffer.pointer); - return count; -} - -static u32 riscv_acpi_add_irq_dep(acpi_handle handle) -{ - struct acpi_handle_list dep_devices; - acpi_handle gsi_handle; - u32 count = 0; - int i; - - for (i = 0; - riscv_acpi_irq_get_dep(handle, i, &gsi_handle); - i++) { - dep_devices.count = 1; - dep_devices.handles = kzalloc_objs(*dep_devices.handles, 1); - if (!dep_devices.handles) { - acpi_handle_err(handle, "failed to allocate memory\n"); - continue; - } - - dep_devices.handles[0] = gsi_handle; - count += acpi_scan_add_dep(handle, &dep_devices); - } - - return count; -} - u32 arch_acpi_add_auto_dep(acpi_handle handle) { - if (acpi_has_method(handle, "_PRT")) - return riscv_acpi_add_prt_dep(handle); - - return riscv_acpi_add_irq_dep(handle); + return acpi_irq_add_auto_dep(handle); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8515e1892643..36c368324138 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -850,6 +850,7 @@ static const char * const acpi_ignore_dep_ids[] = { /* List of HIDs for which we honor deps of matching ACPI devs, when checking _DEP lists. */ static const char * const acpi_honor_dep_ids[] = { + "ARMH0003", /* ARM GICv5 IWB */ "INT3472", /* Camera sensor PMIC / clk and regulator info */ "INTC1059", /* IVSC (TGL) driver must be loaded to allow i2c access to camera sensors */ "INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */ @@ -858,6 +859,7 @@ static const char * const acpi_honor_dep_ids[] = { "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 */ + "INTC10FA", /* CVS (NVL) driver must be loaded to allow camera streaming */ "RSCV0001", /* RISC-V PLIC */ "RSCV0002", /* RISC-V APLIC */ "RSCV0005", /* RISC-V SBI MPXY MBOX */ diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index eb93c060426f..4286e4af1092 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -22,7 +22,6 @@ #include <linux/initrd.h> #include <linux/security.h> #include <linux/kmemleak.h> -#include <asm/fixmap.h> #include "internal.h" #ifdef CONFIG_ACPI_CUSTOM_DSDT diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 335288e8b5b3..fab38bc98113 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -469,9 +469,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) if (retval == -EACCES) retval = -EAGAIN; - if (retval != -EAGAIN && retval != -EBUSY) - dev->power.runtime_error = retval; - return retval; } @@ -751,6 +748,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); + if (retval != -EAGAIN && retval != -EBUSY) + dev->power.runtime_error = retval; + /* * On transient errors, if the callback routine failed an autosuspend, * and if the last_busy time has been updated so that there is a new diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index f765970578f9..8dad7bf5f664 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -2080,6 +2080,15 @@ static int blkfront_resume(struct xenbus_device *dev) continue; /* + * For requests split across multiple slots, process the + * underlying request only once: skip the linked, sg-less + * secondary slot. + */ + if (shadow[j].associated_id != NO_ASSOCIATED_ID && + shadow[j].num_sg == 0) + continue; + + /* * Get the bios in the request so we can re-queue them. */ if (req_op(shadow[j].request) == REQ_OP_FLUSH || diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 2b7231be5973..7a87549f587d 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -1446,72 +1446,134 @@ exit_on_error: return err; } +/* Queue a coredump dump_traces() pass. + * + * Returns true if a new coredump was queued, false if one was already + * in-flight (the BTINTEL_PCIE_COREDUMP_INPROGRESS bit serves as the + * single-writer guard for the @coredump_work item) or the workqueue is + * disabled (reset / remove in progress). + * + * Always queue this AFTER any companion event-reader work (hwexp / + * fwtrigger) so that, on the ordered @dump_workqueue, the event reader + * runs first and populates dmp_hdr.event_type / event_id before + * dump_traces consumes them. + */ +static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data, + u16 trigger_reason) +{ + if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) + return false; + + data->dmp_hdr.trigger_reason = trigger_reason; + + if (queue_work(data->dump_workqueue, &data->coredump_work)) + return true; + + /* Workqueue is disabled (reset/remove drained it). Release the + * guard so a later trigger, after re-probe, can succeed. + */ + clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); + return false; +} + static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data) { bt_dev_dbg(data->hdev, "Received firmware smart trigger cause"); - if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) + /* Per-work guard: deduplicate concurrent FW-trigger interrupts. + * Cleared at the tail of btintel_pcie_fwtrigger_worker(). + */ + if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, + &data->flags)) return; - /* Trigger device core dump when there is FW assert */ - if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) - data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; + if (!queue_work(data->dump_workqueue, &data->fwtrigger_work)) { + clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); + return; + } - queue_work(data->coredump_workqueue, &data->coredump_work); + /* Queue coredump after the fwtrigger event reader so dmp_hdr.event_* + * is populated before dump_traces consumes it. + */ + btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT); } static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data) { bt_dev_err(data->hdev, "Received hw exception interrupt"); + /* CORE_HALTED is the single-writer guard for this handler. It is + * set once on first HW exception and cleared only by re-probe + * (data is reallocated), so it also serializes hwexp_work + * scheduling without needing a separate bit. + */ if (test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags)) return; - if (test_and_set_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) - return; - - /* Trigger device core dump when there is HW exception */ - if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) - data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; + /* Queue companion coredump first so it is appended after hwexp_work + * on the ordered @dump_workqueue (preserves the original + * coredump-then-hwexp ordering). + */ + btintel_pcie_queue_coredump(data, BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT); - queue_work(data->coredump_workqueue, &data->coredump_work); + queue_work(data->dump_workqueue, &data->hwexp_work); } static void btintel_pcie_coredump_worker(struct work_struct *work) { struct btintel_pcie_data *data = container_of(work, struct btintel_pcie_data, coredump_work); - int err; /* hdev is NULL until setup_hdev() succeeds, and is cleared on * teardown after disable_work_sync() drains us; bail in that case. */ if (!data->hdev) + goto out; + + btintel_pcie_dump_traces(data->hdev); +out: + /* Release guard last so a new trigger can run only after this + * pass has fully completed (including dev_coredumpv()). + */ + clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); +} + +static void btintel_pcie_hwexp_worker(struct work_struct *work) +{ + struct btintel_pcie_data *data = container_of(work, + struct btintel_pcie_data, hwexp_work); + + if (!data->hdev) return; - if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) { - err = btintel_pcie_dump_fwtrigger_event(data); - if (err) - bt_dev_warn(data->hdev, "failed to log fwtrigger event"); - clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); - } + /* Unlike usb products, controller will not send hardware exception + * event on exception. Instead controller writes the hardware event + * to device memory along with optional debug events, raises MSIX + * and halts. Driver shall read the exception event from device + * memory and passes it to the stack for further processing. + * + * Re-entry is gated by BTINTEL_PCIE_CORE_HALTED in the IRQ + * handler, which is only cleared by re-probe; no per-work bit + * is needed here. + */ + btintel_pcie_read_hwexp(data); +} - if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) { - btintel_pcie_dump_traces(data->hdev); - clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); - } +static void btintel_pcie_fwtrigger_worker(struct work_struct *work) +{ + struct btintel_pcie_data *data = container_of(work, + struct btintel_pcie_data, fwtrigger_work); + int err; - if (test_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) { - /* Unlike usb products, controller will not send hardware - * exception event on exception. Instead controller writes the - * hardware event to device memory along with optional debug - * events, raises MSIX and halts. Driver shall read the - * exception event from device memory and passes it stack for - * further processing. - */ - btintel_pcie_read_hwexp(data); - clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags); - } + if (!data->hdev) + goto out; + + err = btintel_pcie_dump_fwtrigger_event(data); + if (err) + bt_dev_warn(data->hdev, "failed to log fwtrigger event"); +out: + /* Release guard last; matches set in fw_trigger handler. */ + clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); } static void btintel_pcie_rx_work(struct work_struct *work) @@ -2650,20 +2712,22 @@ static void btintel_pcie_reset_work(struct work_struct *wk) btintel_pcie_synchronize_irqs(data); flush_work(&data->rx_work); - /* Drain any in-flight coredump and block new ones across reset. - * Safe from self-deadlock: coredump_work runs on a separate wq. + /* Drain any in-flight dump workers and block new ones across reset. + * Safe from self-deadlock: they all run on a separate wq. */ disable_work_sync(&data->coredump_work); + disable_work_sync(&data->hwexp_work); + disable_work_sync(&data->fwtrigger_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); /* Both reset paths follow the same contract: on success they * destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs - * the coredump_work with disable count 0), so enable_work() must + * the dump workers with disable count 0), so enable_work() must * NOT be called on the success path. Only the FLR path can fail * with 'data' still alive, in which case we balance the - * disable_work_sync() above so a later successful reset is not - * permanently blocked. + * disable_work_sync() calls above so a later successful reset is + * not permanently blocked. * * pci_lock_rescan_remove() (held above) serializes against PCI * device addition/removal (hotplug), so no device can be added to @@ -2869,8 +2933,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev, if (!data->workqueue) return -ENOMEM; - data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0); - if (!data->coredump_workqueue) { + data->dump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0); + if (!data->dump_workqueue) { destroy_workqueue(data->workqueue); return -ENOMEM; } @@ -2879,6 +2943,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev, INIT_WORK(&data->rx_work, btintel_pcie_rx_work); INIT_WORK(&data->reset_work, btintel_pcie_reset_work); INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker); + INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker); + INIT_WORK(&data->fwtrigger_work, btintel_pcie_fwtrigger_worker); data->boot_stage_cache = 0x00; data->img_resp_cache = 0x00; @@ -2921,7 +2987,7 @@ exit_error: /* reset device before exit */ btintel_pcie_reset_bt(data); - destroy_workqueue(data->coredump_workqueue); + destroy_workqueue(data->dump_workqueue); pci_clear_master(pdev); @@ -2940,12 +3006,14 @@ static void btintel_pcie_remove(struct pci_dev *pdev) return; } - /* Permanently block coredump triggers and drain the worker before - * tearing down. Must run before cancel_work_sync(&reset_work) so - * the disable counter stays >= 1 even after reset_work()'s + /* Permanently block all dump triggers and drain the workers before + * tearing down. Must run before disable_work_sync(&reset_work) so + * the disable counters stay >= 1 even after reset_work()'s * balanced enable_work() (counter 2 -> 1, never reaching 0). */ disable_work_sync(&data->coredump_work); + disable_work_sync(&data->hwexp_work); + disable_work_sync(&data->fwtrigger_work); /* Cancel pending reset work. Skip only when remove() is called from * within the reset work itself (PLDR device_reprobe path) to avoid @@ -2973,7 +3041,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev) btintel_pcie_release_hdev(data); - destroy_workqueue(data->coredump_workqueue); + destroy_workqueue(data->dump_workqueue); destroy_workqueue(data->workqueue); btintel_pcie_free(data); @@ -2992,16 +3060,8 @@ static void btintel_pcie_coredump(struct device *dev) if (!data) return; - if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) - return; - - data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER; - /* queue_work() returns false if the work is disabled (reset or - * remove in progress); clear the in-progress bit so a later - * trigger can succeed once the work is re-enabled. - */ - if (!queue_work(data->coredump_workqueue, &data->coredump_work)) - clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); + btintel_pcie_queue_coredump(data, + BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER); } #endif @@ -3138,12 +3198,8 @@ static int btintel_pcie_resume(struct device *dev) if (btintel_pcie_in_error(data) || btintel_pcie_in_device_halt(data)) { bt_dev_err(data->hdev, "Controller in error state for D0 entry"); - if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, - &data->flags)) { - data->dmp_hdr.trigger_reason = - BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - queue_work(data->coredump_workqueue, &data->coredump_work); - } + btintel_pcie_queue_coredump(data, + BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT); set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); btintel_pcie_reset(data->hdev); } diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 7caee093e316..749369b24031 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -118,7 +118,6 @@ enum { enum { BTINTEL_PCIE_CORE_HALTED, - BTINTEL_PCIE_HWEXP_INPROGRESS, BTINTEL_PCIE_COREDUMP_INPROGRESS, BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, BTINTEL_PCIE_RECOVERY_IN_PROGRESS, @@ -466,8 +465,11 @@ struct btintel_pcie_dump_header { * @workqueue: workqueue for RX work * @rx_skb_q: SKB queue for RX packet * @rx_work: RX work struct to process the RX packet in @rx_skb_q - * @coredump_workqueue: dedicated workqueue for coredump collection - * @coredump_work: work struct for coredump trace collection + * @dump_workqueue: dedicated ordered workqueue serializing the coredump, + * hardware exception, and firmware-trigger dump workers + * @coredump_work: work struct for DRAM trace coredump collection + * @hwexp_work: work struct for hardware exception event read + * @fwtrigger_work: work struct for firmware-triggered diagnostic event read * @dma_pool: DMA pool for descriptors, index array and ci * @dma_p_addr: DMA address for pool * @dma_v_addr: address of pool @@ -516,8 +518,10 @@ struct btintel_pcie_data { struct work_struct rx_work; struct work_struct reset_work; - struct workqueue_struct *coredump_workqueue; + struct workqueue_struct *dump_workqueue; struct work_struct coredump_work; + struct work_struct hwexp_work; + struct work_struct fwtrigger_work; struct dma_pool *dma_pool; dma_addr_t dma_p_addr; diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index b1213786f72c..21bc2c50170f 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1340,14 +1340,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id) ret = dev_set_name(&mhi_dev->dev, "%s_%s", dev_name(&mhi_cntrl->mhi_dev->dev), mhi_dev->name); - if (ret) { - put_device(&mhi_dev->dev); - return ret; - } + if (ret) + goto err_put_channels; ret = device_add(&mhi_dev->dev); if (ret) - put_device(&mhi_dev->dev); + goto err_put_channels; + + return 0; + +err_put_channels: + put_device(&mhi_dev->dev); /* DL channel reference */ + put_device(&mhi_dev->dev); /* UL channel reference */ + put_device(&mhi_dev->dev); /* device_initialize() reference */ return ret; } diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 53c0ffe30070..4d458396233a 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -170,6 +170,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state); void mhi_soc_reset(struct mhi_controller *mhi_cntrl) { + int __maybe_unused ret; + u32 tmp; + if (mhi_cntrl->reset) { mhi_cntrl->reset(mhi_cntrl); return; @@ -178,6 +181,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl) /* Generic MHI SoC reset */ mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, MHI_SOC_RESET_REQ); + /* Flush the posted write to the device (ignore return value) */ + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET, + &tmp); } EXPORT_SYMBOL_GPL(mhi_soc_reset); diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index f799503c8f36..9d29f1591a45 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -651,21 +651,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) /* Trigger MHI RESET so that the device will not access host memory */ if (reset_device) { - u32 in_reset = -1; - unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms); - dev_dbg(dev, "Triggering MHI Reset in device\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); /* Wait for the reset bit to be cleared by the device */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_read_reg_field(mhi_cntrl, - mhi_cntrl->regs, - MHICTRL, - MHICTRL_RESET_MASK, - &in_reset) || - !in_reset, timeout); - if (!ret || in_reset) { + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, + MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms); + if (ret) { dev_err(dev, "Device failed to exit MHI Reset state\n"); write_lock_irq(&mhi_cntrl->pm_lock); cur_state = mhi_tryset_pm_state(mhi_cntrl, diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c index be8166565e7c..ab00c75b9e95 100644 --- a/drivers/bus/qcom-ebi2.c +++ b/drivers/bus/qcom-ebi2.c @@ -353,7 +353,7 @@ static int qcom_ebi2_probe(struct platform_device *pdev) /* Figure out the chipselect */ ret = of_property_read_u32(child, "reg", &csindex); if (ret) - return ret; + goto err_disable_clk; if (csindex > 5) { dev_err(dev, @@ -372,8 +372,12 @@ static int qcom_ebi2_probe(struct platform_device *pdev) have_children = true; } - if (have_children) - return of_platform_default_populate(np, NULL, dev); + if (have_children) { + ret = of_platform_default_populate(np, NULL, dev); + if (ret) + goto err_disable_clk; + } + return 0; err_disable_clk: diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 9309cfb935be..9a88d287fa09 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1125,8 +1125,6 @@ static int sonypi_acpi_probe(struct platform_device *pdev) return -ENODEV; sonypi_acpi_device = device; - strscpy(acpi_device_name(device), "Sony laptop hotkeys"); - strscpy(acpi_device_class(device), "sony/hotkey"); return 0; } diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 6fd4aac1b6cf..00880d676296 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -166,6 +166,16 @@ config CLK_NORD_GCC SPI, I2C, USB, SD/UFS, PCIe etc. The clock controller is a combination of GCC, SE_GCC, NE_GCC and NW_GCC. +config CLK_SHIKRA_GCC + tristate "Shikra Global Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCOM_GDSC + default ARCH_QCOM + help + Support for the global clock controller on Shikra devices. + Say Y if you want to use multimedia devices or peripheral + devices such as Camera, Video, UART, SPI, I2C, USB, SD/eMMC etc. + config CLK_X1E80100_CAMCC tristate "X1E80100 Camera Clock Controller" depends on ARM64 || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 81237e2c8db3..76946206d4aa 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_CLK_KAANAPALI_TCSRCC) += tcsrcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_VIDEOCC) += videocc-kaanapali.o obj-$(CONFIG_CLK_NORD_GCC) += gcc-nord.o negcc-nord.o nwgcc-nord.o segcc-nord.o obj-$(CONFIG_CLK_NORD_TCSRCC) += tcsrcc-nord.o +obj-$(CONFIG_CLK_SHIKRA_GCC) += gcc-shikra.o obj-$(CONFIG_CLK_X1E80100_CAMCC) += camcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_DISPCC) += dispcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_GCC) += gcc-x1e80100.o diff --git a/drivers/clk/qcom/clk-regmap-phy-mux.c b/drivers/clk/qcom/clk-regmap-phy-mux.c index b7d1c69d62f7..7b7243b7107d 100644 --- a/drivers/clk/qcom/clk-regmap-phy-mux.c +++ b/drivers/clk/qcom/clk-regmap-phy-mux.c @@ -15,66 +15,48 @@ #define PHY_MUX_PHY_SRC 0 #define PHY_MUX_REF_SRC 2 -#define XO_RATE 19200000UL - static inline struct clk_regmap_phy_mux *to_clk_regmap_phy_mux(struct clk_regmap *clkr) { return container_of(clkr, struct clk_regmap_phy_mux, clkr); } -static unsigned long phy_mux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +static int phy_mux_is_enabled(struct clk_hw *hw) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - u32 val; + unsigned int val; regmap_read(clkr->regmap, phy_mux->reg, &val); + val = FIELD_GET(PHY_MUX_MASK, val); + + WARN_ON(val != PHY_MUX_PHY_SRC && val != PHY_MUX_REF_SRC); - switch (FIELD_GET(PHY_MUX_MASK, val)) { - case PHY_MUX_PHY_SRC: - return ULONG_MAX; - case PHY_MUX_REF_SRC: - return XO_RATE; - default: - return 0; - } + return val == PHY_MUX_PHY_SRC; } -static int phy_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +static int phy_mux_enable(struct clk_hw *hw) { - if (req->rate == XO_RATE || req->rate == ULONG_MAX) - return 0; + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - return -EINVAL; + return regmap_update_bits(clkr->regmap, phy_mux->reg, + PHY_MUX_MASK, + FIELD_PREP(PHY_MUX_MASK, PHY_MUX_PHY_SRC)); } -static int phy_mux_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) +static void phy_mux_disable(struct clk_hw *hw) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - u32 val; - - switch (rate) { - case XO_RATE: - val = PHY_MUX_REF_SRC; - break; - case ULONG_MAX: - val = PHY_MUX_PHY_SRC; - break; - default: - return -EINVAL; - } regmap_update_bits(clkr->regmap, phy_mux->reg, PHY_MUX_MASK, - FIELD_PREP(PHY_MUX_MASK, val)); - - return 0; + FIELD_PREP(PHY_MUX_MASK, PHY_MUX_REF_SRC)); } const struct clk_ops clk_regmap_phy_mux_ops = { - .recalc_rate = phy_mux_recalc_rate, - .determine_rate = phy_mux_determine_rate, - .set_rate = phy_mux_set_rate, + .enable = phy_mux_enable, + .disable = phy_mux_disable, + .is_enabled = phy_mux_is_enabled, }; EXPORT_SYMBOL_GPL(clk_regmap_phy_mux_ops); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 103db984a40b..0b624ed4715c 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -495,6 +495,7 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(rf_clk2, 5, 19200000); DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(rf_clk3, 6, 19200000); DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(ln_bb_clk, 8, 19200000); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PREFIX(38m4_, rf_clk2, 5, 38400000); DEFINE_CLK_SMD_RPM_XO_BUFFER_PREFIX(38m4_, rf_clk3, 6, 38400000); DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(cxo_d0, 1, 19200000); @@ -1262,6 +1263,10 @@ static struct clk_smd_rpm *qcm2290_clks[] = { [RPM_SMD_QDSS_A_CLK] = &clk_smd_rpm_branch_qdss_a_clk, [RPM_SMD_LN_BB_CLK2] = &clk_smd_rpm_ln_bb_clk2, [RPM_SMD_LN_BB_CLK2_A] = &clk_smd_rpm_ln_bb_clk2_a, + [RPM_SMD_RF_CLK1] = &clk_smd_rpm_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &clk_smd_rpm_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &clk_smd_rpm_38m4_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &clk_smd_rpm_38m4_rf_clk2_a, [RPM_SMD_RF_CLK3] = &clk_smd_rpm_38m4_rf_clk3, [RPM_SMD_RF_CLK3_A] = &clk_smd_rpm_38m4_rf_clk3_a, [RPM_SMD_IPA_CLK] = &clk_smd_rpm_ipa_clk, diff --git a/drivers/clk/qcom/gcc-ipq5424.c b/drivers/clk/qcom/gcc-ipq5424.c index 35af6ffeeb85..3ddd4b3aedea 100644 --- a/drivers/clk/qcom/gcc-ipq5424.c +++ b/drivers/clk/qcom/gcc-ipq5424.c @@ -139,17 +139,6 @@ static struct clk_alpha_pll gpll4 = { .parent_data = &gcc_parent_data_xo, .num_parents = 1, .ops = &clk_alpha_pll_ops, - /* - * There are no consumers for this GPLL in kernel yet, - * (will be added soon), so the clock framework - * disables this source. But some of the clocks - * initialized by boot loaders uses this source. So we - * need to keep this clock ON. Add the - * CLK_IGNORE_UNUSED flag so the clock will not be - * disabled. Once the consumer in kernel is added, we - * can get rid of this flag. - */ - .flags = CLK_IGNORE_UNUSED, }, }, }; diff --git a/drivers/clk/qcom/gcc-qcs8300.c b/drivers/clk/qcom/gcc-qcs8300.c index 07218d9c96a7..31fd870b10f7 100644 --- a/drivers/clk/qcom/gcc-qcs8300.c +++ b/drivers/clk/qcom/gcc-qcs8300.c @@ -3267,7 +3267,7 @@ static struct gdsc gcc_pcie_0_gdsc = { .pd = { .name = "gcc_pcie_0_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = VOTABLE | RETAIN_FF_ENABLE | POLL_CFG_GDSCR, }; @@ -3281,7 +3281,7 @@ static struct gdsc gcc_pcie_1_gdsc = { .pd = { .name = "gcc_pcie_1_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = VOTABLE | RETAIN_FF_ENABLE | POLL_CFG_GDSCR, }; @@ -3305,7 +3305,7 @@ static struct gdsc gcc_usb20_prim_gdsc = { .pd = { .name = "gcc_usb20_prim_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = RETAIN_FF_ENABLE | POLL_CFG_GDSCR, }; @@ -3317,7 +3317,7 @@ static struct gdsc gcc_usb30_prim_gdsc = { .pd = { .name = "gcc_usb30_prim_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = RETAIN_FF_ENABLE | POLL_CFG_GDSCR, }; diff --git a/drivers/clk/qcom/gcc-shikra.c b/drivers/clk/qcom/gcc-shikra.c new file mode 100644 index 000000000000..d5222756f214 --- /dev/null +++ b/drivers/clk/qcom/gcc-shikra.c @@ -0,0 +1,4431 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/clk-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,shikra-gcc.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, + DT_EMAC0_SGMIIPHY_RCLK, + DT_EMAC0_SGMIIPHY_TCLK, + DT_EMAC1_SGMIIPHY_RCLK, + DT_EMAC1_SGMIIPHY_TCLK, + DT_PCIE_PIPE_CLK, + DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +enum { + P_BI_TCXO, + P_EMAC0_SGMIIPHY_RCLK, + P_EMAC0_SGMIIPHY_TCLK, + P_EMAC1_SGMIIPHY_RCLK, + P_EMAC1_SGMIIPHY_TCLK, + P_GPLL0_OUT_AUX2, + P_GPLL0_OUT_EARLY, + P_GPLL10_OUT_MAIN, + P_GPLL11_OUT_AUX, + P_GPLL11_OUT_AUX2, + P_GPLL11_OUT_MAIN, + P_GPLL12_OUT_AUX2, + P_GPLL12_OUT_EARLY, + P_GPLL3_OUT_EARLY, + P_GPLL3_OUT_MAIN, + P_GPLL4_OUT_MAIN, + P_GPLL5_OUT_MAIN, + P_GPLL6_OUT_EARLY, + P_GPLL6_OUT_MAIN, + P_GPLL7_OUT_MAIN, + P_GPLL8_OUT_EARLY, + P_GPLL8_OUT_MAIN, + P_GPLL9_OUT_EARLY, + P_GPLL9_OUT_MAIN, + P_PCIE_PIPE_CLK, + P_SLEEP_CLK, + P_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +static const struct pll_vco brammo_vco[] = { + { 500000000, 1250000000, 0 }, +}; + +static const struct pll_vco default_vco[] = { + { 500000000, 1000000000, 2 }, +}; + +static const struct pll_vco spark_vco[] = { + { 750000000, 1500000000, 1 }, +}; + +static struct clk_alpha_pll gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll0_out_aux2[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll0_out_aux2 = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll0_out_aux2, + .num_post_div = ARRAY_SIZE(post_div_table_gpll0_out_aux2), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll0_out_aux2", + .parent_hws = (const struct clk_hw*[]) { + &gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +/* 1152.0 MHz Configuration */ +static const struct alpha_pll_config gpll10_config = { + .l = 0x3c, + .alpha = 0x0, + .vco_val = BIT(20), + .vco_mask = GENMASK(21, 20), + .main_output_mask = BIT(0), + .config_ctl_val = 0x4001055b, + .test_ctl_hi1_val = 0x1, +}; + +static struct clk_alpha_pll gpll10 = { + .offset = 0xa000, + .config = &gpll10_config, + .vco_table = spark_vco, + .num_vco = ARRAY_SIZE(spark_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gpll10", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +/* 600.0 MHz Configuration */ +static const struct alpha_pll_config gpll11_config = { + .l = 0x1f, + .alpha = 0x0, + .alpha_hi = 0x40, + .alpha_en_mask = BIT(24), + .vco_val = BIT(21), + .vco_mask = GENMASK(21, 20), + .main_output_mask = BIT(0), + .config_ctl_val = 0x4001055b, + .test_ctl_hi1_val = 0x1, +}; + +static struct clk_alpha_pll gpll11 = { + .offset = 0xb000, + .config = &gpll11_config, + .vco_table = default_vco, + .num_vco = ARRAY_SIZE(default_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .flags = SUPPORTS_DYNAMIC_UPDATE, + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gpll11", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll12 = { + .offset = 0xc000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gpll12", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll12_out_aux2[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll12_out_aux2 = { + .offset = 0xc000, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll12_out_aux2, + .num_post_div = ARRAY_SIZE(post_div_table_gpll12_out_aux2), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll12_out_aux2", + .parent_hws = (const struct clk_hw*[]) { + &gpll12.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static struct clk_alpha_pll gpll3 = { + .offset = 0x3000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gpll3", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll3_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll3_out_main = { + .offset = 0x3000, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll3_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_gpll3_out_main), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll3_out_main", + .parent_hws = (const struct clk_hw*[]) { + &gpll3.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static struct clk_alpha_pll gpll4 = { + .offset = 0x4000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gpll4", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll5 = { + .offset = 0x5000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gpll5", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll6 = { + .offset = 0x6000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gpll6", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll6_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll6_out_main = { + .offset = 0x6000, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll6_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_gpll6_out_main), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll6_out_main", + .parent_hws = (const struct clk_hw*[]) { + &gpll6.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static struct clk_alpha_pll gpll7 = { + .offset = 0x7000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gpll7", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_ops, + }, + }, +}; + +/* 533.2 MHz Configuration */ +static const struct alpha_pll_config gpll8_config = { + .l = 0x1b, + .alpha = 0x55555555, + .alpha_hi = 0xc5, + .alpha_en_mask = BIT(24), + .vco_val = BIT(21), + .vco_mask = GENMASK(21, 20), + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), + .post_div_val = BIT(8), + .post_div_mask = GENMASK(11, 8), + .config_ctl_val = 0x4001055b, + .test_ctl_hi1_val = 0x1, +}; + +static struct clk_alpha_pll gpll8 = { + .offset = 0x8000, + .config = &gpll8_config, + .vco_table = default_vco, + .num_vco = ARRAY_SIZE(default_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .flags = SUPPORTS_DYNAMIC_UPDATE, + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gpll8", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll8_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll8_out_main = { + .offset = 0x8000, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll8_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_gpll8_out_main), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll8_out_main", + .parent_hws = (const struct clk_hw*[]) { + &gpll8.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +/* 1152.0 MHz Configuration */ +static const struct alpha_pll_config gpll9_config = { + .l = 0x3c, + .alpha = 0x0, + .post_div_val = BIT(8), + .post_div_mask = GENMASK(9, 8), + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), + .config_ctl_val = 0x00004289, + .test_ctl_val = 0x08000000, +}; + +static struct clk_alpha_pll gpll9 = { + .offset = 0x9000, + .config = &gpll9_config, + .vco_table = brammo_vco, + .num_vco = ARRAY_SIZE(brammo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_BRAMMO_EVO], + .clkr = { + .enable_reg = 0x79000, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gpll9", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll9_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll9_out_main = { + .offset = 0x9000, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll9_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_gpll9_out_main), + .width = 2, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_BRAMMO_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll9_out_main", + .parent_hws = (const struct clk_hw*[]) { + &gpll9.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, + { P_GPLL6_OUT_MAIN, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, + { .hw = &gpll6_out_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL9_OUT_EARLY, 2 }, + { P_GPLL10_OUT_MAIN, 3 }, + { P_GPLL9_OUT_MAIN, 5 }, + { P_GPLL3_OUT_MAIN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll9.clkr.hw }, + { .hw = &gpll10.clkr.hw }, + { .hw = &gpll9_out_main.clkr.hw }, + { .hw = &gpll3_out_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, + { P_GPLL10_OUT_MAIN, 3 }, + { P_GPLL4_OUT_MAIN, 5 }, + { P_GPLL3_OUT_EARLY, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, + { .hw = &gpll10.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll3.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, + { P_GPLL4_OUT_MAIN, 5 }, + { P_GPLL3_OUT_MAIN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_6[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll3_out_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_7[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL8_OUT_EARLY, 2 }, + { P_GPLL10_OUT_MAIN, 3 }, + { P_GPLL8_OUT_MAIN, 4 }, + { P_GPLL9_OUT_MAIN, 5 }, + { P_GPLL3_OUT_EARLY, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_7[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll8.clkr.hw }, + { .hw = &gpll10.clkr.hw }, + { .hw = &gpll8_out_main.clkr.hw }, + { .hw = &gpll9.clkr.hw }, + { .hw = &gpll3.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL8_OUT_EARLY, 2 }, + { P_GPLL10_OUT_MAIN, 3 }, + { P_GPLL8_OUT_MAIN, 4 }, + { P_GPLL9_OUT_MAIN, 5 }, + { P_GPLL3_OUT_MAIN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_8[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll8.clkr.hw }, + { .hw = &gpll10.clkr.hw }, + { .hw = &gpll8_out_main.clkr.hw }, + { .hw = &gpll9.clkr.hw }, + { .hw = &gpll3_out_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_9[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL8_OUT_EARLY, 2 }, + { P_GPLL10_OUT_MAIN, 3 }, + { P_GPLL6_OUT_MAIN, 4 }, + { P_GPLL9_OUT_MAIN, 5 }, + { P_GPLL3_OUT_EARLY, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_9[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll8.clkr.hw }, + { .hw = &gpll10.clkr.hw }, + { .hw = &gpll6_out_main.clkr.hw }, + { .hw = &gpll9.clkr.hw }, + { .hw = &gpll3.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_10[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_10[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_11[] = { + { P_BI_TCXO, 0 }, + { P_GPLL12_OUT_EARLY, 1 }, + { P_GPLL12_OUT_AUX2, 4 }, + { P_GPLL3_OUT_EARLY, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_11[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll12.clkr.hw }, + { .hw = &gpll12_out_aux2.clkr.hw }, + { .hw = &gpll3.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_12[] = { + { P_BI_TCXO, 0 }, + { P_GPLL12_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, + { P_GPLL12_OUT_AUX2, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_12[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll12.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, + { .hw = &gpll12_out_aux2.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_13[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, +}; + +static const struct clk_parent_data gcc_parent_data_13[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_14[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, + { P_GPLL10_OUT_MAIN, 3 }, + { P_GPLL8_OUT_MAIN, 4 }, + { P_GPLL9_OUT_MAIN, 5 }, + { P_GPLL3_OUT_EARLY, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_14[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, + { .hw = &gpll10.clkr.hw }, + { .hw = &gpll8_out_main.clkr.hw }, + { .hw = &gpll9.clkr.hw }, + { .hw = &gpll3.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_15[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL8_OUT_EARLY, 2 }, + { P_GPLL10_OUT_MAIN, 3 }, + { P_GPLL6_OUT_EARLY, 5 }, + { P_GPLL3_OUT_MAIN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_15[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll8.clkr.hw }, + { .hw = &gpll10.clkr.hw }, + { .hw = &gpll6.clkr.hw }, + { .hw = &gpll3_out_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_21[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_EARLY, 1 }, + { P_GPLL0_OUT_AUX2, 2 }, + { P_GPLL7_OUT_MAIN, 3 }, + { P_GPLL4_OUT_MAIN, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_21[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_aux2.clkr.hw }, + { .hw = &gpll7.clkr.hw }, + { .hw = &gpll4.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_22[] = { + { P_BI_TCXO, 0 }, + { P_GPLL12_OUT_EARLY, 1 }, + { P_GPLL5_OUT_MAIN, 3 }, + { P_GPLL12_OUT_AUX2, 4 }, + { P_GPLL3_OUT_EARLY, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_22[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll12.clkr.hw }, + { .hw = &gpll5.clkr.hw }, + { .hw = &gpll12_out_aux2.clkr.hw }, + { .hw = &gpll3.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_24[] = { + { P_BI_TCXO, 0 }, + { P_GPLL11_OUT_MAIN, 1 }, + { P_GPLL11_OUT_AUX, 2 }, + { P_GPLL11_OUT_AUX2, 3 }, +}; + +static const struct clk_parent_data gcc_parent_data_24[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpll11.clkr.hw }, + { .hw = &gpll11.clkr.hw }, + { .hw = &gpll11.clkr.hw }, +}; + +static struct clk_regmap_phy_mux gcc_emac0_cc_sgmiiphy_rx_clk_src = { + .reg = 0xad048, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_cc_sgmiiphy_rx_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_EMAC0_SGMIIPHY_RCLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_emac0_cc_sgmiiphy_tx_clk_src = { + .reg = 0xad040, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_cc_sgmiiphy_tx_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_EMAC0_SGMIIPHY_TCLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_emac1_cc_sgmiiphy_rx_clk_src = { + .reg = 0xae048, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_cc_sgmiiphy_rx_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_EMAC1_SGMIIPHY_RCLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_emac1_cc_sgmiiphy_tx_clk_src = { + .reg = 0xae040, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_cc_sgmiiphy_tx_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_EMAC1_SGMIIPHY_TCLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_pipe_clk_src = { + .reg = 0xaf058, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_usb3_prim_phy_pipe_clk_src = { + .reg = 0x1a05c, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_axi_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(150000000, P_GPLL0_OUT_AUX2, 2, 0, 0), + F(200000000, P_GPLL0_OUT_AUX2, 1.5, 0, 0), + F(300000000, P_GPLL0_OUT_AUX2, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_axi_clk_src = { + .cmd_rcgr = 0x5802c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_camss_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_axi_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_cci_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_GPLL0_OUT_AUX2, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_cci_clk_src = { + .cmd_rcgr = 0x56000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_14, + .freq_tbl = ftbl_gcc_camss_cci_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_cci_clk_src", + .parent_data = gcc_parent_data_14, + .num_parents = ARRAY_SIZE(gcc_parent_data_14), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_csi0phytimer_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_AUX2, 3, 0, 0), + F(200000000, P_GPLL0_OUT_AUX2, 1.5, 0, 0), + F(268800000, P_GPLL4_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_csi0phytimer_clk_src = { + .cmd_rcgr = 0x45000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_camss_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_csi0phytimer_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_camss_csi1phytimer_clk_src = { + .cmd_rcgr = 0x4501c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_camss_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_csi1phytimer_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_mclk0_clk_src[] = { + F(19200000, P_GPLL9_OUT_EARLY, 1, 1, 60), + F(24000000, P_GPLL9_OUT_MAIN, 1, 1, 24), + F(64000000, P_GPLL9_OUT_EARLY, 9, 1, 2), + { } +}; + +static struct clk_rcg2 gcc_camss_mclk0_clk_src = { + .cmd_rcgr = 0x51000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_camss_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk0_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_camss_mclk1_clk_src = { + .cmd_rcgr = 0x5101c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_camss_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk1_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_camss_mclk2_clk_src = { + .cmd_rcgr = 0x51038, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_camss_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk2_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_camss_mclk3_clk_src = { + .cmd_rcgr = 0x51054, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_camss_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk3_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_ope_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(171428571, P_GPLL0_OUT_EARLY, 3.5, 0, 0), + F(240000000, P_GPLL0_OUT_EARLY, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_ope_ahb_clk_src = { + .cmd_rcgr = 0x55024, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gcc_camss_ope_ahb_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_ope_ahb_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_ope_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_GPLL8_OUT_MAIN, 2, 0, 0), + F(266600000, P_GPLL8_OUT_MAIN, 1, 0, 0), + F(465000000, P_GPLL8_OUT_MAIN, 1, 0, 0), + F(580000000, P_GPLL8_OUT_EARLY, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_ope_clk_src = { + .cmd_rcgr = 0x55004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gcc_camss_ope_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_ope_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_tfe_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(128000000, P_GPLL10_OUT_MAIN, 9, 0, 0), + F(135529412, P_GPLL10_OUT_MAIN, 8.5, 0, 0), + F(144000000, P_GPLL10_OUT_MAIN, 8, 0, 0), + F(153600000, P_GPLL10_OUT_MAIN, 7.5, 0, 0), + F(164571429, P_GPLL10_OUT_MAIN, 7, 0, 0), + F(177230769, P_GPLL10_OUT_MAIN, 6.5, 0, 0), + F(192000000, P_GPLL10_OUT_MAIN, 6, 0, 0), + F(209454545, P_GPLL10_OUT_MAIN, 5.5, 0, 0), + F(230400000, P_GPLL10_OUT_MAIN, 5, 0, 0), + F(256000000, P_GPLL10_OUT_MAIN, 4.5, 0, 0), + F(288000000, P_GPLL10_OUT_MAIN, 4, 0, 0), + F(329142857, P_GPLL10_OUT_MAIN, 3.5, 0, 0), + F(384000000, P_GPLL10_OUT_MAIN, 3, 0, 0), + F(460800000, P_GPLL10_OUT_MAIN, 2.5, 0, 0), + F(576000000, P_GPLL10_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_tfe_0_clk_src = { + .cmd_rcgr = 0x52004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_camss_tfe_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_0_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_tfe_0_csid_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(120000000, P_GPLL0_OUT_EARLY, 5, 0, 0), + F(192000000, P_GPLL6_OUT_MAIN, 2, 0, 0), + F(240000000, P_GPLL0_OUT_EARLY, 2.5, 0, 0), + F(384000000, P_GPLL6_OUT_MAIN, 1, 0, 0), + F(426400000, P_GPLL3_OUT_EARLY, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_tfe_0_csid_clk_src = { + .cmd_rcgr = 0x52094, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_9, + .freq_tbl = ftbl_gcc_camss_tfe_0_csid_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_0_csid_clk_src", + .parent_data = gcc_parent_data_9, + .num_parents = ARRAY_SIZE(gcc_parent_data_9), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_camss_tfe_1_clk_src = { + .cmd_rcgr = 0x52024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_camss_tfe_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_1_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_camss_tfe_1_csid_clk_src = { + .cmd_rcgr = 0x520b4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_9, + .freq_tbl = ftbl_gcc_camss_tfe_0_csid_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_1_csid_clk_src", + .parent_data = gcc_parent_data_9, + .num_parents = ARRAY_SIZE(gcc_parent_data_9), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_tfe_cphy_rx_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(240000000, P_GPLL0_OUT_EARLY, 2.5, 0, 0), + F(341333333, P_GPLL6_OUT_EARLY, 1, 4, 9), + F(384000000, P_GPLL6_OUT_EARLY, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_tfe_cphy_rx_clk_src = { + .cmd_rcgr = 0x52064, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_15, + .freq_tbl = ftbl_gcc_camss_tfe_cphy_rx_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_cphy_rx_clk_src", + .parent_data = gcc_parent_data_15, + .num_parents = ARRAY_SIZE(gcc_parent_data_15), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_camss_top_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(40000000, P_GPLL0_OUT_AUX2, 7.5, 0, 0), + F(80000000, P_GPLL0_OUT_EARLY, 7.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_camss_top_ahb_clk_src = { + .cmd_rcgr = 0x58010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_camss_top_ahb_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_top_ahb_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_emac0_axi_clk_src[] = { + F(75000000, P_GPLL0_OUT_AUX2, 4, 0, 0), + F(120000000, P_GPLL0_OUT_AUX2, 2.5, 0, 0), + F(150000000, P_GPLL0_OUT_AUX2, 2, 0, 0), + F(200000000, P_GPLL0_OUT_AUX2, 1.5, 0, 0), + F(240000000, P_GPLL0_OUT_EARLY, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_emac0_axi_clk_src = { + .cmd_rcgr = 0x109dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_emac0_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_axi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_emac0_phy_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_emac0_phy_aux_clk_src = { + .cmd_rcgr = 0xad01c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .freq_tbl = ftbl_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_phy_aux_clk_src", + .parent_data = gcc_parent_data_10, + .num_parents = ARRAY_SIZE(gcc_parent_data_10), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_emac0_ptp_clk_src[] = { + F(250000000, P_GPLL12_OUT_AUX2, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_emac0_ptp_clk_src = { + .cmd_rcgr = 0xad064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_11, + .freq_tbl = ftbl_gcc_emac0_ptp_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_ptp_clk_src", + .parent_data = gcc_parent_data_11, + .num_parents = ARRAY_SIZE(gcc_parent_data_11), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_emac0_rgmii_clk_src[] = { + F(50000000, P_GPLL0_OUT_AUX2, 6, 0, 0), + F(125000000, P_GPLL12_OUT_AUX2, 4, 0, 0), + F(250000000, P_GPLL12_OUT_EARLY, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_emac0_rgmii_clk_src = { + .cmd_rcgr = 0xad04c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_12, + .freq_tbl = ftbl_gcc_emac0_rgmii_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_rgmii_clk_src", + .parent_data = gcc_parent_data_12, + .num_parents = ARRAY_SIZE(gcc_parent_data_12), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_emac1_axi_clk_src = { + .cmd_rcgr = 0x109fc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_emac0_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_axi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_emac1_phy_aux_clk_src = { + .cmd_rcgr = 0xae01c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .freq_tbl = ftbl_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_phy_aux_clk_src", + .parent_data = gcc_parent_data_10, + .num_parents = ARRAY_SIZE(gcc_parent_data_10), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_emac1_ptp_clk_src = { + .cmd_rcgr = 0xae064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_11, + .freq_tbl = ftbl_gcc_emac0_ptp_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_ptp_clk_src", + .parent_data = gcc_parent_data_11, + .num_parents = ARRAY_SIZE(gcc_parent_data_11), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_emac1_rgmii_clk_src = { + .cmd_rcgr = 0xae04c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_12, + .freq_tbl = ftbl_gcc_emac0_rgmii_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_rgmii_clk_src", + .parent_data = gcc_parent_data_12, + .num_parents = ARRAY_SIZE(gcc_parent_data_12), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { + F(25000000, P_GPLL0_OUT_AUX2, 12, 0, 0), + F(50000000, P_GPLL0_OUT_AUX2, 6, 0, 0), + F(100000000, P_GPLL0_OUT_AUX2, 3, 0, 0), + F(200000000, P_GPLL0_OUT_AUX2, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_gp1_clk_src = { + .cmd_rcgr = 0x4d004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_gp2_clk_src = { + .cmd_rcgr = 0x4e004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_gp3_clk_src = { + .cmd_rcgr = 0x4f004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_aux_clk_src = { + .cmd_rcgr = 0xaf074, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_aux_phy_clk_src = { + .cmd_rcgr = 0xaf05c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_aux_phy_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_rchng_phy_clk_src[] = { + F(100000000, P_GPLL0_OUT_AUX2, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_rchng_phy_clk_src = { + .cmd_rcgr = 0xaf028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_rchng_phy_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_rchng_phy_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pdm2_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(60000000, P_GPLL0_OUT_AUX2, 5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pdm2_clk_src = { + .cmd_rcgr = 0x20010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pdm2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = { + F(7372800, P_GPLL0_OUT_AUX2, 1, 384, 15625), + F(14745600, P_GPLL0_OUT_AUX2, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GPLL0_OUT_AUX2, 1, 1536, 15625), + F(32000000, P_GPLL0_OUT_AUX2, 1, 8, 75), + F(48000000, P_GPLL0_OUT_AUX2, 1, 4, 25), + F(64000000, P_GPLL0_OUT_AUX2, 1, 16, 75), + F(75000000, P_GPLL0_OUT_AUX2, 4, 0, 0), + F(80000000, P_GPLL0_OUT_AUX2, 1, 4, 15), + F(96000000, P_GPLL0_OUT_AUX2, 1, 8, 25), + F(100000000, P_GPLL0_OUT_AUX2, 3, 0, 0), + F(102400000, P_GPLL0_OUT_AUX2, 1, 128, 375), + F(112000000, P_GPLL0_OUT_AUX2, 1, 28, 75), + F(117964800, P_GPLL0_OUT_AUX2, 1, 6144, 15625), + F(120000000, P_GPLL0_OUT_AUX2, 2.5, 0, 0), + F(128000000, P_GPLL6_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap0_s0_clk_src_init = { + .name = "gcc_qupv3_wrap0_s0_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s0_clk_src = { + .cmd_rcgr = 0x1f148, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = { + .name = "gcc_qupv3_wrap0_s1_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = { + .cmd_rcgr = 0x1f278, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s2_clk_src_init = { + .name = "gcc_qupv3_wrap0_s2_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = { + .cmd_rcgr = 0x1f3a8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s3_clk_src_init = { + .name = "gcc_qupv3_wrap0_s3_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = { + .cmd_rcgr = 0x1f4d8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = { + .name = "gcc_qupv3_wrap0_s4_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { + .cmd_rcgr = 0x1f608, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = { + .name = "gcc_qupv3_wrap0_s5_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = { + .cmd_rcgr = 0x1f738, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s6_clk_src_init = { + .name = "gcc_qupv3_wrap0_s6_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s6_clk_src = { + .cmd_rcgr = 0x1f868, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s6_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s7_clk_src_init = { + .name = "gcc_qupv3_wrap0_s7_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s7_clk_src = { + .cmd_rcgr = 0x1f998, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s7_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s8_clk_src_init = { + .name = "gcc_qupv3_wrap0_s8_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s8_clk_src = { + .cmd_rcgr = 0x1fac8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s8_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s9_clk_src_init = { + .name = "gcc_qupv3_wrap0_s9_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s9_clk_src = { + .cmd_rcgr = 0x1fbf8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s9_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, P_BI_TCXO, 16, 3, 25), + F(400000, P_BI_TCXO, 12, 1, 4), + F(20000000, P_GPLL0_OUT_AUX2, 5, 1, 3), + F(25000000, P_GPLL0_OUT_AUX2, 6, 1, 2), + F(50000000, P_GPLL0_OUT_AUX2, 6, 0, 0), + F(100000000, P_GPLL0_OUT_AUX2, 3, 0, 0), + F(192000000, P_GPLL6_OUT_MAIN, 2, 0, 0), + F(384000000, P_GPLL6_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0x38028, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = { + F(75000000, P_GPLL0_OUT_AUX2, 4, 0, 0), + F(100000000, P_GPLL0_OUT_AUX2, 3, 0, 0), + F(150000000, P_GPLL0_OUT_AUX2, 2, 0, 0), + F(200000000, P_GPLL0_OUT_EARLY, 3, 0, 0), + F(300000000, P_GPLL0_OUT_AUX2, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x38010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_AUX2, 12, 0, 0), + F(50000000, P_GPLL0_OUT_AUX2, 6, 0, 0), + F(100000000, P_GPLL0_OUT_AUX2, 3, 0, 0), + F(202000000, P_GPLL7_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { + .cmd_rcgr = 0x1e00c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_21, + .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk_src", + .parent_data = gcc_parent_data_21, + .num_parents = ARRAY_SIZE(gcc_parent_data_21), + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static struct clk_rcg2 gcc_tscss_clk_src = { + .cmd_rcgr = 0xac004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_22, + .freq_tbl = ftbl_gcc_emac0_ptp_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_tscss_clk_src", + .parent_data = gcc_parent_data_22, + .num_parents = ARRAY_SIZE(gcc_parent_data_22), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb20_master_clk_src[] = { + F(60000000, P_GPLL0_OUT_AUX2, 5, 0, 0), + F(120000000, P_GPLL0_OUT_EARLY, 5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb20_master_clk_src = { + .cmd_rcgr = 0xb003c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb20_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb20_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_usb20_mock_utmi_clk_src = { + .cmd_rcgr = 0xb0020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_13, + .freq_tbl = ftbl_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb20_mock_utmi_clk_src", + .parent_data = gcc_parent_data_13, + .num_parents = ARRAY_SIZE(gcc_parent_data_13), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { + F(66666667, P_GPLL0_OUT_AUX2, 4.5, 0, 0), + F(133333333, P_GPLL0_OUT_EARLY, 4.5, 0, 0), + F(200000000, P_GPLL0_OUT_EARLY, 3, 0, 0), + F(240000000, P_GPLL0_OUT_EARLY, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { + .cmd_rcgr = 0x1a01c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb30_prim_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { + .cmd_rcgr = 0x1a034, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { + .cmd_rcgr = 0x1a060, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_video_venus_clk_src[] = { + F(133333333, P_GPLL11_OUT_MAIN, 4.5, 0, 0), + F(240000000, P_GPLL11_OUT_MAIN, 2.5, 0, 0), + F(300000000, P_GPLL11_OUT_MAIN, 2, 0, 0), + F(384000000, P_GPLL11_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_video_venus_clk_src = { + .cmd_rcgr = 0x6d000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_24, + .freq_tbl = ftbl_gcc_video_venus_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_video_venus_clk_src", + .parent_data = gcc_parent_data_24, + .num_parents = ARRAY_SIZE(gcc_parent_data_24), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div gcc_disp_gpll0_clk_src = { + .reg = 0x17058, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_disp_gpll0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, +}; + +static struct clk_regmap_div gcc_usb20_mock_utmi_postdiv_clk_src = { + .reg = 0xb0038, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb20_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb20_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb30_prim_mock_utmi_postdiv_clk_src = { + .reg = 0x1a04c, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gcc_ahb2phy_csi_clk = { + .halt_reg = 0x1d004, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x1d004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1d004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ahb2phy_csi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ahb2phy_usb_clk = { + .halt_reg = 0x1d008, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x1d008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1d008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ahb2phy_usb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x23004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cam_throttle_nrt_clk = { + .halt_reg = 0x17070, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17070, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cam_throttle_nrt_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cam_throttle_rt_clk = { + .halt_reg = 0x1706c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1706c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cam_throttle_rt_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_axi_clk = { + .halt_reg = 0x58044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x58044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_camnoc_atb_clk = { + .halt_reg = 0x5804c, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x5804c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x5804c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_camnoc_atb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_camnoc_dragonlink_atb_clk = { + .halt_reg = 0x58060, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x58060, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x58060, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_camnoc_dragonlink_atb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_camnoc_nts_xo_clk = { + .halt_reg = 0x58050, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x58050, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x58050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_camnoc_nts_xo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_cci_0_clk = { + .halt_reg = 0x56018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x56018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_cci_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_cci_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_cphy_0_clk = { + .halt_reg = 0x52088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x52088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_cphy_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_cphy_1_clk = { + .halt_reg = 0x5208c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5208c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_cphy_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_csi0phytimer_clk = { + .halt_reg = 0x45018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x45018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_csi0phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_csi0phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_csi1phytimer_clk = { + .halt_reg = 0x45034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x45034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_csi1phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_csi1phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_mclk0_clk = { + .halt_reg = 0x51018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x51018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_mclk1_clk = { + .halt_reg = 0x51034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x51034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_mclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_mclk2_clk = { + .halt_reg = 0x51050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x51050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_mclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_mclk3_clk = { + .halt_reg = 0x5106c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5106c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_mclk3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_mclk3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_nrt_axi_clk = { + .halt_reg = 0x58054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x58054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_nrt_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_ope_ahb_clk = { + .halt_reg = 0x5503c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5503c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_ope_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_ope_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_ope_clk = { + .halt_reg = 0x5501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5501c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_ope_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_ope_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_rt_axi_clk = { + .halt_reg = 0x5805c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5805c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_rt_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_tfe_0_clk = { + .halt_reg = 0x5201c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5201c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_tfe_0_cphy_rx_clk = { + .halt_reg = 0x5207c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5207c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_0_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_tfe_0_csid_clk = { + .halt_reg = 0x520ac, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x520ac, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_0_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_0_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_tfe_1_clk = { + .halt_reg = 0x5203c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5203c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_tfe_1_cphy_rx_clk = { + .halt_reg = 0x52080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x52080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_1_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_tfe_1_csid_clk = { + .halt_reg = 0x520cc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x520cc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_tfe_1_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_tfe_1_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camss_top_ahb_clk = { + .halt_reg = 0x58028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x58028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camss_top_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_camss_top_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_usb2_prim_axi_clk = { + .halt_reg = 0x111c4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x111c4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x111c4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_usb2_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb20_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_usb3_prim_axi_clk = { + .halt_reg = 0x1a07c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1a07c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a07c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ddrss_gpu_axi_clk = { + .halt_reg = 0x71000, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x71000, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x71000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ddrss_gpu_axi_clk", + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gcc_ddrss_memnoc_pcie_sf_clk = { + .halt_reg = 0x29044, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x29044, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x29044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ddrss_memnoc_pcie_sf_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_disp_gpll0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_disp_gpll0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_hf_axi_clk = { + .halt_reg = 0x17020, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x17020, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x17020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_disp_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_throttle_core_clk = { + .halt_reg = 0x17064, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17064, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_disp_throttle_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_ahb_clk = { + .halt_reg = 0xad010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xad010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xad010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_axi_clk = { + .halt_reg = 0xad014, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xad014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xad014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_axi_sys_noc_clk = { + .halt_reg = 0x109d4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x109d4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x109d4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_axi_sys_noc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_cc_sgmiiphy_rx_clk = { + .halt_reg = 0xad044, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xad044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_cc_sgmiiphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_cc_sgmiiphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_cc_sgmiiphy_tx_clk = { + .halt_reg = 0xad03c, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0xad03c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xad03c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_cc_sgmiiphy_tx_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_cc_sgmiiphy_tx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_phy_aux_clk = { + .halt_reg = 0xad018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xad018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_ptp_clk = { + .halt_reg = 0xad034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xad034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_ptp_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_ptp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac0_rgmii_clk = { + .halt_reg = 0xad038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xad038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac0_rgmii_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_rgmii_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_ahb_clk = { + .halt_reg = 0xae010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xae010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xae010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_axi_clk = { + .halt_reg = 0xae014, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xae014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xae014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac1_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_axi_sys_noc_clk = { + .halt_reg = 0x109f4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x109f4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x109f4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_axi_sys_noc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac1_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_cc_sgmiiphy_rx_clk = { + .halt_reg = 0xae044, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xae044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_cc_sgmiiphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac1_cc_sgmiiphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_cc_sgmiiphy_tx_clk = { + .halt_reg = 0xae03c, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0xae03c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xae03c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_cc_sgmiiphy_tx_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac1_cc_sgmiiphy_tx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_phy_aux_clk = { + .halt_reg = 0xae018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xae018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac1_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_ptp_clk = { + .halt_reg = 0xae034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xae034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_ptp_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac1_ptp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_emac1_rgmii_clk = { + .halt_reg = 0xae038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xae038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_emac1_rgmii_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac1_rgmii_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x4d000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x4e000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4e000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0x4f000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4f000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gpll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gpll0_out_aux2.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_memnoc_gfx_clk = { + .halt_reg = 0x3600c, + .halt_check = BRANCH_VOTED, + .hwcg_reg = 0x3600c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x3600c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_memnoc_gfx_clk", + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_smmu_vote_clk = { + .halt_reg = 0x7d000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_smmu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_snoc_dvm_gfx_clk = { + .halt_reg = 0x36018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x36018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_snoc_dvm_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_throttle_core_clk = { + .halt_reg = 0x36048, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x36048, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_throttle_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mmu_tcu_vote_clk = { + .halt_reg = 0x7d06c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d06c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mmu_tcu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_aux_clk = { + .halt_reg = 0xaf044, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf044, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_cfg_ahb_clk = { + .halt_reg = 0xaf010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_clkref_en = { + .halt_reg = 0xb8000, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xb8000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_clkref_en", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_mstr_axi_clk = { + .halt_reg = 0xaf020, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf020, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_pipe_clk = { + .halt_reg = 0xaf050, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0xaf050, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79018, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_rchng_phy_clk = { + .halt_reg = 0xaf040, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(31), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_rchng_phy_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_rchng_phy_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_sleep_clk = { + .halt_reg = 0xaf04c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf04c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79018, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_sleep_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_phy_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_slv_axi_clk = { + .halt_reg = 0xaf018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_slv_q2a_axi_clk = { + .halt_reg = 0xaf014, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_tbu_clk = { + .halt_reg = 0xaf098, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf098, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79018, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_tbu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_throttle_core_clk = { + .halt_reg = 0xaf094, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf094, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79018, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_throttle_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_throttle_xo_clk = { + .halt_reg = 0xaf090, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79018, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_throttle_xo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_tile_axi_sys_noc_clk = { + .halt_reg = 0x10f2c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x10f2c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x10f2c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_tile_axi_sys_noc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_emac0_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x2000c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2000c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pdm2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x20004, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x20004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x20004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_xo4_clk = { + .halt_reg = 0x20008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x20008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_xo4_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pwm0_xo512_clk = { + .halt_reg = 0x2002c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2002c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pwm0_xo512_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_camera_nrt_ahb_clk = { + .halt_reg = 0x17014, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_camera_nrt_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_camera_rt_ahb_clk = { + .halt_reg = 0x17060, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17060, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_camera_rt_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_disp_ahb_clk = { + .halt_reg = 0x17018, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_disp_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_gpu_cfg_ahb_clk = { + .halt_reg = 0x36040, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x36040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7900c, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_gpu_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_pcie_cfg_ahb_clk = { + .halt_reg = 0xaf08c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xaf08c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79018, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_pcie_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_video_vcodec_ahb_clk = { + .halt_reg = 0x17010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_video_vcodec_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_core_2x_clk = { + .halt_reg = 0x1f014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_core_clk = { + .halt_reg = 0x1f00c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s0_clk = { + .halt_reg = 0x1f144, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s1_clk = { + .halt_reg = 0x1f274, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s2_clk = { + .halt_reg = 0x1f3a4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s3_clk = { + .halt_reg = 0x1f4d4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s4_clk = { + .halt_reg = 0x1f604, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s5_clk = { + .halt_reg = 0x1f734, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s6_clk = { + .halt_reg = 0x1f864, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s7_clk = { + .halt_reg = 0x1f994, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s7_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s8_clk = { + .halt_reg = 0x1fac4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s8_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s8_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s9_clk = { + .halt_reg = 0x1fbf4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(31), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap0_s9_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap0_s9_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_0_m_ahb_clk = { + .halt_reg = 0x1f004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1f004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_0_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_0_s_ahb_clk = { + .halt_reg = 0x1f008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1f008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_0_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x38008, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x38008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x38008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x38004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x38004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0x3800c, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x3800c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x3800c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x1e008, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x1e008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1e008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x1e004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1e004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc2_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sys_noc_usb2_prim_axi_clk = { + .halt_reg = 0x10a14, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x10a14, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x10a14, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sys_noc_usb2_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb20_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sys_noc_usb3_prim_axi_clk = { + .halt_reg = 0x1a078, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1a078, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a078, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sys_noc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_tscss_ahb_clk = { + .halt_reg = 0xac024, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xac024, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xac024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_tscss_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_tscss_cntr_clk = { + .halt_reg = 0xac020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xac020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_tscss_cntr_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_tscss_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_tscss_etu_clk = { + .halt_reg = 0xac01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xac01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_tscss_etu_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_tscss_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_clkref_en = { + .halt_reg = 0x8c000, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x8c000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_clkref_en", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb20_master_clk = { + .halt_reg = 0xb0010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xb0010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xb0010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb20_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb20_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb20_mock_utmi_clk = { + .halt_reg = 0xb001c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb001c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb20_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb20_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb20_sleep_clk = { + .halt_reg = 0xb0018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb0018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb20_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_master_clk = { + .halt_reg = 0x1a010, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x1a010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_mock_utmi_clk = { + .halt_reg = 0x1a018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_sleep_clk = { + .halt_reg = 0x1a014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_clkref_en = { + .halt_reg = 0x9f000, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x9f000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_clkref_en", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_com_aux_clk = { + .halt_reg = 0x1a054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_com_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_pipe_clk = { + .halt_reg = 0x1a058, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x1a058, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_vcodec0_axi_clk = { + .halt_reg = 0x6e008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6e008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_vcodec0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_venus_ahb_clk = { + .halt_reg = 0x6e010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6e010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_venus_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_venus_ctl_axi_clk = { + .halt_reg = 0x6e004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6e004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_venus_ctl_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi0_clk = { + .halt_reg = 0x1701c, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x1701c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1701c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_throttle_core_clk = { + .halt_reg = 0x17068, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x79004, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_throttle_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_vcodec0_sys_clk = { + .halt_reg = 0x6d044, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x6d044, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x6d044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_vcodec0_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_video_venus_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_venus_ctl_clk = { + .halt_reg = 0x6d02c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6d02c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_venus_ctl_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_video_venus_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gcc_camss_top_gdsc = { + .gdscr = 0x58004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_camss_top_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_emac0_gdsc = { + .gdscr = 0xad004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_emac0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_emac1_gdsc = { + .gdscr = 0xae004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_emac1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_pcie_gdsc = { + .gdscr = 0xaf004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_pcie_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb20_gdsc = { + .gdscr = 0xb0004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_usb20_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb30_prim_gdsc = { + .gdscr = 0x1a004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_usb30_prim_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_venus_gdsc = { + .gdscr = 0x6d01c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_venus_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_vcodec0_gdsc = { + .gdscr = 0x6d038, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_vcodec0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &gcc_venus_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *gcc_shikra_clocks[] = { + [GCC_AHB2PHY_CSI_CLK] = &gcc_ahb2phy_csi_clk.clkr, + [GCC_AHB2PHY_USB_CLK] = &gcc_ahb2phy_usb_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_CAM_THROTTLE_NRT_CLK] = &gcc_cam_throttle_nrt_clk.clkr, + [GCC_CAM_THROTTLE_RT_CLK] = &gcc_cam_throttle_rt_clk.clkr, + [GCC_CAMSS_AXI_CLK] = &gcc_camss_axi_clk.clkr, + [GCC_CAMSS_AXI_CLK_SRC] = &gcc_camss_axi_clk_src.clkr, + [GCC_CAMSS_CAMNOC_ATB_CLK] = &gcc_camss_camnoc_atb_clk.clkr, + [GCC_CAMSS_CAMNOC_DRAGONLINK_ATB_CLK] = &gcc_camss_camnoc_dragonlink_atb_clk.clkr, + [GCC_CAMSS_CAMNOC_NTS_XO_CLK] = &gcc_camss_camnoc_nts_xo_clk.clkr, + [GCC_CAMSS_CCI_0_CLK] = &gcc_camss_cci_0_clk.clkr, + [GCC_CAMSS_CCI_CLK_SRC] = &gcc_camss_cci_clk_src.clkr, + [GCC_CAMSS_CPHY_0_CLK] = &gcc_camss_cphy_0_clk.clkr, + [GCC_CAMSS_CPHY_1_CLK] = &gcc_camss_cphy_1_clk.clkr, + [GCC_CAMSS_CSI0PHYTIMER_CLK] = &gcc_camss_csi0phytimer_clk.clkr, + [GCC_CAMSS_CSI0PHYTIMER_CLK_SRC] = &gcc_camss_csi0phytimer_clk_src.clkr, + [GCC_CAMSS_CSI1PHYTIMER_CLK] = &gcc_camss_csi1phytimer_clk.clkr, + [GCC_CAMSS_CSI1PHYTIMER_CLK_SRC] = &gcc_camss_csi1phytimer_clk_src.clkr, + [GCC_CAMSS_MCLK0_CLK] = &gcc_camss_mclk0_clk.clkr, + [GCC_CAMSS_MCLK0_CLK_SRC] = &gcc_camss_mclk0_clk_src.clkr, + [GCC_CAMSS_MCLK1_CLK] = &gcc_camss_mclk1_clk.clkr, + [GCC_CAMSS_MCLK1_CLK_SRC] = &gcc_camss_mclk1_clk_src.clkr, + [GCC_CAMSS_MCLK2_CLK] = &gcc_camss_mclk2_clk.clkr, + [GCC_CAMSS_MCLK2_CLK_SRC] = &gcc_camss_mclk2_clk_src.clkr, + [GCC_CAMSS_MCLK3_CLK] = &gcc_camss_mclk3_clk.clkr, + [GCC_CAMSS_MCLK3_CLK_SRC] = &gcc_camss_mclk3_clk_src.clkr, + [GCC_CAMSS_NRT_AXI_CLK] = &gcc_camss_nrt_axi_clk.clkr, + [GCC_CAMSS_OPE_AHB_CLK] = &gcc_camss_ope_ahb_clk.clkr, + [GCC_CAMSS_OPE_AHB_CLK_SRC] = &gcc_camss_ope_ahb_clk_src.clkr, + [GCC_CAMSS_OPE_CLK] = &gcc_camss_ope_clk.clkr, + [GCC_CAMSS_OPE_CLK_SRC] = &gcc_camss_ope_clk_src.clkr, + [GCC_CAMSS_RT_AXI_CLK] = &gcc_camss_rt_axi_clk.clkr, + [GCC_CAMSS_TFE_0_CLK] = &gcc_camss_tfe_0_clk.clkr, + [GCC_CAMSS_TFE_0_CLK_SRC] = &gcc_camss_tfe_0_clk_src.clkr, + [GCC_CAMSS_TFE_0_CPHY_RX_CLK] = &gcc_camss_tfe_0_cphy_rx_clk.clkr, + [GCC_CAMSS_TFE_0_CSID_CLK] = &gcc_camss_tfe_0_csid_clk.clkr, + [GCC_CAMSS_TFE_0_CSID_CLK_SRC] = &gcc_camss_tfe_0_csid_clk_src.clkr, + [GCC_CAMSS_TFE_1_CLK] = &gcc_camss_tfe_1_clk.clkr, + [GCC_CAMSS_TFE_1_CLK_SRC] = &gcc_camss_tfe_1_clk_src.clkr, + [GCC_CAMSS_TFE_1_CPHY_RX_CLK] = &gcc_camss_tfe_1_cphy_rx_clk.clkr, + [GCC_CAMSS_TFE_1_CSID_CLK] = &gcc_camss_tfe_1_csid_clk.clkr, + [GCC_CAMSS_TFE_1_CSID_CLK_SRC] = &gcc_camss_tfe_1_csid_clk_src.clkr, + [GCC_CAMSS_TFE_CPHY_RX_CLK_SRC] = &gcc_camss_tfe_cphy_rx_clk_src.clkr, + [GCC_CAMSS_TOP_AHB_CLK] = &gcc_camss_top_ahb_clk.clkr, + [GCC_CAMSS_TOP_AHB_CLK_SRC] = &gcc_camss_top_ahb_clk_src.clkr, + [GCC_CFG_NOC_USB2_PRIM_AXI_CLK] = &gcc_cfg_noc_usb2_prim_axi_clk.clkr, + [GCC_CFG_NOC_USB3_PRIM_AXI_CLK] = &gcc_cfg_noc_usb3_prim_axi_clk.clkr, + [GCC_DDRSS_GPU_AXI_CLK] = &gcc_ddrss_gpu_axi_clk.clkr, + [GCC_DDRSS_MEMNOC_PCIE_SF_CLK] = &gcc_ddrss_memnoc_pcie_sf_clk.clkr, + [GCC_DISP_GPLL0_CLK_SRC] = &gcc_disp_gpll0_clk_src.clkr, + [GCC_DISP_GPLL0_DIV_CLK_SRC] = &gcc_disp_gpll0_div_clk_src.clkr, + [GCC_DISP_HF_AXI_CLK] = &gcc_disp_hf_axi_clk.clkr, + [GCC_DISP_THROTTLE_CORE_CLK] = &gcc_disp_throttle_core_clk.clkr, + [GCC_EMAC0_AHB_CLK] = &gcc_emac0_ahb_clk.clkr, + [GCC_EMAC0_AXI_CLK] = &gcc_emac0_axi_clk.clkr, + [GCC_EMAC0_AXI_CLK_SRC] = &gcc_emac0_axi_clk_src.clkr, + [GCC_EMAC0_AXI_SYS_NOC_CLK] = &gcc_emac0_axi_sys_noc_clk.clkr, + [GCC_EMAC0_CC_SGMIIPHY_RX_CLK] = &gcc_emac0_cc_sgmiiphy_rx_clk.clkr, + [GCC_EMAC0_CC_SGMIIPHY_RX_CLK_SRC] = &gcc_emac0_cc_sgmiiphy_rx_clk_src.clkr, + [GCC_EMAC0_CC_SGMIIPHY_TX_CLK] = &gcc_emac0_cc_sgmiiphy_tx_clk.clkr, + [GCC_EMAC0_CC_SGMIIPHY_TX_CLK_SRC] = &gcc_emac0_cc_sgmiiphy_tx_clk_src.clkr, + [GCC_EMAC0_PHY_AUX_CLK] = &gcc_emac0_phy_aux_clk.clkr, + [GCC_EMAC0_PHY_AUX_CLK_SRC] = &gcc_emac0_phy_aux_clk_src.clkr, + [GCC_EMAC0_PTP_CLK] = &gcc_emac0_ptp_clk.clkr, + [GCC_EMAC0_PTP_CLK_SRC] = &gcc_emac0_ptp_clk_src.clkr, + [GCC_EMAC0_RGMII_CLK] = &gcc_emac0_rgmii_clk.clkr, + [GCC_EMAC0_RGMII_CLK_SRC] = &gcc_emac0_rgmii_clk_src.clkr, + [GCC_EMAC1_AHB_CLK] = &gcc_emac1_ahb_clk.clkr, + [GCC_EMAC1_AXI_CLK] = &gcc_emac1_axi_clk.clkr, + [GCC_EMAC1_AXI_CLK_SRC] = &gcc_emac1_axi_clk_src.clkr, + [GCC_EMAC1_AXI_SYS_NOC_CLK] = &gcc_emac1_axi_sys_noc_clk.clkr, + [GCC_EMAC1_CC_SGMIIPHY_RX_CLK] = &gcc_emac1_cc_sgmiiphy_rx_clk.clkr, + [GCC_EMAC1_CC_SGMIIPHY_RX_CLK_SRC] = &gcc_emac1_cc_sgmiiphy_rx_clk_src.clkr, + [GCC_EMAC1_CC_SGMIIPHY_TX_CLK] = &gcc_emac1_cc_sgmiiphy_tx_clk.clkr, + [GCC_EMAC1_CC_SGMIIPHY_TX_CLK_SRC] = &gcc_emac1_cc_sgmiiphy_tx_clk_src.clkr, + [GCC_EMAC1_PHY_AUX_CLK] = &gcc_emac1_phy_aux_clk.clkr, + [GCC_EMAC1_PHY_AUX_CLK_SRC] = &gcc_emac1_phy_aux_clk_src.clkr, + [GCC_EMAC1_PTP_CLK] = &gcc_emac1_ptp_clk.clkr, + [GCC_EMAC1_PTP_CLK_SRC] = &gcc_emac1_ptp_clk_src.clkr, + [GCC_EMAC1_RGMII_CLK] = &gcc_emac1_rgmii_clk.clkr, + [GCC_EMAC1_RGMII_CLK_SRC] = &gcc_emac1_rgmii_clk_src.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP1_CLK_SRC] = &gcc_gp1_clk_src.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr, + [GCC_GPU_GPLL0_CLK_SRC] = &gcc_gpu_gpll0_clk_src.clkr, + [GCC_GPU_GPLL0_DIV_CLK_SRC] = &gcc_gpu_gpll0_div_clk_src.clkr, + [GCC_GPU_MEMNOC_GFX_CLK] = &gcc_gpu_memnoc_gfx_clk.clkr, + [GCC_GPU_SMMU_VOTE_CLK] = &gcc_gpu_smmu_vote_clk.clkr, + [GCC_GPU_SNOC_DVM_GFX_CLK] = &gcc_gpu_snoc_dvm_gfx_clk.clkr, + [GCC_GPU_THROTTLE_CORE_CLK] = &gcc_gpu_throttle_core_clk.clkr, + [GCC_MMU_TCU_VOTE_CLK] = &gcc_mmu_tcu_vote_clk.clkr, + [GCC_PCIE_AUX_CLK] = &gcc_pcie_aux_clk.clkr, + [GCC_PCIE_AUX_CLK_SRC] = &gcc_pcie_aux_clk_src.clkr, + [GCC_PCIE_AUX_PHY_CLK_SRC] = &gcc_pcie_aux_phy_clk_src.clkr, + [GCC_PCIE_CFG_AHB_CLK] = &gcc_pcie_cfg_ahb_clk.clkr, + [GCC_PCIE_CLKREF_EN] = &gcc_pcie_clkref_en.clkr, + [GCC_PCIE_MSTR_AXI_CLK] = &gcc_pcie_mstr_axi_clk.clkr, + [GCC_PCIE_PIPE_CLK] = &gcc_pcie_pipe_clk.clkr, + [GCC_PCIE_PIPE_CLK_SRC] = &gcc_pcie_pipe_clk_src.clkr, + [GCC_PCIE_RCHNG_PHY_CLK] = &gcc_pcie_rchng_phy_clk.clkr, + [GCC_PCIE_RCHNG_PHY_CLK_SRC] = &gcc_pcie_rchng_phy_clk_src.clkr, + [GCC_PCIE_SLEEP_CLK] = &gcc_pcie_sleep_clk.clkr, + [GCC_PCIE_SLV_AXI_CLK] = &gcc_pcie_slv_axi_clk.clkr, + [GCC_PCIE_SLV_Q2A_AXI_CLK] = &gcc_pcie_slv_q2a_axi_clk.clkr, + [GCC_PCIE_TBU_CLK] = &gcc_pcie_tbu_clk.clkr, + [GCC_PCIE_THROTTLE_CORE_CLK] = &gcc_pcie_throttle_core_clk.clkr, + [GCC_PCIE_THROTTLE_XO_CLK] = &gcc_pcie_throttle_xo_clk.clkr, + [GCC_PCIE_TILE_AXI_SYS_NOC_CLK] = &gcc_pcie_tile_axi_sys_noc_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM2_CLK_SRC] = &gcc_pdm2_clk_src.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PDM_XO4_CLK] = &gcc_pdm_xo4_clk.clkr, + [GCC_PWM0_XO512_CLK] = &gcc_pwm0_xo512_clk.clkr, + [GCC_QMIP_CAMERA_NRT_AHB_CLK] = &gcc_qmip_camera_nrt_ahb_clk.clkr, + [GCC_QMIP_CAMERA_RT_AHB_CLK] = &gcc_qmip_camera_rt_ahb_clk.clkr, + [GCC_QMIP_DISP_AHB_CLK] = &gcc_qmip_disp_ahb_clk.clkr, + [GCC_QMIP_GPU_CFG_AHB_CLK] = &gcc_qmip_gpu_cfg_ahb_clk.clkr, + [GCC_QMIP_PCIE_CFG_AHB_CLK] = &gcc_qmip_pcie_cfg_ahb_clk.clkr, + [GCC_QMIP_VIDEO_VCODEC_AHB_CLK] = &gcc_qmip_video_vcodec_ahb_clk.clkr, + [GCC_QUPV3_WRAP0_CORE_2X_CLK] = &gcc_qupv3_wrap0_core_2x_clk.clkr, + [GCC_QUPV3_WRAP0_CORE_CLK] = &gcc_qupv3_wrap0_core_clk.clkr, + [GCC_QUPV3_WRAP0_S0_CLK] = &gcc_qupv3_wrap0_s0_clk.clkr, + [GCC_QUPV3_WRAP0_S0_CLK_SRC] = &gcc_qupv3_wrap0_s0_clk_src.clkr, + [GCC_QUPV3_WRAP0_S1_CLK] = &gcc_qupv3_wrap0_s1_clk.clkr, + [GCC_QUPV3_WRAP0_S1_CLK_SRC] = &gcc_qupv3_wrap0_s1_clk_src.clkr, + [GCC_QUPV3_WRAP0_S2_CLK] = &gcc_qupv3_wrap0_s2_clk.clkr, + [GCC_QUPV3_WRAP0_S2_CLK_SRC] = &gcc_qupv3_wrap0_s2_clk_src.clkr, + [GCC_QUPV3_WRAP0_S3_CLK] = &gcc_qupv3_wrap0_s3_clk.clkr, + [GCC_QUPV3_WRAP0_S3_CLK_SRC] = &gcc_qupv3_wrap0_s3_clk_src.clkr, + [GCC_QUPV3_WRAP0_S4_CLK] = &gcc_qupv3_wrap0_s4_clk.clkr, + [GCC_QUPV3_WRAP0_S4_CLK_SRC] = &gcc_qupv3_wrap0_s4_clk_src.clkr, + [GCC_QUPV3_WRAP0_S5_CLK] = &gcc_qupv3_wrap0_s5_clk.clkr, + [GCC_QUPV3_WRAP0_S5_CLK_SRC] = &gcc_qupv3_wrap0_s5_clk_src.clkr, + [GCC_QUPV3_WRAP0_S6_CLK] = &gcc_qupv3_wrap0_s6_clk.clkr, + [GCC_QUPV3_WRAP0_S6_CLK_SRC] = &gcc_qupv3_wrap0_s6_clk_src.clkr, + [GCC_QUPV3_WRAP0_S7_CLK] = &gcc_qupv3_wrap0_s7_clk.clkr, + [GCC_QUPV3_WRAP0_S7_CLK_SRC] = &gcc_qupv3_wrap0_s7_clk_src.clkr, + [GCC_QUPV3_WRAP0_S8_CLK] = &gcc_qupv3_wrap0_s8_clk.clkr, + [GCC_QUPV3_WRAP0_S8_CLK_SRC] = &gcc_qupv3_wrap0_s8_clk_src.clkr, + [GCC_QUPV3_WRAP0_S9_CLK] = &gcc_qupv3_wrap0_s9_clk.clkr, + [GCC_QUPV3_WRAP0_S9_CLK_SRC] = &gcc_qupv3_wrap0_s9_clk_src.clkr, + [GCC_QUPV3_WRAP_0_M_AHB_CLK] = &gcc_qupv3_wrap_0_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_0_S_AHB_CLK] = &gcc_qupv3_wrap_0_s_ahb_clk.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_APPS_CLK_SRC] = &gcc_sdcc1_apps_clk_src.clkr, + [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr, + [GCC_SDCC1_ICE_CORE_CLK_SRC] = &gcc_sdcc1_ice_core_clk_src.clkr, + [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SDCC2_APPS_CLK_SRC] = &gcc_sdcc2_apps_clk_src.clkr, + [GCC_SYS_NOC_USB2_PRIM_AXI_CLK] = &gcc_sys_noc_usb2_prim_axi_clk.clkr, + [GCC_SYS_NOC_USB3_PRIM_AXI_CLK] = &gcc_sys_noc_usb3_prim_axi_clk.clkr, + [GCC_TSCSS_AHB_CLK] = &gcc_tscss_ahb_clk.clkr, + [GCC_TSCSS_CLK_SRC] = &gcc_tscss_clk_src.clkr, + [GCC_TSCSS_CNTR_CLK] = &gcc_tscss_cntr_clk.clkr, + [GCC_TSCSS_ETU_CLK] = &gcc_tscss_etu_clk.clkr, + [GCC_UFS_CLKREF_EN] = &gcc_ufs_clkref_en.clkr, + [GCC_USB20_MASTER_CLK] = &gcc_usb20_master_clk.clkr, + [GCC_USB20_MASTER_CLK_SRC] = &gcc_usb20_master_clk_src.clkr, + [GCC_USB20_MOCK_UTMI_CLK] = &gcc_usb20_mock_utmi_clk.clkr, + [GCC_USB20_MOCK_UTMI_CLK_SRC] = &gcc_usb20_mock_utmi_clk_src.clkr, + [GCC_USB20_MOCK_UTMI_POSTDIV_CLK_SRC] = &gcc_usb20_mock_utmi_postdiv_clk_src.clkr, + [GCC_USB20_SLEEP_CLK] = &gcc_usb20_sleep_clk.clkr, + [GCC_USB30_PRIM_MASTER_CLK] = &gcc_usb30_prim_master_clk.clkr, + [GCC_USB30_PRIM_MASTER_CLK_SRC] = &gcc_usb30_prim_master_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK] = &gcc_usb30_prim_mock_utmi_clk.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC] = &gcc_usb30_prim_mock_utmi_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC] = &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr, + [GCC_USB30_PRIM_SLEEP_CLK] = &gcc_usb30_prim_sleep_clk.clkr, + [GCC_USB3_PRIM_CLKREF_EN] = &gcc_usb3_prim_clkref_en.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK_SRC] = &gcc_usb3_prim_phy_aux_clk_src.clkr, + [GCC_USB3_PRIM_PHY_COM_AUX_CLK] = &gcc_usb3_prim_phy_com_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK] = &gcc_usb3_prim_phy_pipe_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK_SRC] = &gcc_usb3_prim_phy_pipe_clk_src.clkr, + [GCC_VCODEC0_AXI_CLK] = &gcc_vcodec0_axi_clk.clkr, + [GCC_VENUS_AHB_CLK] = &gcc_venus_ahb_clk.clkr, + [GCC_VENUS_CTL_AXI_CLK] = &gcc_venus_ctl_axi_clk.clkr, + [GCC_VIDEO_AXI0_CLK] = &gcc_video_axi0_clk.clkr, + [GCC_VIDEO_THROTTLE_CORE_CLK] = &gcc_video_throttle_core_clk.clkr, + [GCC_VIDEO_VCODEC0_SYS_CLK] = &gcc_video_vcodec0_sys_clk.clkr, + [GCC_VIDEO_VENUS_CLK_SRC] = &gcc_video_venus_clk_src.clkr, + [GCC_VIDEO_VENUS_CTL_CLK] = &gcc_video_venus_ctl_clk.clkr, + [GPLL0] = &gpll0.clkr, + [GPLL0_OUT_AUX2] = &gpll0_out_aux2.clkr, + [GPLL10] = &gpll10.clkr, + [GPLL11] = &gpll11.clkr, + [GPLL12] = &gpll12.clkr, + [GPLL12_OUT_AUX2] = &gpll12_out_aux2.clkr, + [GPLL3] = &gpll3.clkr, + [GPLL3_OUT_MAIN] = &gpll3_out_main.clkr, + [GPLL4] = &gpll4.clkr, + [GPLL5] = &gpll5.clkr, + [GPLL6] = &gpll6.clkr, + [GPLL6_OUT_MAIN] = &gpll6_out_main.clkr, + [GPLL7] = &gpll7.clkr, + [GPLL8] = &gpll8.clkr, + [GPLL8_OUT_MAIN] = &gpll8_out_main.clkr, + [GPLL9] = &gpll9.clkr, + [GPLL9_OUT_MAIN] = &gpll9_out_main.clkr, +}; + +static struct gdsc *gcc_shikra_gdscs[] = { + [GCC_CAMSS_TOP_GDSC] = &gcc_camss_top_gdsc, + [GCC_EMAC0_GDSC] = &gcc_emac0_gdsc, + [GCC_EMAC1_GDSC] = &gcc_emac1_gdsc, + [GCC_PCIE_GDSC] = &gcc_pcie_gdsc, + [GCC_USB20_GDSC] = &gcc_usb20_gdsc, + [GCC_USB30_PRIM_GDSC] = &gcc_usb30_prim_gdsc, + [GCC_VCODEC0_GDSC] = &gcc_vcodec0_gdsc, + [GCC_VENUS_GDSC] = &gcc_venus_gdsc, +}; + +static const struct qcom_reset_map gcc_shikra_resets[] = { + [GCC_CAMSS_OPE_BCR] = { 0x55000 }, + [GCC_CAMSS_TFE_BCR] = { 0x52000 }, + [GCC_CAMSS_TOP_BCR] = { 0x58000 }, + [GCC_EMAC0_BCR] = { 0xad000 }, + [GCC_EMAC1_BCR] = { 0xae000 }, + [GCC_GPU_BCR] = { 0x36000 }, + [GCC_MMSS_BCR] = { 0x17000 }, + [GCC_PCIE_BCR] = { 0xaf000 }, + [GCC_PCIE_PHY_BCR] = { 0xb1000 }, + [GCC_PDM_BCR] = { 0x20000 }, + [GCC_QUPV3_WRAPPER_0_BCR] = { 0x1f000 }, + [GCC_QUSB2PHY_PRIM_BCR] = { 0x1c000 }, + [GCC_QUSB2PHY_SEC_BCR] = { 0x1c004 }, + [GCC_SDCC1_BCR] = { 0x38000 }, + [GCC_SDCC2_BCR] = { 0x1e000 }, + [GCC_TSCSS_BCR] = { 0xac000 }, + [GCC_USB20_BCR] = { 0xb0000 }, + [GCC_USB30_PRIM_BCR] = { 0x1a000 }, + [GCC_USB3PHY_PHY_PRIM_SP0_BCR] = { 0x1b008 }, + [GCC_USB3_DP_PHY_PRIM_BCR] = { 0x1b020 }, + [GCC_USB3_PHY_PRIM_SP0_BCR] = { 0x1b000 }, + [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x1d000 }, + [GCC_VCODEC0_BCR] = { 0x6d034 }, + [GCC_VENUS_BCR] = { 0x6d018 }, + [GCC_VIDEO_INTERFACE_BCR] = { 0x6e000 }, +}; + +static struct clk_alpha_pll *gcc_shikra_plls[] = { + &gpll10, + &gpll11, + &gpll8, + &gpll9, +}; + +static const u32 gcc_shikra_critical_cbcrs[] = { + 0x17008, /* GCC_CAMERA_AHB_CLK */ + 0x17028, /* GCC_CAMERA_XO_CLK */ + 0x1700c, /* GCC_DISP_AHB_CLK */ + 0x1702c, /* GCC_DISP_XO_CLK */ + 0x36004, /* GCC_GPU_CFG_AHB_CLK */ + 0x36100, /* GCC_GPU_IREF_CLK */ + 0x3a00c, /* GCC_LPASS_CONFIG_CLK */ + 0x3a008, /* GCC_LPASS_CORE_AXIM_CLK */ + 0x79004, /* GCC_SYS_NOC_CPUSS_AHB_CLK */ + 0x17004, /* GCC_VIDEO_AHB_CLK */ + 0x17024, /* GCC_VIDEO_XO_CLK */ +}; + +static const struct clk_rcg_dfs_data gcc_shikra_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s7_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s8_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s9_clk_src), +}; + +static const struct regmap_config gcc_shikra_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xc7000, + .fast_io = true, +}; + +static const struct qcom_cc_driver_data gcc_shikra_driver_data = { + .alpha_plls = gcc_shikra_plls, + .num_alpha_plls = ARRAY_SIZE(gcc_shikra_plls), + .clk_cbcrs = gcc_shikra_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(gcc_shikra_critical_cbcrs), + .dfs_rcgs = gcc_shikra_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(gcc_shikra_dfs_clocks), +}; + +static const struct qcom_cc_desc gcc_shikra_desc = { + .config = &gcc_shikra_regmap_config, + .clks = gcc_shikra_clocks, + .num_clks = ARRAY_SIZE(gcc_shikra_clocks), + .resets = gcc_shikra_resets, + .num_resets = ARRAY_SIZE(gcc_shikra_resets), + .gdscs = gcc_shikra_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_shikra_gdscs), + .driver_data = &gcc_shikra_driver_data, +}; + +static const struct of_device_id gcc_shikra_match_table[] = { + { .compatible = "qcom,shikra-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_shikra_match_table); + +static int gcc_shikra_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_shikra_desc); +} + +static struct platform_driver gcc_shikra_driver = { + .probe = gcc_shikra_probe, + .driver = { + .name = "gcc-shikra", + .of_match_table = gcc_shikra_match_table, + }, +}; + +static int __init gcc_shikra_init(void) +{ + return platform_driver_register(&gcc_shikra_driver); +} +subsys_initcall(gcc_shikra_init); + +static void __exit gcc_shikra_exit(void) +{ + platform_driver_unregister(&gcc_shikra_driver); +} +module_exit(gcc_shikra_exit); + +MODULE_DESCRIPTION("QTI GCC Shikra Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-sm6115.c b/drivers/clk/qcom/gcc-sm6115.c index 4c3804701e24..c5251aff9886 100644 --- a/drivers/clk/qcom/gcc-sm6115.c +++ b/drivers/clk/qcom/gcc-sm6115.c @@ -3218,6 +3218,7 @@ static struct gdsc gcc_vcodec0_gdsc = { .pd = { .name = "gcc_vcodec0", }, + .flags = HW_CTRL_TRIGGER, .pwrsts = PWRSTS_OFF_ON, }; diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index f419a28f616b..b99d70149af3 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -104,14 +104,21 @@ static int gdsc_hwctrl(struct gdsc *sc, bool en) static int gdsc_poll_status(struct gdsc *sc, enum gdsc_status status) { ktime_t start; + int ret; start = ktime_get(); do { - if (gdsc_check_status(sc, status)) + ret = gdsc_check_status(sc, status); + if (ret < 0) + return ret; + if (ret) return 0; } while (ktime_us_delta(ktime_get(), start) < STATUS_POLL_TIMEOUT_US); - if (gdsc_check_status(sc, status)) + ret = gdsc_check_status(sc, status); + if (ret < 0) + return ret; + if (ret) return 0; return -ETIMEDOUT; @@ -493,7 +500,9 @@ static int gdsc_init(struct gdsc *sc) } else if (sc->flags & ALWAYS_ON) { /* If ALWAYS_ON GDSCs are not ON, turn them ON */ - gdsc_enable(&sc->pd); + ret = gdsc_enable(&sc->pd); + if (ret) + return ret; on = true; } @@ -669,10 +678,18 @@ err_pm_subdomain_remove: void gdsc_unregister(struct gdsc_desc *desc) { struct device *dev = desc->dev; + struct gdsc **scs = desc->scs; size_t num = desc->num; + int i; - gdsc_pm_subdomain_remove(desc, num); of_genpd_del_provider(dev->of_node); + gdsc_pm_subdomain_remove(desc, num); + + for (i = 0; i < num; i++) { + if (!scs[i]) + continue; + pm_genpd_remove(&scs[i]->pd); + } } /* diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index ddc86551bf57..ac96c46bd1bb 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -7,7 +7,7 @@ */ #include <linux/clk-provider.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h> @@ -35,13 +35,13 @@ static int clk_prcmu_prepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - return prcmu_request_clock(clk->cg_sel, true); + return db8500_prcmu_request_clock(clk->cg_sel, true); } static void clk_prcmu_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) + if (db8500_prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, clk_hw_get_name(hw)); } @@ -86,7 +86,7 @@ static int clk_prcmu_opp_prepare(struct clk_hw *hw) clk->opp_requested = 1; } - err = prcmu_request_clock(clk->cg_sel, true); + err = db8500_prcmu_request_clock(clk->cg_sel, true); if (err) { prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, (char *)clk_hw_get_name(hw)); @@ -101,7 +101,7 @@ static void clk_prcmu_opp_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) { + if (db8500_prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, clk_hw_get_name(hw)); return; @@ -120,7 +120,7 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) struct clk_prcmu *clk = to_clk_prcmu(hw); if (!clk->opp_requested) { - err = prcmu_request_ape_opp_100_voltage(true); + err = db8500_prcmu_request_ape_opp_100_voltage(true); if (err) { pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", __func__, clk_hw_get_name(hw)); @@ -129,9 +129,9 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) clk->opp_requested = 1; } - err = prcmu_request_clock(clk->cg_sel, true); + err = db8500_prcmu_request_clock(clk->cg_sel, true); if (err) { - prcmu_request_ape_opp_100_voltage(false); + db8500_prcmu_request_ape_opp_100_voltage(false); clk->opp_requested = 0; return err; } @@ -143,14 +143,14 @@ static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) { + if (db8500_prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, clk_hw_get_name(hw)); return; } if (clk->opp_requested) { - prcmu_request_ape_opp_100_voltage(false); + db8500_prcmu_request_ape_opp_100_voltage(false); clk->opp_requested = 0; } } diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index 6f78808387b1..d2499815226f 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -9,7 +9,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/clk-provider.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include "clk.h" #include "prcc.h" diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index f07b73694cec..0d0df986fa3d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2590,6 +2590,9 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy) cpu = cpumask_first(policy->related_cpus); max_freq = arch_scale_freq_ref(cpu); + if (!max_freq) + max_freq = policy->cpuinfo.max_freq; + capped_freq = policy->max; /* diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 2f048929ff6b..2cbf810db66e 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1048,12 +1048,14 @@ static void hybrid_clear_cpu_capacity(unsigned int cpunum) static void hybrid_get_capacity_perf(struct cpudata *cpu) { + u64 hwp_cap = READ_ONCE(cpu->hwp_cap_cached); + if (READ_ONCE(global.no_turbo)) { - cpu->capacity_perf = cpu->pstate.max_pstate_physical; + cpu->capacity_perf = HWP_GUARANTEED_PERF(hwp_cap); return; } - cpu->capacity_perf = HWP_HIGHEST_PERF(READ_ONCE(cpu->hwp_cap_cached)); + cpu->capacity_perf = HWP_HIGHEST_PERF(hwp_cap); } static void hybrid_set_capacity_of_cpus(void) diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index d6d8386d3f02..00e2562041fd 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -71,6 +71,11 @@ depends on RISCV source "drivers/cpuidle/Kconfig.riscv" endmenu +menu "S390 CPU Idle Drivers" +depends on S390 +source "drivers/cpuidle/Kconfig.s390" +endmenu + config HALTPOLL_CPUIDLE tristate "Halt poll cpuidle driver" depends on X86 && KVM_GUEST diff --git a/drivers/cpuidle/Kconfig.s390 b/drivers/cpuidle/Kconfig.s390 new file mode 100644 index 000000000000..c9acf7ba8eee --- /dev/null +++ b/drivers/cpuidle/Kconfig.s390 @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# S390 CPU Idle drivers +# + +config S390_CPUIDLE + bool "S390 CPU idle driver" + default y + help + Select this option to enable processor idle state management + through cpuidle subsystem. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 1de9e92c5b0f..88cbc2a7aea8 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -42,3 +42,7 @@ obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o ############################################################################### # RISC-V drivers obj-$(CONFIG_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o + +############################################################################### +# S390 drivers +obj-$(CONFIG_S390_CPUIDLE) += cpuidle-s390.o diff --git a/drivers/cpuidle/cpuidle-s390.c b/drivers/cpuidle/cpuidle-s390.c new file mode 100644 index 000000000000..1d02a77ebce0 --- /dev/null +++ b/drivers/cpuidle/cpuidle-s390.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * s390 generic CPU idle driver. + * + * Copyright IBM Corp. 2026 + */ + +#define pr_fmt(fmt) "CPUidle s390: " fmt + +#include <linux/init.h> +#include <linux/cpuidle.h> +#include <linux/cpu.h> +#include <linux/sched/clock.h> + +static __cpuidle int s390_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + arch_cpu_idle(); + return index; +} + +static struct cpuidle_driver s390_cpuidle_driver = { + .cpumask = (struct cpumask *)cpu_present_mask, + .name = "s390-idle", + .states = { + { /* entry 0 is for polling */}, + { + .enter = s390_enter_idle, + .name = "IDLE", + .desc = "ENABLED WAIT", + }, + }, + .safe_state_index = 0, + .state_count = 2, +}; + +static int s390_cpuidle_cpu_online(unsigned int cpu) +{ + struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); + int rc; + + if (dev->registered) { + cpuidle_pause_and_lock(); + rc = cpuidle_enable_device(dev); + cpuidle_resume_and_unlock(); + if (rc) + pr_err("Failed to enable cpuidle device on cpu %u\n", cpu); + } else { + dev->cpu = cpu; + rc = cpuidle_register_device(dev); + if (rc) + pr_err("Failed to register cpuidle driver on cpu %u\n", cpu); + } + return rc; +} + +static int s390_cpuidle_cpu_dead(unsigned int cpu) +{ + struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu); + + if (!dev->registered) + return 0; + cpuidle_pause_and_lock(); + cpuidle_disable_device(dev); + cpuidle_resume_and_unlock(); + return 0; +} + +/* + * The target_residency and exit_latency values are benchmark-derived estimates + * that remain non-deterministic due to s390's virtualized architecture. + * + * Configuration strategy: + * - Poll idle state: Values derived from the next enabled idle state (EW) + * - Enabled Wait state: Values selected based on idle behavior and empirical + * measurement data + * + * Goal is to improve responsiveness for workloads with frequent sleep/wakeup + * cycles while minimizing any side effects. + */ +static void __init s390_cpuidle_ew_tune(void) +{ + struct cpuidle_state *state = &s390_cpuidle_driver.states[1]; + + if (machine_is_lpar()) { + state->target_residency = 5; + state->exit_latency = 5; + } else { + state->target_residency = 1; + state->exit_latency = 1; + } +} + +static int __init s390_cpuidle_init(void) +{ + int rc; + + s390_cpuidle_ew_tune(); + cpuidle_poll_state_init(&s390_cpuidle_driver); + rc = cpuidle_register(&s390_cpuidle_driver, NULL); + if (rc) + return rc; + rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, + "cpuidle/s390:online", + s390_cpuidle_cpu_online, + s390_cpuidle_cpu_dead); + if (rc < 0) { + cpuidle_unregister(&s390_cpuidle_driver); + pr_err("Failed to allocate hotplug state: cpuidle/s390:online\n"); + return rc; + } + return 0; +} +device_initcall(s390_cpuidle_init); diff --git a/drivers/cpuidle/cpuidle-ux500.c b/drivers/cpuidle/cpuidle-ux500.c index f7d778580e9b..6d6c52c0bcc2 100644 --- a/drivers/cpuidle/cpuidle-ux500.c +++ b/drivers/cpuidle/cpuidle-ux500.c @@ -11,7 +11,7 @@ #include <linux/spinlock.h> #include <linux/atomic.h> #include <linux/smp.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/platform_data/arm-ux500-pm.h> #include <linux/platform_device.h> @@ -66,7 +66,7 @@ static inline int ux500_enter_idle(struct cpuidle_device *dev, /* Go to the retention state, the prcmu will wait for the * cpu to go WFI and this is what happens after exiting this * 'master' critical section */ - if (prcmu_set_power_state(PRCMU_AP_IDLE, true, true)) + if (db8500_prcmu_set_power_state(PRCMU_AP_IDLE, true, true)) goto out; /* When we switch to retention, the prcmu is in charge @@ -109,7 +109,7 @@ static struct cpuidle_driver ux500_idle_driver = { static int dbx500_cpuidle_probe(struct platform_device *pdev) { /* Configure wake up reasons */ - prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) | + db8500_prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) | PRCMU_WAKEUP(ABB)); return cpuidle_register(&ux500_idle_driver, NULL); diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index ec3b48cb0e87..0f2e09311152 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -254,6 +254,11 @@ static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok, read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } + if ((u64)offset + size > rmb_node->len) { + read_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; + } + memcpy((char *)rmb_node->cpu_addr + offset, data, size); sba_idx = rmb_node->sba_idx; read_unlock_bh(&ldev->dmb_ht_lock); diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 7e3c9f38297b..e735f43dcdeb 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -210,3 +210,4 @@ module_platform_driver(adc_jack_driver); MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); MODULE_DESCRIPTION("ADC Jack extcon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index e3fb36825978..783c24a20e29 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -96,7 +96,7 @@ config ARM_SCMI_POWER_CONTROL firmware. This driver can also be built as a module. If so, the module will be - called scmi_power_control. Note this may needed early in boot to catch - early shutdown/reboot SCMI requests. + called scmi_power_control. Note this may be needed early in boot to + catch early shutdown/reboot SCMI requests. endmenu diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 42e666a628c7..0278705d809e 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -718,7 +718,7 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph, u32 clk_id, unsigned long *rate) { - u64 fmin, fmax, ftmp; + u64 fmin, fmax, ftmp, step; struct scmi_clock_info *clk; struct scmi_clock_desc *clkd; struct clock_info *ci = ph->get_priv(ph); @@ -749,11 +749,14 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph, return 0; } + step = clkd->r.rates[RATE_STEP]; + if (!step) + return -EINVAL; + ftmp = *rate - fmin; - ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */ - ftmp = div64_ul(ftmp, clkd->r.rates[RATE_STEP]); + ftmp = DIV64_U64_ROUND_UP(ftmp, step); - *rate = ftmp * clkd->r.rates[RATE_STEP] + fmin; + *rate = ftmp * step + fmin; return 0; } diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 40ec184eedae..3cf706aacc11 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -209,11 +209,11 @@ struct scmi_registered_events_desc; * @init_work: A work item to perform final initializations of pending handlers * @notify_wq: A reference to the allocated Kernel cmwq * @pending_mtx: A mutex to protect @pending_events_handlers + * @pending_events_handlers: An hashtable containing all pending events' + * handlers descriptors * @registered_protocols: A statically allocated array containing pointers to * all the registered protocol-level specific information * related to events' handling - * @pending_events_handlers: An hashtable containing all pending events' - * handlers descriptors * * Each platform instance, represented by a handle, has its own instance of * the notification subsystem represented by this structure. @@ -225,8 +225,8 @@ struct scmi_notify_instance { struct workqueue_struct *notify_wq; /* lock to protect pending_events_handlers */ struct mutex pending_mtx; - struct scmi_registered_events_desc **registered_protocols; DECLARE_HASHTABLE(pending_events_handlers, SCMI_PENDING_HASH_SZ); + struct scmi_registered_events_desc *registered_protocols[SCMI_MAX_PROTO]; }; /** @@ -276,13 +276,13 @@ struct scmi_registered_event; * @eh_sz: Size of the pre-allocated buffer @eh * @in_flight: A reference to an in flight &struct scmi_registered_event * @num_events: Number of events in @registered_events - * @registered_events: A dynamically allocated array holding all the registered - * events' descriptors, whose fixed-size is determined at - * compile time. * @registered_mtx: A mutex to protect @registered_events_handlers * @ph: SCMI protocol handle reference * @registered_events_handlers: An hashtable containing all events' handlers * descriptors registered for this protocol + * @registered_events: A dynamically allocated array holding all the registered + * events' descriptors, whose fixed-size is determined at + * compile time. * * All protocols that register at least one event have their protocol-specific * information stored here, together with the embedded allocated events_queue. @@ -302,11 +302,11 @@ struct scmi_registered_events_desc { size_t eh_sz; void *in_flight; int num_events; - struct scmi_registered_event **registered_events; /* mutex to protect registered_events_handlers */ struct mutex registered_mtx; const struct scmi_protocol_handle *ph; DECLARE_HASHTABLE(registered_events_handlers, SCMI_REGISTERED_HASH_SZ); + struct scmi_registered_event *registered_events[] __counted_by(num_events); }; /** @@ -338,9 +338,9 @@ struct scmi_registered_event { void *report; u32 num_sources; bool not_supported_by_platform; - refcount_t *sources; /* locking to serialize the access to sources */ struct mutex sources_mtx; + refcount_t sources[] __counted_by(num_sources); }; /** @@ -600,9 +600,9 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, return -EINVAL; } if (kfifo_avail(&r_evt->proto->equeue.kfifo) < sizeof(eh) + len) { - dev_warn(handle->dev, - "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n", - proto_id, evt_id, ktime_to_ns(ts)); + dev_warn_ratelimited(handle->dev, + "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n", + proto_id, evt_id, ktime_to_ns(ts)); return -ENOMEM; } @@ -706,9 +706,13 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni, if (WARN_ON(ni->registered_protocols[proto_id])) return ERR_PTR(-EINVAL); - pd = devm_kzalloc(ni->handle->dev, sizeof(*pd), GFP_KERNEL); + pd = devm_kzalloc(ni->handle->dev, + struct_size(pd, registered_events, num_events), + GFP_KERNEL); if (!pd) return ERR_PTR(-ENOMEM); + + pd->num_events = num_events; pd->id = proto_id; pd->ops = ops; pd->ni = ni; @@ -722,12 +726,6 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni, return ERR_PTR(-ENOMEM); pd->eh_sz = eh_sz; - pd->registered_events = devm_kcalloc(ni->handle->dev, num_events, - sizeof(char *), GFP_KERNEL); - if (!pd->registered_events) - return ERR_PTR(-ENOMEM); - pd->num_events = num_events; - /* Initialize per protocol handlers table */ mutex_init(&pd->registered_mtx); hash_init(pd->registered_events_handlers); @@ -796,18 +794,16 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, int id; struct scmi_registered_event *r_evt; - r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt), + r_evt = devm_kzalloc(ni->handle->dev, + struct_size(r_evt, sources, num_sources), GFP_KERNEL); if (!r_evt) return -ENOMEM; + + r_evt->num_sources = num_sources; r_evt->proto = pd; r_evt->evt = evt; - r_evt->sources = devm_kcalloc(ni->handle->dev, num_sources, - sizeof(refcount_t), GFP_KERNEL); - if (!r_evt->sources) - return -ENOMEM; - r_evt->num_sources = num_sources; mutex_init(&r_evt->sources_mtx); r_evt->report = devm_kzalloc(ni->handle->dev, @@ -1673,11 +1669,6 @@ int scmi_notification_init(struct scmi_handle *handle) ni->gid = gid; ni->handle = handle; - ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO, - sizeof(char *), GFP_KERNEL); - if (!ni->registered_protocols) - goto err; - ni->notify_wq = alloc_workqueue(dev_name(handle->dev), WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, 0); diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index da8d29621644..591a725b1061 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -119,6 +119,14 @@ union efi_rts_args { struct efi_runtime_work efi_rts_work; /* + * Upper bound on how long we wait for a single EFI runtime service + * call to finish before declaring firmware wedged. Chosen to be longer + * than any plausible legitimate call (including UpdateCapsule on slow + * SPI-NOR) while still bounding userspace wait time. + */ +#define EFI_RTS_TIMEOUT (120 * HZ) + +/* * efi_queue_work: Queue EFI runtime service call and wait for completion * @_rts: EFI runtime service function identifier * @_args: Arguments to pass to the EFI runtime service @@ -212,6 +220,19 @@ extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); #endif /* + * Park a worker that must never run efi_rts_wq again: EFI runtime services + * have been disabled and its efi_rts_work is abandoned. Loop in schedule() + * so a spurious wakeup cannot resume it. + */ +void efi_rts_park_worker(void) +{ + for (;;) { + set_current_state(TASK_IDLE); + schedule(); + } +} + +/* * Calls the appropriate efi_runtime_service() with the appropriate * arguments. */ @@ -221,6 +242,9 @@ static void __nocfi efi_call_rts(struct work_struct *work) efi_status_t status = EFI_NOT_FOUND; unsigned long flags; + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + efi_rts_park_worker(); + efi_runtime_lock_owner = current; arch_efi_call_virt_setup(); @@ -312,6 +336,9 @@ static void __nocfi efi_call_rts(struct work_struct *work) efi_call_virt_check_flags(flags, efi_rts_work.caller); arch_efi_call_virt_teardown(); + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + efi_rts_park_worker(); + efi_rts_work.status = status; complete(&efi_rts_work.efi_rts_comp); efi_runtime_lock_owner = NULL; @@ -320,17 +347,16 @@ static void __nocfi efi_call_rts(struct work_struct *work) static efi_status_t __efi_queue_work(enum efi_rts_ids id, union efi_rts_args *args) { + if (!efi_enabled(EFI_RUNTIME_SERVICES)) { + pr_warn_once("EFI Runtime Services are disabled!\n"); + return EFI_DEVICE_ERROR; + } + efi_rts_work.efi_rts_id = id; efi_rts_work.args = args; efi_rts_work.caller = __builtin_return_address(0); efi_rts_work.status = EFI_ABORTED; - if (!efi_enabled(EFI_RUNTIME_SERVICES)) { - pr_warn_once("EFI Runtime Services are disabled!\n"); - efi_rts_work.status = EFI_DEVICE_ERROR; - goto exit; - } - init_completion(&efi_rts_work.efi_rts_comp); INIT_WORK(&efi_rts_work.work, efi_call_rts); @@ -338,10 +364,18 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id, * queue_work() returns 0 if work was already on queue, * _ideally_ this should never happen. */ - if (queue_work(efi_rts_wq, &efi_rts_work.work)) - wait_for_completion(&efi_rts_work.efi_rts_comp); - else + if (!queue_work(efi_rts_wq, &efi_rts_work.work)) { pr_err("Failed to queue work to efi_rts_wq.\n"); + goto exit; + } + + if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp, + EFI_RTS_TIMEOUT)) { + pr_err("EFI runtime service %d wedged in firmware; disabling EFI runtime services\n", + id); + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); + return EFI_ABORTED; + } WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); exit: @@ -449,6 +483,11 @@ virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr, if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; + if (!efi_enabled(EFI_RUNTIME_SERVICES)) { + up(&efi_runtime_lock); + return EFI_DEVICE_ERROR; + } + efi_runtime_lock_owner = current; status = efi_call_virt_pointer(efi.runtime, set_variable, name, vendor, attr, data_size, data); @@ -488,6 +527,11 @@ virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space, if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; + if (!efi_enabled(EFI_RUNTIME_SERVICES)) { + up(&efi_runtime_lock); + return EFI_DEVICE_ERROR; + } + efi_runtime_lock_owner = current; status = efi_call_virt_pointer(efi.runtime, query_variable_info, attr, storage_space, remaining_space, @@ -518,6 +562,12 @@ virt_efi_reset_system(int reset_type, efi_status_t status, return; } + if (!efi_enabled(EFI_RUNTIME_SERVICES)) { + pr_warn("EFI Runtime Services are disabled, not invoking reset_system()\n"); + up(&efi_runtime_lock); + return; + } + efi_runtime_lock_owner = current; arch_efi_call_virt_setup(); efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM; diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index b477d54b495a..c7f8413ab996 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -6,9 +6,29 @@ menu "Qualcomm firmware drivers" +config QCOM_PAS + tristate "Qualcomm generic PAS interface driver" + help + Enable the generic Peripheral Authentication Service (PAS) provided + by the firmware. It acts as the common layer with different TZ + backends plugged in whether it's an SCM implementation or a proper + TEE bus based PAS service implementation. + +config QCOM_PAS_TEE + tristate "Qualcomm PAS TEE interface driver" + select QCOM_PAS + depends on TEE + depends on !CPU_BIG_ENDIAN + default m if ARCH_QCOM + help + Enable the generic Peripheral Authentication Service (PAS) provided + by the firmware TEE implementation as the backend. + config QCOM_SCM + tristate "Qualcomm PAS SCM interface driver" + select QCOM_PAS select QCOM_TZMEM - tristate + default y if ARCH_QCOM config QCOM_TZMEM tristate diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile index 0be40a1abc13..48801d18f37b 100644 --- a/drivers/firmware/qcom/Makefile +++ b/drivers/firmware/qcom/Makefile @@ -8,3 +8,5 @@ qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o +obj-$(CONFIG_QCOM_PAS) += qcom_pas.o +obj-$(CONFIG_QCOM_PAS_TEE) += qcom_pas_tee.o diff --git a/drivers/firmware/qcom/qcom_pas.c b/drivers/firmware/qcom/qcom_pas.c new file mode 100644 index 000000000000..24485dd0fa10 --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/device/devres.h> +#include <linux/firmware/qcom/qcom_pas.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "qcom_pas.h" + +static struct qcom_pas_ops *ops_ptr; + +/** + * devm_qcom_pas_context_alloc() - Allocate peripheral authentication service + * context for a given peripheral + * + * PAS context is device-resource managed, so the caller does not need + * to worry about freeing the context memory. + * + * @dev: PAS firmware device + * @pas_id: peripheral authentication service id + * @mem_phys: Subsystem reserve memory start address + * @mem_size: Subsystem reserve memory size + * + * Return: The new PAS context, or ERR_PTR() on failure. + */ +struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size) +{ + struct qcom_pas_context *ctx; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->dev = dev; + ctx->pas_id = pas_id; + ctx->mem_phys = mem_phys; + ctx->mem_size = mem_size; + + return ctx; +} +EXPORT_SYMBOL_GPL(devm_qcom_pas_context_alloc); + +/** + * qcom_pas_init_image() - Initialize peripheral authentication service state + * machine for a given peripheral, using the metadata + * @pas_id: peripheral authentication service id + * @metadata: pointer to memory containing ELF header, program header table + * and optional blob of data used for authenticating the metadata + * and the rest of the firmware + * @size: size of the metadata + * @ctx: optional pas context + * + * Return: 0 on success. + * + * Upon successful return, the PAS metadata context (@ctx) will be used to + * track the metadata allocation, this needs to be released by invoking + * qcom_pas_metadata_release() by the caller. + */ +int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_pas_context *ctx) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->init_image(ops_ptr->dev, pas_id, metadata, size, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_init_image); + +/** + * qcom_pas_metadata_release() - release metadata context + * @ctx: pas context + */ +void qcom_pas_metadata_release(struct qcom_pas_context *ctx) +{ + if (!ops_ptr || !ctx || !ctx->ptr) + return; + + ops_ptr->metadata_release(ops_ptr->dev, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_metadata_release); + +/** + * qcom_pas_mem_setup() - Prepare the memory related to a given peripheral + * for firmware loading + * @pas_id: peripheral authentication service id + * @addr: start address of memory area to prepare + * @size: size of the memory area to prepare + * + * Return: 0 on success. + */ +int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size); +} +EXPORT_SYMBOL_GPL(qcom_pas_mem_setup); + +/** + * qcom_pas_get_rsc_table() - Retrieve the resource table in passed output buffer + * for a given peripheral. + * + * Qualcomm remote processor may rely on both static and dynamic resources for + * its functionality. Static resources typically refer to memory-mapped + * addresses required by the subsystem and are often embedded within the + * firmware binary and dynamic resources, such as shared memory in DDR etc., + * are determined at runtime during the boot process. + * + * On Qualcomm Technologies devices, it's possible that static resources are + * not embedded in the firmware binary and instead are provided by TrustZone. + * However, dynamic resources are always expected to come from TrustZone. This + * indicates that for Qualcomm devices, all resources (static and dynamic) will + * be provided by TrustZone PAS service. + * + * If the remote processor firmware binary does contain static resources, they + * should be passed in input_rt. These will be forwarded to TrustZone for + * authentication. TrustZone will then append the dynamic resources and return + * the complete resource table in output_rt_tzm. + * + * If the remote processor firmware binary does not include a resource table, + * the caller of this function should set input_rt as NULL and input_rt_size + * as zero respectively. + * + * More about documentation on resource table data structures can be found in + * include/linux/remoteproc.h + * + * @ctx: PAS context + * @input_rt: resource table buffer which is present in firmware binary + * @input_rt_size: size of the resource table present in firmware binary + * @output_rt_size: TrustZone expects caller should pass worst case size for + * the output_rt_tzm. + * + * Return: + * On success, returns a pointer to the allocated buffer containing the final + * resource table and output_rt_size will have actual resource table size from + * TrustZone. The caller is responsible for freeing the buffer. On failure, + * returns ERR_PTR(-errno). + */ +struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) +{ + if (!ops_ptr) + return ERR_PTR(-ENODEV); + if (!ctx) + return ERR_PTR(-EINVAL); + + return ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt, + input_rt_size, output_rt_size); +} +EXPORT_SYMBOL_GPL(qcom_pas_get_rsc_table); + +/** + * qcom_pas_auth_and_reset() - Authenticate the given peripheral firmware + * and reset the remote processor + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_auth_and_reset(u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->auth_and_reset(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_auth_and_reset); + +/** + * qcom_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the + * remote processor + * + * @ctx: Context saved during call to devm_qcom_pas_context_alloc() + * + * This function performs the necessary steps to prepare a PAS subsystem, + * authenticate it using the provided metadata, and initiate a reset sequence. + * + * It should be used when Linux is in control setting up the IOMMU hardware + * for remote subsystem during secure firmware loading processes. The + * preparation step sets up a shmbridge over the firmware memory before + * TrustZone accesses the firmware memory region for authentication. The + * authentication step verifies the integrity and authenticity of the firmware + * or configuration using secure metadata. Finally, the reset step ensures the + * subsystem starts in a clean and sane state. + * + * Return: 0 on success, negative errno on failure. + */ +int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx) +{ + if (!ops_ptr) + return -ENODEV; + if (!ctx) + return -EINVAL; + + return ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_prepare_and_auth_reset); + +/** + * qcom_pas_set_remote_state() - Set the remote processor state + * @state: peripheral state + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_set_remote_state(u32 state, u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_set_remote_state); + +/** + * qcom_pas_shutdown() - Shut down the remote processor + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_shutdown(u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->shutdown(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_shutdown); + +/** + * qcom_pas_supported() - Check if the peripheral authentication service is + * supported for the given peripheral + * @pas_id: peripheral authentication service id + * + * Return: true if PAS is supported for this peripheral, otherwise false. + */ +bool qcom_pas_supported(u32 pas_id) +{ + if (!ops_ptr) + return false; + + return ops_ptr->supported(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_supported); + +/** + * qcom_pas_is_available() - Check if the peripheral authentication service is + * available. Note that it is mandatory for any PAS + * client to invoke this API. If it returns true then + * only any other PAS API can be invoked. + * + * Return: true if PAS is available, otherwise false. + */ +bool qcom_pas_is_available(void) +{ + /* + * The barrier for ops_ptr is intended to synchronize the data stores + * for the ops data structure when client drivers are in parallel + * checking for PAS service availability. + * + * Once the PAS backend becomes available, it is allowed for multiple + * threads to enter TZ for parallel bringup of co-processors during + * boot. + */ + return !!smp_load_acquire(&ops_ptr); +} +EXPORT_SYMBOL_GPL(qcom_pas_is_available); + +void qcom_pas_ops_register(struct qcom_pas_ops *ops) +{ + if (!qcom_pas_is_available()) + /* Paired with smp_load_acquire() in qcom_pas_is_available() */ + smp_store_release(&ops_ptr, ops); + else + pr_err("qcom_pas: ops already registered by %s\n", + ops_ptr->drv_name); +} +EXPORT_SYMBOL_GPL(qcom_pas_ops_register); + +void qcom_pas_ops_unregister(void) +{ + /* Paired with smp_load_acquire() in qcom_pas_is_available() */ + smp_store_release(&ops_ptr, NULL); +} +EXPORT_SYMBOL_GPL(qcom_pas_ops_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm generic TZ PAS driver"); diff --git a/drivers/firmware/qcom/qcom_pas.h b/drivers/firmware/qcom/qcom_pas.h new file mode 100644 index 000000000000..8643e2760602 --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_PAS_INT_H +#define __QCOM_PAS_INT_H + +struct device; + +/** + * struct qcom_pas_ops - Qcom Peripheral Authentication Service (PAS) ops + * @drv_name: PAS driver name. + * @dev: PAS device pointer. + * @supported: Peripheral supported callback. + * @init_image: Peripheral image initialization callback. + * @mem_setup: Peripheral memory setup callback. + * @get_rsc_table: Peripheral get resource table callback. + * @prepare_and_auth_reset: Peripheral prepare firmware authentication and + * reset callback. + * @auth_and_reset: Peripheral firmware authentication and reset + * callback. + * @set_remote_state: Peripheral set remote state callback. + * @shutdown: Peripheral shutdown callback. + * @metadata_release: Image metadata release callback. + */ +struct qcom_pas_ops { + const char *drv_name; + struct device *dev; + bool (*supported)(struct device *dev, u32 pas_id); + int (*init_image)(struct device *dev, u32 pas_id, const void *metadata, + size_t size, struct qcom_pas_context *ctx); + int (*mem_setup)(struct device *dev, u32 pas_id, phys_addr_t addr, + phys_addr_t size); + void *(*get_rsc_table)(struct device *dev, struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size); + int (*prepare_and_auth_reset)(struct device *dev, + struct qcom_pas_context *ctx); + int (*auth_and_reset)(struct device *dev, u32 pas_id); + int (*set_remote_state)(struct device *dev, u32 state, u32 pas_id); + int (*shutdown)(struct device *dev, u32 pas_id); + void (*metadata_release)(struct device *dev, + struct qcom_pas_context *ctx); +}; + +void qcom_pas_ops_register(struct qcom_pas_ops *ops); +void qcom_pas_ops_unregister(void); + +#endif /* __QCOM_PAS_INT_H */ diff --git a/drivers/firmware/qcom/qcom_pas_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c new file mode 100644 index 000000000000..ac33a00687aa --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas_tee.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/firmware/qcom/qcom_pas.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/tee_drv.h> +#include <linux/uuid.h> + +#include "qcom_pas.h" + +/* + * Peripheral Authentication Service (PAS) supported. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define TA_QCOM_PAS_IS_SUPPORTED 1 + +/* + * PAS capabilities. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [out] params[1].value.a: PAS capability flags + */ +#define TA_QCOM_PAS_CAPABILITIES 2 + +/* + * PAS image initialization. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].memref: Loadable firmware metadata + */ +#define TA_QCOM_PAS_INIT_IMAGE 3 + +/* + * PAS memory setup. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Relocatable firmware size + * [in] params[1].value.a: 32bit LSB relocatable firmware memory address + * [in] params[1].value.b: 32bit MSB relocatable firmware memory address + */ +#define TA_QCOM_PAS_MEM_SETUP 4 + +/* + * PAS get resource table. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [inout] params[1].memref: Resource table config + */ +#define TA_QCOM_PAS_GET_RESOURCE_TABLE 5 + +/* + * PAS image authentication and co-processor reset. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Firmware size + * [in] params[1].value.a: 32bit LSB firmware memory address + * [in] params[1].value.b: 32bit MSB firmware memory address + * [in] params[2].memref: Optional fw memory space shared/lent + */ +#define TA_QCOM_PAS_AUTH_AND_RESET 6 + +/* + * PAS co-processor set suspend/resume state. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Co-processor state identifier + */ +#define TA_QCOM_PAS_SET_REMOTE_STATE 7 + +/* + * PAS co-processor shutdown. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define TA_QCOM_PAS_SHUTDOWN 8 + +#define TEE_NUM_PARAMS 4 + +/** + * struct qcom_pas_tee_private - PAS service private data + * @dev: PAS service device. + * @ctx: TEE context handler. + * @session_id: PAS TA session identifier. + */ +struct qcom_pas_tee_private { + struct device *dev; + struct tee_context *ctx; + u32 session_id; +}; + +static bool qcom_pas_tee_supported(struct device *dev, u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_IS_SUPPORTED, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS not supported, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return false; + } + + return true; +} + +static int qcom_pas_tee_init_image(struct device *dev, u32 pas_id, + const void *metadata, size_t size, + struct qcom_pas_context *ctx) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_INIT_IMAGE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + } + }; + struct tee_shm *mdata_shm; + u8 *mdata_buf = NULL; + int ret; + + mdata_shm = tee_shm_alloc_kernel_buf(data->ctx, size); + if (IS_ERR(mdata_shm)) { + dev_err(dev, "mdata_shm allocation failed\n"); + return PTR_ERR(mdata_shm); + } + + mdata_buf = tee_shm_get_va(mdata_shm, 0); + if (IS_ERR(mdata_buf)) { + dev_err(dev, "mdata_buf get VA failed\n"); + tee_shm_free(mdata_shm); + return PTR_ERR(mdata_buf); + } + memcpy(mdata_buf, metadata, size); + + param[1].u.memref.shm = mdata_shm; + param[1].u.memref.size = size; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS init image failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + tee_shm_free(mdata_shm); + return ret ?: -EINVAL; + } + + if (ctx) + ctx->ptr = (void *)mdata_shm; + else + tee_shm_free(mdata_shm); + + return ret; +} + +static int qcom_pas_tee_mem_setup(struct device *dev, u32 pas_id, + phys_addr_t addr, phys_addr_t size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_MEM_SETUP, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = size, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = lower_32_bits(addr), + .u.value.b = upper_32_bits(addr), + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS mem setup failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +DEFINE_FREE(shm_free, struct tee_shm *, tee_shm_free(_T)) + +static void *qcom_pas_tee_get_rsc_table(struct device *dev, + struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_GET_RESOURCE_TABLE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = ctx->pas_id, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, + .u.memref.size = input_rt_size, + } + }; + void *rt_buf = NULL; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n", + ctx->pas_id, ret, inv_arg.ret); + return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL); + } + + if (param[1].u.memref.size >= input_rt_size) { + struct tee_shm *rt_shm __free(shm_free) = + tee_shm_alloc_kernel_buf(data->ctx, + param[1].u.memref.size); + void *rt_shm_va; + + if (IS_ERR_OR_NULL(rt_shm)) { + dev_err(dev, "rt_shm allocation failed\n"); + rt_shm = NULL; + return ERR_PTR(-ENOMEM); + } + + rt_shm_va = tee_shm_get_va(rt_shm, 0); + if (IS_ERR(rt_shm_va)) { + dev_err(dev, "rt_shm get VA failed\n"); + return ERR_CAST(rt_shm_va); + } + memcpy(rt_shm_va, input_rt, input_rt_size); + + param[1].u.memref.shm = rt_shm; + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n", + ctx->pas_id, ret, inv_arg.ret); + return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL); + } + + if (param[1].u.memref.size) { + *output_rt_size = param[1].u.memref.size; + rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL); + if (!rt_buf) + return ERR_PTR(-ENOMEM); + } + } else { + *output_rt_size = 0; + } + + return rt_buf; +} + +static int __qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id, + phys_addr_t mem_phys, size_t mem_size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_AUTH_AND_RESET, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = mem_size, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = lower_32_bits(mem_phys), + .u.value.b = upper_32_bits(mem_phys), + }, + /* Reserved for fw memory space to be shared or lent */ + [2] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS auth reset failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static int qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id) +{ + return __qcom_pas_tee_auth_and_reset(dev, pas_id, 0, 0); +} + +static int qcom_pas_tee_prepare_and_auth_reset(struct device *dev, + struct qcom_pas_context *ctx) +{ + return __qcom_pas_tee_auth_and_reset(dev, ctx->pas_id, ctx->mem_phys, + ctx->mem_size); +} + +static int qcom_pas_tee_set_remote_state(struct device *dev, u32 state, + u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_SET_REMOTE_STATE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = state, + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS set remote state failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static int qcom_pas_tee_shutdown(struct device *dev, u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_SHUTDOWN, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS shutdown failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static void qcom_pas_tee_metadata_release(struct device *dev, + struct qcom_pas_context *ctx) +{ + struct tee_shm *mdata_shm = ctx->ptr; + + tee_shm_free(mdata_shm); + ctx->ptr = NULL; +} + +static struct qcom_pas_ops qcom_pas_ops_tee = { + .drv_name = "qcom-pas-tee", + .supported = qcom_pas_tee_supported, + .init_image = qcom_pas_tee_init_image, + .mem_setup = qcom_pas_tee_mem_setup, + .get_rsc_table = qcom_pas_tee_get_rsc_table, + .auth_and_reset = qcom_pas_tee_auth_and_reset, + .prepare_and_auth_reset = qcom_pas_tee_prepare_and_auth_reset, + .set_remote_state = qcom_pas_tee_set_remote_state, + .shutdown = qcom_pas_tee_shutdown, + .metadata_release = qcom_pas_tee_metadata_release, +}; + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_OPTEE; +} + +static int qcom_pas_tee_probe(struct tee_client_device *pas_dev) +{ + struct device *dev = &pas_dev->dev; + struct qcom_pas_tee_private *data; + struct tee_ioctl_open_session_arg sess_arg = { + .clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL + }; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR(data->ctx)) + return -ENODEV; + + export_uuid(sess_arg.uuid, &pas_dev->id.uuid); + ret = tee_client_open_session(data->ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, ret: %d, err: 0x%x\n", + ret, sess_arg.ret); + tee_client_close_context(data->ctx); + return ret ?: -EINVAL; + } + + data->session_id = sess_arg.session; + dev_set_drvdata(dev, data); + qcom_pas_ops_tee.dev = dev; + qcom_pas_ops_register(&qcom_pas_ops_tee); + + return ret; +} + +static void qcom_pas_tee_remove(struct tee_client_device *pas_dev) +{ + struct device *dev = &pas_dev->dev; + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + + qcom_pas_ops_unregister(); + tee_client_close_session(data->ctx, data->session_id); + tee_client_close_context(data->ctx); +} + +static const struct tee_client_device_id qcom_pas_tee_id_table[] = { + {UUID_INIT(0xcff7d191, 0x7ca0, 0x4784, + 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe)}, + {} +}; +MODULE_DEVICE_TABLE(tee, qcom_pas_tee_id_table); + +static struct tee_client_driver optee_pas_tee_driver = { + .probe = qcom_pas_tee_probe, + .remove = qcom_pas_tee_remove, + .id_table = qcom_pas_tee_id_table, + .driver = { + .name = "qcom-pas-tee", + }, +}; + +module_tee_client_driver(optee_pas_tee_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm PAS TEE driver"); diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 6b601a4b89db..7933e55803dc 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -13,6 +13,7 @@ #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/export.h> +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/firmware/qcom/qcom_scm.h> #include <linux/firmware/qcom/qcom_tzmem.h> #include <linux/init.h> @@ -33,6 +34,7 @@ #include <dt-bindings/interrupt-controller/arm-gic.h> +#include "qcom_pas.h" #include "qcom_scm.h" #include "qcom_tzmem.h" @@ -479,25 +481,6 @@ void qcom_scm_cpu_power_down(u32 flags) } EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down); -int qcom_scm_set_remote_state(u32 state, u32 id) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = state, - .args[1] = id, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); - static int qcom_scm_disable_sdi(void) { int ret; @@ -570,26 +553,12 @@ static void qcom_scm_set_download_mode(u32 dload_mode) dev_err(__scm->dev, "failed to set download mode: %d\n", ret); } -/** - * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service - * context for a given peripheral - * - * PAS context is device-resource managed, so the caller does not need - * to worry about freeing the context memory. - * - * @dev: PAS firmware device - * @pas_id: peripheral authentication service id - * @mem_phys: Subsystem reserve memory start address - * @mem_size: Subsystem reserve memory size - * - * Returns: The new PAS context, or ERR_PTR() on failure. - */ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, u32 pas_id, phys_addr_t mem_phys, size_t mem_size) { - struct qcom_scm_pas_context *ctx; + struct qcom_pas_context *ctx; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -600,11 +569,12 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, ctx->mem_phys = mem_phys; ctx->mem_size = mem_size; - return ctx; + return (struct qcom_scm_pas_context *)ctx; } EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); -static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, +static int __qcom_scm_pas_init_image(struct device *dev, u32 pas_id, + dma_addr_t mdata_phys, struct qcom_scm_res *res) { struct qcom_scm_desc desc = { @@ -626,7 +596,7 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, desc.args[1] = mdata_phys; - ret = qcom_scm_call(__scm->dev, &desc, res); + ret = qcom_scm_call(dev, &desc, res); qcom_scm_bw_disable(); disable_clk: @@ -635,7 +605,8 @@ disable_clk: return ret; } -static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, +static int qcom_scm_pas_prep_and_init_image(struct device *dev, + struct qcom_pas_context *ctx, const void *metadata, size_t size) { struct qcom_scm_res res; @@ -650,7 +621,7 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, memcpy(mdata_buf, metadata, size); mdata_phys = qcom_tzmem_to_phys(mdata_buf); - ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res); + ret = __qcom_scm_pas_init_image(dev, ctx->pas_id, mdata_phys, &res); if (ret < 0) qcom_tzmem_free(mdata_buf); else @@ -659,25 +630,9 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, return ret ? : res.result[0]; } -/** - * qcom_scm_pas_init_image() - Initialize peripheral authentication service - * state machine for a given peripheral, using the - * metadata - * @pas_id: peripheral authentication service id - * @metadata: pointer to memory containing ELF header, program header table - * and optional blob of data used for authenticating the metadata - * and the rest of the firmware - * @size: size of the metadata - * @ctx: optional pas context - * - * Return: 0 on success. - * - * Upon successful return, the PAS metadata context (@ctx) will be used to - * track the metadata allocation, this needs to be released by invoking - * qcom_scm_pas_metadata_release() by the caller. - */ -int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, - struct qcom_scm_pas_context *ctx) +static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id, + const void *metadata, size_t size, + struct qcom_pas_context *ctx) { struct qcom_scm_res res; dma_addr_t mdata_phys; @@ -685,7 +640,7 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, int ret; if (ctx && ctx->use_tzmem) - return qcom_scm_pas_prep_and_init_image(ctx, metadata, size); + return qcom_scm_pas_prep_and_init_image(dev, ctx, metadata, size); /* * During the scm call memory protection will be enabled for the meta @@ -699,16 +654,15 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, * If we pass a buffer that is already part of an SHM Bridge to this * call, it will fail. */ - mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, - GFP_KERNEL); + mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL); if (!mdata_buf) return -ENOMEM; memcpy(mdata_buf, metadata, size); - ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res); + ret = __qcom_scm_pas_init_image(dev, pas_id, mdata_phys, &res); if (ret < 0 || !ctx) { - dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); + dma_free_coherent(dev, size, mdata_buf, mdata_phys); } else if (ctx) { ctx->ptr = mdata_buf; ctx->phys = mdata_phys; @@ -717,36 +671,35 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, return ret ? : res.result[0]; } -EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); -/** - * qcom_scm_pas_metadata_release() - release metadata context - * @ctx: pas context - */ -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_scm_pas_context *ctx) { - if (!ctx->ptr) - return; + return __qcom_scm_pas_init_image2(__scm->dev, pas_id, metadata, size, + (struct qcom_pas_context *)ctx); +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); +static void __qcom_scm_pas_metadata_release(struct device *dev, + struct qcom_pas_context *ctx) +{ if (ctx->use_tzmem) qcom_tzmem_free(ctx->ptr); else - dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); + dma_free_coherent(dev, ctx->size, ctx->ptr, ctx->phys); ctx->ptr = NULL; } + +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) +{ + __qcom_scm_pas_metadata_release(__scm->dev, + (struct qcom_pas_context *)ctx); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); -/** - * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral - * for firmware loading - * @pas_id: peripheral authentication service id - * @addr: start address of memory area to prepare - * @size: size of the memory area to prepare - * - * Returns 0 on success. - */ -int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id, + phys_addr_t addr, phys_addr_t size) { int ret; struct qcom_scm_desc desc = { @@ -768,7 +721,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -776,9 +729,15 @@ disable_clk: return ret ? : res.result[0]; } + +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +{ + return __qcom_scm_pas_mem_setup(__scm->dev, pas_id, addr, size); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); -static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, +static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id, + void *input_rt_tzm, size_t input_rt_size, size_t *output_rt_size) { @@ -813,7 +772,7 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, * with output_rt_tzm buffer with res.result[2] size however, It should not * be of unresonable size. */ - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); if (!ret && res.result[2] > SZ_1G) { ret = -E2BIG; goto free_output_rt; @@ -830,51 +789,11 @@ free_output_rt: return ret ? ERR_PTR(ret) : output_rt_tzm; } -/** - * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer - * for a given peripheral. - * - * Qualcomm remote processor may rely on both static and dynamic resources for - * its functionality. Static resources typically refer to memory-mapped addresses - * required by the subsystem and are often embedded within the firmware binary - * and dynamic resources, such as shared memory in DDR etc., are determined at - * runtime during the boot process. - * - * On Qualcomm Technologies devices, it's possible that static resources are not - * embedded in the firmware binary and instead are provided by TrustZone However, - * dynamic resources are always expected to come from TrustZone. This indicates - * that for Qualcomm devices, all resources (static and dynamic) will be provided - * by TrustZone via the SMC call. - * - * If the remote processor firmware binary does contain static resources, they - * should be passed in input_rt. These will be forwarded to TrustZone for - * authentication. TrustZone will then append the dynamic resources and return - * the complete resource table in output_rt_tzm. - * - * If the remote processor firmware binary does not include a resource table, - * the caller of this function should set input_rt as NULL and input_rt_size - * as zero respectively. - * - * More about documentation on resource table data structures can be found in - * include/linux/remoteproc.h - * - * @ctx: PAS context - * @pas_id: peripheral authentication service id - * @input_rt: resource table buffer which is present in firmware binary - * @input_rt_size: size of the resource table present in firmware binary - * @output_rt_size: TrustZone expects caller should pass worst case size for - * the output_rt_tzm. - * - * Return: - * On success, returns a pointer to the allocated buffer containing the final - * resource table and output_rt_size will have actual resource table size from - * TrustZone. The caller is responsible for freeing the buffer. On failure, - * returns ERR_PTR(-errno). - */ -struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, - void *input_rt, - size_t input_rt_size, - size_t *output_rt_size) +static void *__qcom_scm_pas_get_rsc_table2(struct device *dev, + struct qcom_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) { struct resource_table empty_rsc = {}; size_t size = SZ_16K; @@ -909,11 +828,12 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c memcpy(input_rt_tzm, input_rt, input_rt_size); - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm, + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id, + input_rt_tzm, input_rt_size, &size); if (PTR_ERR(output_rt_tzm) == -EOVERFLOW) /* Try again with the size requested by the TZ */ - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id, input_rt_tzm, input_rt_size, &size); @@ -943,16 +863,20 @@ disable_clk: return ret ? ERR_PTR(ret) : tbl_ptr; } + +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) +{ + return __qcom_scm_pas_get_rsc_table2(__scm->dev, + (struct qcom_pas_context *)ctx, + input_rt, input_rt_size, + output_rt_size); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table); -/** - * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware - * and reset the remote processor - * @pas_id: peripheral authentication service id - * - * Return 0 on success. - */ -int qcom_scm_pas_auth_and_reset(u32 pas_id) +static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -972,7 +896,7 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -980,28 +904,15 @@ disable_clk: return ret ? : res.result[0]; } + +int qcom_scm_pas_auth_and_reset(u32 pas_id) +{ + return __qcom_scm_pas_auth_and_reset(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); -/** - * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the - * remote processor - * - * @ctx: Context saved during call to qcom_scm_pas_context_init() - * - * This function performs the necessary steps to prepare a PAS subsystem, - * authenticate it using the provided metadata, and initiate a reset sequence. - * - * It should be used when Linux is in control setting up the IOMMU hardware - * for remote subsystem during secure firmware loading processes. The preparation - * step sets up a shmbridge over the firmware memory before TrustZone accesses the - * firmware memory region for authentication. The authentication step verifies - * the integrity and authenticity of the firmware or configuration using secure - * metadata. Finally, the reset step ensures the subsystem starts in a clean and - * sane state. - * - * Return: 0 on success, negative errno on failure. - */ -int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) +static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev, + struct qcom_pas_context *ctx) { u64 handle; int ret; @@ -1012,7 +923,7 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) * memory region and then invokes a call to TrustZone to authenticate. */ if (!ctx->use_tzmem) - return qcom_scm_pas_auth_and_reset(ctx->pas_id); + return __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id); /* * When Linux runs @ EL2 Linux must create the shmbridge itself and then @@ -1022,20 +933,45 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) if (ret) return ret; - ret = qcom_scm_pas_auth_and_reset(ctx->pas_id); + ret = __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id); qcom_tzmem_shm_bridge_delete(handle); return ret; } + +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) +{ + return __qcom_scm_pas_prepare_and_auth_reset(__scm->dev, + (struct qcom_pas_context *)ctx); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset); -/** - * qcom_scm_pas_shutdown() - Shut down the remote processor - * @pas_id: peripheral authentication service id - * - * Returns 0 on success. - */ -int qcom_scm_pas_shutdown(u32 pas_id) +static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state, + u32 pas_id) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = state, + .args[1] = pas_id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(dev, &desc, &res); + + return ret ? : res.result[0]; +} + +int qcom_scm_set_remote_state(u32 state, u32 id) +{ + return __qcom_scm_pas_set_remote_state(__scm->dev, state, id); +} +EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); + +static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -1055,7 +991,7 @@ int qcom_scm_pas_shutdown(u32 pas_id) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -1063,16 +999,14 @@ disable_clk: return ret ? : res.result[0]; } + +int qcom_scm_pas_shutdown(u32 pas_id) +{ + return __qcom_scm_pas_shutdown(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); -/** - * qcom_scm_pas_supported() - Check if the peripheral authentication service is - * available for the given peripherial - * @pas_id: peripheral authentication service id - * - * Returns true if PAS is supported for this peripheral, otherwise false. - */ -bool qcom_scm_pas_supported(u32 pas_id) +static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -1084,16 +1018,49 @@ bool qcom_scm_pas_supported(u32 pas_id) }; struct qcom_scm_res res; - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + if (!__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PIL_PAS_IS_SUPPORTED)) return false; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); return ret ? false : !!res.result[0]; } + +bool qcom_scm_pas_supported(u32 pas_id) +{ + return __qcom_scm_pas_supported(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); +static struct qcom_pas_ops qcom_pas_ops_scm = { + .drv_name = "qcom_scm", + .supported = __qcom_scm_pas_supported, + .init_image = __qcom_scm_pas_init_image2, + .mem_setup = __qcom_scm_pas_mem_setup, + .get_rsc_table = __qcom_scm_pas_get_rsc_table2, + .auth_and_reset = __qcom_scm_pas_auth_and_reset, + .prepare_and_auth_reset = __qcom_scm_pas_prepare_and_auth_reset, + .set_remote_state = __qcom_scm_pas_set_remote_state, + .shutdown = __qcom_scm_pas_shutdown, + .metadata_release = __qcom_scm_pas_metadata_release, +}; + +/** + * qcom_scm_is_pas_available() - Check if the peripheral authentication service + * is available via SCM or not + * + * Returns true if PAS is available, otherwise false. + */ +static bool qcom_scm_is_pas_available(void) +{ + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PIL_PAS_AUTH_AND_RESET)) + return false; + + return true; +} + static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) { struct qcom_scm_desc desc = { @@ -2837,6 +2804,11 @@ static int qcom_scm_probe(struct platform_device *pdev) __get_convention(); + if (qcom_scm_is_pas_available()) { + qcom_pas_ops_scm.dev = scm->dev; + qcom_pas_ops_register(&qcom_pas_ops_scm); + } + /* * If "download mode" is requested, from this point on warmboot * will cause the boot stages to enter download mode, unless @@ -2876,6 +2848,7 @@ static void qcom_scm_shutdown(struct platform_device *pdev) { /* Clean shutdown, disable download mode to allow normal restart */ qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP); + qcom_pas_ops_unregister(); } static const struct of_device_id qcom_scm_dt_match[] = { diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 5b4408fcc10b..8d3acadb0d68 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1139,6 +1139,7 @@ static void mvebu_gpio_remove_irq_domain(void *data) { struct irq_domain *domain = data; + irq_domain_remove_generic_chips(domain); irq_domain_remove(domain); } diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index dc81fe0301ce..3c73ae87f3c5 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -1118,22 +1118,30 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, blocks, total_allocated_on_err); } +static int __alloc_contig_aligned_retry(struct gpu_buddy *mm, + u64 unaligned_offset, + u64 size, + u64 min_block_size, + struct list_head *blocks) +{ + u64 aligned_offset = round_down(unaligned_offset, min_block_size); + + return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks); +} + static int __alloc_contig_try_harder(struct gpu_buddy *mm, u64 size, u64 min_block_size, struct list_head *blocks) { - u64 rhs_offset, lhs_offset, lhs_size, filled; + u64 rhs_offset, lhs_offset, filled; struct gpu_buddy_block *block; unsigned int tree, order; - LIST_HEAD(blocks_lhs); - unsigned long pages; u64 modify_size; int err; modify_size = rounddown_pow_of_two(size); - pages = modify_size >> ilog2(mm->chunk_size); - order = fls(pages) - 1; + order = ilog2(modify_size) - ilog2(mm->chunk_size); if (order == 0) return -ENOSPC; @@ -1149,31 +1157,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm, while (iter) { block = rbtree_get_free_block(iter); - /* Allocate blocks traversing RHS */ rhs_offset = gpu_buddy_block_offset(block); + + /* Allocate blocks traversing RHS */ err = __gpu_buddy_alloc_range(mm, rhs_offset, size, &filled, blocks); - if (!err || err != -ENOSPC) + if (err && err != -ENOSPC) return err; + if (!err && IS_ALIGNED(rhs_offset, min_block_size)) + return 0; + if (!err) { + /* Allocate the unaligned RHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, rhs_offset, + size, + min_block_size, + blocks); + if (!err) + return 0; + if (err != -ENOSPC) { + gpu_buddy_free_list_internal(mm, blocks); + return err; + } + goto next; + } - lhs_size = max((size - filled), min_block_size); - if (!IS_ALIGNED(lhs_size, min_block_size)) - lhs_size = round_up(lhs_size, min_block_size); + if (size - filled > rhs_offset) + goto next; - /* Allocate blocks traversing LHS */ - lhs_offset = gpu_buddy_block_offset(block) - lhs_size; - err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size, - NULL, &blocks_lhs); - if (!err) { - list_splice(&blocks_lhs, blocks); + lhs_offset = rhs_offset - (size - filled); + + /* Allocate the unaligned LHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, lhs_offset, size, + min_block_size, blocks); + if (!err) return 0; - } else if (err != -ENOSPC) { + if (err != -ENOSPC) { gpu_buddy_free_list_internal(mm, blocks); return err; } - /* Free blocks for the next iteration */ +next: gpu_buddy_free_list_internal(mm, blocks); - iter = rb_prev(iter); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c index 8aa068a4d3e3..724beb96ed1a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c @@ -139,7 +139,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev, regSDMA1_QUEUE0_RB_CNTL) - regSDMA0_QUEUE0_RB_CNTL; break; default: - BUG(); + WARN(1, "Invalid SDMA engine id %d\n", engine_id); + break; } sdma_rlc_reg_offset = sdma_engine_reg_base diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c index bf0bd7688ee4..e11ba3e91841 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c @@ -90,7 +90,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev, regSDMA1_QUEUE0_RB_CNTL) - regSDMA0_QUEUE0_RB_CNTL; break; default: - BUG(); + WARN(1, "Invalid SDMA engine id %d\n", engine_id); + break; } sdma_rlc_reg_offset = sdma_engine_reg_base diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c index bcb180f9d3ff..38ca1aea33b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c @@ -93,7 +93,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev, regSDMA1_SDMA_QUEUE0_RB_CNTL) - regSDMA0_SDMA_QUEUE0_RB_CNTL; break; default: - BUG(); + WARN(1, "Invalid SDMA engine id %d\n", engine_id); + break; } sdma_rlc_reg_offset = sdma_engine_reg_base diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c index 6860a3a4d466..1e21e3444802 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c @@ -917,8 +917,6 @@ int amdgpu_atomfirmware_get_clock_info(struct amdgpu_device *adev) (crev != 6)) { spll->reference_freq = le32_to_cpu(gfx_info->v22.rlc_gpu_timer_refclk); ret = 0; - } else { - BUG(); } } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index babd23e5a27e..9bbe60968352 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -76,10 +76,9 @@ static uint32_t amdgpu_cgs_read_ind_register(struct cgs_device *cgs_device, DRM_ERROR("audio endpt register access not implemented.\n"); return 0; default: - BUG(); + WARN(1, "Invalid indirect register space"); + return 0; } - WARN(1, "Invalid indirect register space"); - return 0; } static void amdgpu_cgs_write_ind_register(struct cgs_device *cgs_device, @@ -104,9 +103,8 @@ static void amdgpu_cgs_write_ind_register(struct cgs_device *cgs_device, DRM_ERROR("audio endpt register access not implemented.\n"); return; default: - BUG(); + WARN(1, "Invalid indirect register space"); } - WARN(1, "Invalid indirect register space"); } static uint32_t fw_type_convert(struct cgs_device *cgs_device, uint32_t fw_type) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index d777375e5350..d58701908158 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1118,7 +1118,8 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) if (fpriv->csa_va) { bo_va = fpriv->csa_va; - BUG_ON(!bo_va); + if (!bo_va) + return -ENOMEM; r = amdgpu_vm_bo_update(adev, bo_va, false); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c index 6d9e96fabd58..4f1c711df5bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c @@ -1478,7 +1478,8 @@ void amdgpu_ucode_ip_version_decode(struct amdgpu_device *adev, int block_type, ip_name = "isp"; break; default: - BUG(); + WARN(1, "invalid HWIP %d\n", block_type); + return; } maj = IP_VERSION_MAJ(version); diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index b703ef1fe340..b951328d94cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -125,7 +125,8 @@ static int cik_sdma_init_microcode(struct amdgpu_device *adev) case CHIP_MULLINS: chip_name = "mullins"; break; - default: BUG(); + default: + return -EINVAL; } for (i = 0; i < adev->sdma.num_instances; i++) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 9d325867a1aa..55342962422b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -5350,6 +5350,15 @@ static void gfx_v10_0_constants_init(struct amdgpu_device *adev) gfx_v10_0_get_tcc_info(adev); adev->gfx.config.pa_sc_tile_steering_override = gfx_v10_0_init_pa_sc_tile_steering_override(adev); + /* Program DB_RING_CONTROL for multiple GFX pipes + * Default power up value is 1. + * Possible values: + * 0 - split occlusion counters between gfx pipes + * 1 - all occlusion counters to pipe 0 + * 2 - all occlusion counters to pipe 1 + */ + WREG32_FIELD15(GC, 0, DB_RING_CONTROL, COUNTER_CONTROL, + (adev->gfx.me.num_pipe_per_me > 1) ? 0 : 1); /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 254a72f26ec5..2ef6bc818aba 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -51,7 +51,7 @@ #include "mes_userqueue.h" #include "amdgpu_userq_fence.h" -#define GFX11_NUM_GFX_RINGS 1 +#define GFX11_NUM_GFX_RINGS 2 #define GFX11_MEC_HPD_SIZE 2048 #define RLCG_UCODE_LOADING_START_ADDRESS 0x00002000L @@ -1625,7 +1625,10 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 7, 0): case IP_VERSION(11, 7, 1): adev->gfx.me.num_me = 1; - adev->gfx.me.num_pipe_per_me = 1; + if (adev->gfx.rs64_enable) + adev->gfx.me.num_pipe_per_me = 1; + else + adev->gfx.me.num_pipe_per_me = 2; adev->gfx.me.num_queue_per_pipe = 2; adev->gfx.mec.num_mec = 1; adev->gfx.mec.num_pipe_per_mec = 4; @@ -2191,6 +2194,15 @@ static void gfx_v11_0_constants_init(struct amdgpu_device *adev) tmp = RREG32_SOC15(GC, 0, regTA_CNTL2); adev->gfx.config.ta_cntl2_truncate_coord_mode = REG_GET_FIELD(tmp, TA_CNTL2, TRUNCATE_COORD_MODE); + /* Program DB_RING_CONTROL for multiple GFX pipes + * Default power up value is 1. + * Possible values: + * 0 - split occlusion counters between gfx pipes + * 1 - all occlusion counters to pipe 0 + * 2 - all occlusion counters to pipe 1 + */ + WREG32_FIELD15_PREREG(GC, 0, DB_RING_CONTROL, COUNTER_CONTROL, + (adev->gfx.me.num_pipe_per_me > 1) ? 0 : 1); /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ @@ -5357,6 +5369,7 @@ static void gfx_v11_0_ring_emit_gds_switch(struct amdgpu_ring *ring, static int gfx_v11_0_early_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; + int r; switch (amdgpu_user_queue) { case -1: @@ -5377,6 +5390,11 @@ static int gfx_v11_0_early_init(struct amdgpu_ip_block *ip_block) adev->gfx.funcs = &gfx_v11_0_gfx_funcs; + gfx_v11_0_set_imu_funcs(adev); + r = gfx_v11_0_init_microcode(adev); + if (r) + return r; + if (adev->gfx.disable_kq) { /* We need one GFX ring temporarily to set up * the clear state. @@ -5384,7 +5402,11 @@ static int gfx_v11_0_early_init(struct amdgpu_ip_block *ip_block) adev->gfx.num_gfx_rings = 1; adev->gfx.num_compute_rings = 0; } else { - adev->gfx.num_gfx_rings = GFX11_NUM_GFX_RINGS; + /* rs64 only supports one gfx pipe */ + if (adev->gfx.rs64_enable) + adev->gfx.num_gfx_rings = 1; + else + adev->gfx.num_gfx_rings = GFX11_NUM_GFX_RINGS; adev->gfx.num_compute_rings = min(amdgpu_gfx_get_num_kcq(adev), AMDGPU_MAX_COMPUTE_RINGS); } @@ -5395,13 +5417,12 @@ static int gfx_v11_0_early_init(struct amdgpu_ip_block *ip_block) gfx_v11_0_set_gds_init(adev); gfx_v11_0_set_rlc_funcs(adev); gfx_v11_0_set_mqd_funcs(adev); - gfx_v11_0_set_imu_funcs(adev); gfx_v11_0_init_rlcg_reg_access_ctrl(adev); amdgpu_init_rlc_reg_funcs(adev); - return gfx_v11_0_init_microcode(adev); + return 0; } static bool gfx_v11_0_is_rlc_enabled(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 8221a77b6b75..6fedb6df8db9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -1827,6 +1827,15 @@ static void gfx_v12_0_constants_init(struct amdgpu_device *adev) gfx_v12_0_get_cu_info(adev, &adev->gfx.cu_info); gfx_v12_0_get_tcc_info(adev); adev->gfx.config.pa_sc_tile_steering_override = 0; + /* Program DB_RING_CONTROL for multiple GFX pipes + * Default power up value is 1. + * Possible values: + * 0 - split occlusion counters between gfx pipes + * 1 - all occlusion counters to pipe 0 + * 2 - all occlusion counters to pipe 1 + */ + WREG32_FIELD15_PREREG(GC, 0, DB_RING_CONTROL, COUNTER_CONTROL, + (adev->gfx.me.num_pipe_per_me > 1) ? 0 : 1); /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index a914dd8183b5..b49098931d19 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -124,7 +124,7 @@ static int gmc_v6_0_init_microcode(struct amdgpu_device *adev) chip_name = "hainan"; break; default: - BUG(); + return -EINVAL; } /* this memory configuration requires special firmware */ diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 397b08c7173a..c7253b908351 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -138,7 +138,7 @@ static int sdma_v2_4_init_microcode(struct amdgpu_device *adev) chip_name = "topaz"; break; default: - BUG(); + return -EINVAL; } for (i = 0; i < adev->sdma.num_instances; i++) { diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 8ac1c9dae72e..05cce4bde73e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -298,7 +298,8 @@ static int sdma_v3_0_init_microcode(struct amdgpu_device *adev) case CHIP_STONEY: chip_name = "stoney"; break; - default: BUG(); + default: + return -EINVAL; } for (i = 0; i < adev->sdma.num_instances; i++) { diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c index 6e455ad66d62..7100f30ffeff 100644 --- a/drivers/gpu/drm/amd/amdgpu/si.c +++ b/drivers/gpu/drm/amd/amdgpu/si.c @@ -2218,7 +2218,7 @@ static void si_init_golden_registers(struct amdgpu_device *adev) default: - BUG(); + break; } } @@ -2735,7 +2735,7 @@ int si_set_ip_blocks(struct amdgpu_device *adev) amdgpu_device_ip_block_add(adev, &amdgpu_vkms_ip_block); break; default: - BUG(); + break; } return 0; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 411ee894f623..2c1a936459ac 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -3303,6 +3303,11 @@ static int kfd_ioctl_create_process(struct file *filep, struct kfd_process *p, v } filep->private_data = process; + ret = kfd_debugfs_add_process(process); + if (ret) + pr_warn("Failed to create debugfs entry for the kfd_process, ret = %d\n", + ret); + mutex_unlock(&kfd_processes_mutex); ret = kfd_create_process_sysfs(process); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_debugfs.c b/drivers/gpu/drm/amd/amdkfd/kfd_debugfs.c index 7d4e07452cdb..464836f2a53f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_debugfs.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_debugfs.c @@ -33,6 +33,7 @@ static struct list_head procs; struct debugfs_proc_entry { struct list_head list; struct dentry *proc_dentry; + struct kfd_process *process; pid_t pid; }; @@ -140,34 +141,97 @@ static const struct file_operations kfd_debugfs_pasid_fops = { .read = kfd_debugfs_pasid_read, }; -void kfd_debugfs_add_process(struct kfd_process *p) +/* This helper locates the debugfs entry of a kfd process */ +static struct debugfs_proc_entry *kfd_debugfs_find_process_entry(struct kfd_process *p) { + struct debugfs_proc_entry *entry; + + list_for_each_entry(entry, &procs, list) { + if (entry->process == p) + return entry; + } + + return NULL; +} + +/* This helper creates pasid file of a kfd process under debugfs */ +static void kfd_debugfs_create_pasid_files(struct kfd_process *p, + struct dentry *dir) +{ + char name[MAX_DEBUGFS_FILENAME_LEN]; + struct kfd_process_device *pdd; int i; + + /* create pasid file for each GPU */ + for (i = 0; i < p->n_pdds; i++) { + pdd = p->pdds[i]; + snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "pasid_%u", pdd->dev->id); + debugfs_create_file((const char *)name, S_IFREG | 0444, + dir, pdd, &kfd_debugfs_pasid_fops); + } +} + +int kfd_debugfs_add_process(struct kfd_process *p) +{ + struct debugfs_proc_entry *primary_entry; char name[MAX_DEBUGFS_FILENAME_LEN]; + struct kfd_process *primary_process; struct debugfs_proc_entry *entry; + int ret; entry = kzalloc_obj(*entry); if (!entry) - return; + return -ENOMEM; - list_add(&entry->list, &procs); + entry->process = p; entry->pid = p->lead_thread->pid; - snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "%d", - (int)entry->pid); - entry->proc_dentry = debugfs_create_dir(name, debugfs_proc); - /* Create debugfs files for each GPU: - * - proc/<pid>/pasid_<gpuid> - */ - for (i = 0; i < p->n_pdds; i++) { - struct kfd_process_device *pdd = p->pdds[i]; + if (p->context_id == KFD_CONTEXT_ID_PRIMARY) { + snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "%d", + (int)entry->pid); + entry->proc_dentry = debugfs_create_dir(name, debugfs_proc); + } else { + primary_process = kfd_lookup_process_by_mm(p->lead_thread->mm); + if (!primary_process) { + ret = -ESRCH; + goto err_free_entry; + } - snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "pasid_%u", - pdd->dev->id); - debugfs_create_file((const char *)name, S_IFREG | 0444, - entry->proc_dentry, pdd, - &kfd_debugfs_pasid_fops); + primary_entry = kfd_debugfs_find_process_entry(primary_process); + kfd_unref_process(primary_process); + if (!primary_entry) { + pr_warn("Failed to find the primary debugfs entry for pid %d\n", + entry->pid); + ret = -ENOENT; + goto err_free_entry; + } + + snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "context_%u", + p->context_id); + entry->proc_dentry = debugfs_create_dir(name, + primary_entry->proc_dentry); } + if (IS_ERR_OR_NULL(entry->proc_dentry)) { + ret = entry->proc_dentry ? PTR_ERR(entry->proc_dentry) : -ENOMEM; + goto err_free_entry; + } + + list_add(&entry->list, &procs); + kfd_debugfs_create_pasid_files(p, entry->proc_dentry); + + return 0; + +err_free_entry: + kfree(entry); + return ret; +} + +/* This helper removes a debugfs entry and its sub-entries */ +static void kfd_debugfs_remove_entry(struct debugfs_proc_entry *entry) +{ + debugfs_remove(entry->proc_dentry); + list_del(&entry->list); + kfree(entry); } void kfd_debugfs_remove_process(struct kfd_process *p) @@ -175,13 +239,22 @@ void kfd_debugfs_remove_process(struct kfd_process *p) struct debugfs_proc_entry *entry, *next; mutex_lock(&kfd_processes_mutex); + if (p->context_id == KFD_CONTEXT_ID_PRIMARY) { + /* remove entries of secondary contexts */ + list_for_each_entry_safe(entry, next, &procs, list) { + if (entry->pid != p->lead_thread->pid || entry->process == p) + continue; + + kfd_debugfs_remove_entry(entry); + } + } + list_for_each_entry_safe(entry, next, &procs, list) { - if (entry->pid != p->lead_thread->pid) + if (entry->process != p) continue; - debugfs_remove_recursive(entry->proc_dentry); - list_del(&entry->list); - kfree(entry); + kfd_debugfs_remove_entry(entry); } + mutex_unlock(&kfd_processes_mutex); } 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 97402e6c8f83..cc42feb24277 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3777,6 +3777,12 @@ out: dqm_unlock(dqm); return r; } + +size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, enum kfd_queue_type type) +{ + return dqm->mqd_mgrs[get_mqd_type_from_queue_type(type)]->mqd_size; +} + #if defined(CONFIG_DEBUG_FS) static void seq_reg_dump(struct seq_file *m, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index 2229f8b2f446..c9f9f7a87111 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -335,6 +335,8 @@ bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm, int doorbell_off, u32 *queue_format); int kfd_reset_queue_mes(struct device_queue_manager *dqm, int queue_type, int pipe, int queue, unsigned int db); +size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, + enum kfd_queue_type type); static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index dae01e2bb464..f5fd78f0df7d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -61,6 +61,9 @@ static int allocate_event_notification_slot(struct kfd_process *p, return -ENOMEM; if (restore_id) { + if (*restore_id >= KFD_SIGNAL_EVENT_LIMIT) + return -EINVAL; + id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1, GFP_KERNEL); } else { @@ -158,7 +161,7 @@ static int create_signal_event(struct file *devkfd, struct kfd_process *p, ret = allocate_event_notification_slot(p, ev, restore_id); if (ret) { - pr_warn("Signal event wasn't created because out of kernel memory\n"); + pr_warn("Failed to create signal event notification slot\n"); return ret; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 86a944d36aaf..88191a4c1657 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -441,7 +441,8 @@ enum kfd_queue_type { KFD_QUEUE_TYPE_SDMA, KFD_QUEUE_TYPE_HIQ, KFD_QUEUE_TYPE_SDMA_XGMI, - KFD_QUEUE_TYPE_SDMA_BY_ENG_ID + KFD_QUEUE_TYPE_SDMA_BY_ENG_ID, + KFD_QUEUE_TYPE_MAX, }; enum kfd_queue_format { @@ -1650,14 +1651,14 @@ int kfd_debugfs_hang_hws(struct kfd_node *dev); int pm_debugfs_hang_hws(struct packet_manager *pm); int dqm_debugfs_hang_hws(struct device_queue_manager *dqm); -void kfd_debugfs_add_process(struct kfd_process *p); +int kfd_debugfs_add_process(struct kfd_process *p); void kfd_debugfs_remove_process(struct kfd_process *p); #else static inline void kfd_debugfs_init(void) {} static inline void kfd_debugfs_fini(void) {} -static inline void kfd_debugfs_add_process(struct kfd_process *p) {} +static inline int kfd_debugfs_add_process(struct kfd_process *p) { return 0; } static inline void kfd_debugfs_remove_process(struct kfd_process *p) {} #endif diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 7a604b8c8886..eb508fe3ded7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -948,7 +948,7 @@ static void kfd_process_free_id(struct kfd_process *process) { struct kfd_process *primary_process; - if (process->context_id != KFD_CONTEXT_ID_PRIMARY) + if (process->context_id == KFD_CONTEXT_ID_PRIMARY) return; primary_process = kfd_lookup_process_by_mm(process->lead_thread->mm); @@ -1013,7 +1013,10 @@ struct kfd_process *kfd_create_process(struct task_struct *thread) if (ret) pr_warn("Failed to create sysfs entry for the kfd_process"); - kfd_debugfs_add_process(process); + ret = kfd_debugfs_add_process(process); + if (ret) + pr_warn("Failed to create debugfs entry for the kfd_process, ret = %d\n", + ret); init_waitqueue_head(&process->wait_irq_drain); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index acbdca91cde5..97e77a5a35e5 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -265,6 +265,11 @@ static int init_user_queue(struct process_queue_manager *pqm, (*q)->process = pqm->process; if (dev->kfd->shared_resources.enable_mes) { + if (!q_properties->wptr_bo) { + pr_debug("Queue initialization with shared MES requires queue buffers to be initialized\n"); + return -EINVAL; + } + retval = amdgpu_amdkfd_alloc_kernel_mem(dev->adev, AMDGPU_MES_GANG_CTX_SIZE, AMDGPU_GEM_DOMAIN_GTT, @@ -1003,6 +1008,23 @@ int kfd_criu_restore_queue(struct kfd_process *p, goto exit; } + pdd = kfd_process_device_data_by_id(p, q_data->gpu_id); + if (!pdd) { + pr_err("Failed to get pdd\n"); + ret = -EINVAL; + goto exit; + } + + if (q_data->type >= KFD_QUEUE_TYPE_MAX) { + ret = -EINVAL; + goto exit; + } + + if (q_data->mqd_size != mqd_size_from_queue_type(pdd->dev->dqm, q_data->type)) { + ret = -EINVAL; + goto exit; + } + *priv_data_offset += sizeof(*q_data); q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size; @@ -1025,13 +1047,6 @@ int kfd_criu_restore_queue(struct kfd_process *p, *priv_data_offset += q_extra_data_size; - pdd = kfd_process_device_data_by_id(p, q_data->gpu_id); - if (!pdd) { - pr_err("Failed to get pdd\n"); - ret = -EINVAL; - goto exit; - } - /* * data stored in this order: * mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]... @@ -1042,24 +1057,10 @@ int kfd_criu_restore_queue(struct kfd_process *p, memset(&qp, 0, sizeof(qp)); set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->xcc_mask)); - ret = kfd_queue_acquire_buffers(pdd, &qp); - if (ret) { - pr_debug("failed to acquire user queue buffers for CRIU\n"); - goto exit; - } - - ret = kfd_queue_acquire_buffers(pdd, &qp); - if (ret) { - pr_debug("failed to acquire user queue buffers for CRIU\n"); - goto exit; - } - print_queue_properties(&qp); ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL); if (ret) { - kfd_queue_unref_bo_vas(pdd, &qp); - kfd_queue_release_buffers(pdd, &qp); pr_err("Failed to create new queue err:%d\n", ret); goto exit; } 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 d6cf643205ab..0fbcaf884419 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 @@ -1621,19 +1621,23 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu, table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; CustomSkuTable_t *skutable = &pptable->CustomSkuTable; - int16_t od_percent_upper = 0, od_percent_lower = 0; + uint32_t pp_limit = smu->adev->pm.ac_power ? + skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : + skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - uint32_t power_limit; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + int16_t od_percent_upper = 0, od_percent_lower = 0; + int ret; - if (smu_v14_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? - skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : - skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + if (current_power_limit) { + ret = smu_v14_0_get_current_power_limit(smu, current_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 && @@ -1647,15 +1651,15 @@ static int smu_v14_0_2_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 = max_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 = min_limit * (100 + od_percent_lower); *min_power_limit /= 100; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 3316bb00a662..d4664ed468b2 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -225,16 +225,106 @@ static void drm_fb_helper_resume_worker(struct work_struct *work) console_unlock(); } +static int find_crtc_index_atomic(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + int crtc_index = -EINVAL; + struct drm_modeset_acquire_ctx ctx; + struct drm_plane *plane; + int ret = 0; + + drm_modeset_acquire_init(&ctx, 0); + +retry: + drm_for_each_plane(plane, dev) { + const struct drm_plane_state *plane_state; + + if (plane->type != DRM_PLANE_TYPE_PRIMARY) + continue; + + ret = drm_modeset_lock(&plane->mutex, &ctx); + if (ret) + goto err_drm_modeset_lock; + plane_state = plane->state; + + if (plane_state->fb == helper->fb && plane_state->crtc) { + struct drm_crtc *crtc = plane_state->crtc; + + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret) + goto err_drm_modeset_lock; + if (crtc->state->active) + crtc_index = crtc->index; + drm_modeset_unlock(&crtc->mutex); + } + drm_modeset_unlock(&plane->mutex); + + if (crtc_index >= 0) + break; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return crtc_index; + +err_drm_modeset_lock: + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + return ret; +} + +static int find_crtc_index_legacy(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, dev) { + struct drm_plane *plane = crtc->primary; + + if (!crtc->enabled) + continue; + if (!plane || plane->fb != helper->fb) + continue; /* CRTC doesn't display fbdev emulation */ + + return crtc->index; + } + + return -EINVAL; +} + +static int drm_fb_helper_find_crtc_index(struct drm_fb_helper *helper) +{ + struct drm_device *dev = helper->dev; + int crtc_index; + + mutex_lock(&dev->mode_config.mutex); + + if (drm_drv_uses_atomic_modeset(dev)) + crtc_index = find_crtc_index_atomic(helper); + else + crtc_index = find_crtc_index_legacy(helper); + + mutex_unlock(&dev->mode_config.mutex); + + return crtc_index; +} + static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) { struct drm_device *dev = helper->dev; struct drm_clip_rect *clip = &helper->damage_clip; struct drm_clip_rect clip_copy; + int crtc_index; unsigned long flags; int ret; mutex_lock(&helper->lock); - drm_client_modeset_wait_for_vblank(&helper->client, 0); + crtc_index = drm_fb_helper_find_crtc_index(helper); + if (crtc_index >= 0) + drm_client_modeset_wait_for_vblank(&helper->client, crtc_index); mutex_unlock(&helper->lock); if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty)) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 958cb605aedd..fcfe635bc195 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -48,6 +48,47 @@ * event. As mentioned above, ranges are tracked in a notifier's Red-Black * tree. * + * - Pages: + * struct drm_gpusvm_pages holds the DMA mapping state for a range of + * CPU virtual addresses: the DMA mapped device addresses, + * the device private pagemap, the IOVA state, the per mapping + * notifier sequence number, and the drm_device that owns the DMA + * mappings. + * A driver embeds one or more struct drm_gpusvm_pages alongside its + * struct drm_gpusvm_range, choosing one of two layouts: + * + * 1:1 - one drm_gpusvm_pages per range (one drm_device). Simplest + * layout; to mirror a VA range on several devices a driver uses a + * separate range (and notifier) per device, so the HMM fault is taken + * once per device. + * + * N:1 - one drm_gpusvm_pages per drm_device, all sharing one range and + * notifier; only the per-device DMA mapping differs. The instances must + * sit in contiguous memory so a single drm_gpusvm_range_set_unmapped() + * can mark them all. A driver can keep one instance inline for the single + * device case and switch to a heap array only when more devices join, + * e.g.: + * + * .. code-block:: c + * + * struct driver_range { + * struct drm_gpusvm_range base; + * unsigned int num_pages; // 1: inline_pages, >1: pages[] + * union { + * struct drm_gpusvm_pages inline_pages; + * struct drm_gpusvm_pages *pages; + * }; + * }; + * + * In the N:1 case the driver allocates the pages array with a zeroing + * allocator (e.g. kcalloc(num_pages, ...)), initialises each entry with + * drm_gpusvm_init_pages(), and frees each entry with + * drm_gpusvm_free_pages() plus the array itself, from its range free + * callback. Each drm_gpusvm_pages is mapped independently by their own + * drm_device. + * Each drm_gpusvm_pages must be zero-initialised and initialised with + * drm_gpusvm_init_pages(), called once per entry. + * * - Operations: * Define the interface for driver-specific GPU SVM operations such as * range allocation, notifier allocation, and invalidations. @@ -92,7 +133,7 @@ * range RB tree and list, as well as the range's DMA mappings and sequence * number. GPU SVM manages all necessary locking and unlocking operations, * except for the recheck range's pages being valid - * (drm_gpusvm_range_pages_valid) when the driver is committing GPU bindings. + * (drm_gpusvm_pages_valid) when the driver is committing GPU bindings. * This lock corresponds to the ``driver->update`` lock mentioned in * Documentation/mm/hmm.rst. Future revisions may transition from a GPU SVM * global lock to a per-notifier lock if finer-grained locking is deemed @@ -140,15 +181,20 @@ * * .. code-block:: c * - * int driver_bind_range(struct drm_gpusvm *gpusvm, struct drm_gpusvm_range *range) + * struct driver_range { + * struct drm_gpusvm_range base; + * struct drm_gpusvm_pages pages; + * }; + * + * int driver_bind_range(struct drm_gpusvm *gpusvm, struct driver_range *drange) * { * int err = 0; * - * driver_alloc_and_setup_memory_for_bind(gpusvm, range); + * driver_alloc_and_setup_memory_for_bind(gpusvm, drange); * * drm_gpusvm_notifier_lock(gpusvm); - * if (drm_gpusvm_range_pages_valid(range)) - * driver_commit_bind(gpusvm, range); + * if (drm_gpusvm_pages_valid(gpusvm, &drange->pages)) + * driver_commit_bind(gpusvm, drange); * else * err = -EAGAIN; * drm_gpusvm_notifier_unlock(gpusvm); @@ -160,6 +206,8 @@ * unsigned long gpuva_start, unsigned long gpuva_end) * { * struct drm_gpusvm_ctx ctx = {}; + * struct driver_range *drange; + * struct drm_gpusvm_range *range; * int err; * * driver_svm_lock(); @@ -174,6 +222,7 @@ * err = PTR_ERR(range); * goto unlock; * } + * drange = container_of(range, struct driver_range, base); * * if (driver_migration_policy(range)) { * err = drm_pagemap_populate_mm(driver_choose_drm_pagemap(), @@ -183,7 +232,10 @@ * goto retry; * } * - * err = drm_gpusvm_range_get_pages(gpusvm, range, &ctx); + * err = drm_gpusvm_get_pages(gpusvm, &drange->pages, + * gpusvm->mm, &range->notifier->notifier, + * drm_gpusvm_range_start(range), + * drm_gpusvm_range_end(range), &ctx); * if (err == -EOPNOTSUPP || err == -EFAULT || err == -EPERM) { // CPU mappings changed * if (err == -EOPNOTSUPP) * drm_gpusvm_range_evict(gpusvm, range); @@ -192,7 +244,7 @@ * goto unlock; * } * - * err = driver_bind_range(gpusvm, range); + * err = driver_bind_range(gpusvm, drange); * if (err == -EAGAIN) // CPU mappings changed * goto retry * @@ -205,6 +257,21 @@ * * .. code-block:: c * + * // The driver owns the drm_gpusvm_pages lifecycle. ops->range_free is + * // the final fallback: drm_gpusvm_free_pages() unmaps any + * // lingering DMA mapping and a no-op if already unmapped and frees the + * // dma_addr array. The normal flow is to DMA unmap before + * // drm_gpusvm_range_remove() (before the range leaves the tree). + * void driver_range_free(struct drm_gpusvm_range *range) + * { + * struct driver_range *drange = + * container_of(range, struct driver_range, base); + * + * drm_gpusvm_free_pages(range->gpusvm, &drange->pages, + * drm_gpusvm_range_size(range) >> PAGE_SHIFT); + * kfree(drange); + * } + * * void __driver_garbage_collector(struct drm_gpusvm *gpusvm, * struct drm_gpusvm_range *range) * { @@ -215,6 +282,14 @@ * drm_gpusvm_range_evict(gpusvm, range); * * driver_unbind_range(range); + * // The pages must be DMA unmapped before drm_gpusvm_range_remove() + * // , so a range is never off the MMU interval tree while still DMA + * // mapped as the original drmsvm design flow. Otherwise a concurrent CPU + * // munmap's notifier could miss this range and free pages still mapped + * // for device DMA. This is the normal unmap point. + * drm_gpusvm_unmap_pages(gpusvm, &drange->pages, + * drm_gpusvm_range_size(range) >> PAGE_SHIFT, + * &(struct drm_gpusvm_ctx){ .in_notifier = false }); * drm_gpusvm_range_remove(gpusvm, range); * } * @@ -236,17 +311,22 @@ * { * struct drm_gpusvm_ctx ctx = { .in_notifier = true, }; * struct drm_gpusvm_range *range = NULL; + * struct driver_range *drange; * * driver_invalidate_device_pages(gpusvm, mmu_range->start, mmu_range->end); * * drm_gpusvm_for_each_range(range, notifier, mmu_range->start, * mmu_range->end) { - * drm_gpusvm_range_unmap_pages(gpusvm, range, &ctx); + * drange = container_of(range, struct driver_range, base); + * + * drm_gpusvm_unmap_pages(gpusvm, &drange->pages, + * drm_gpusvm_range_size(range) >> PAGE_SHIFT, + * &ctx); * * if (mmu_range->event != MMU_NOTIFY_UNMAP) * continue; * - * drm_gpusvm_range_set_unmapped(range, mmu_range); + * drm_gpusvm_range_set_unmapped(range, &drange->pages, 1, mmu_range); * driver_garbage_collector_add(gpusvm, range); * } * } @@ -359,7 +439,6 @@ static const struct mmu_interval_notifier_ops drm_gpusvm_notifier_ops = { * drm_gpusvm_init() - Initialize the GPU SVM. * @gpusvm: Pointer to the GPU SVM structure. * @name: Name of the GPU SVM. - * @drm: Pointer to the DRM device structure. * @mm: Pointer to the mm_struct for the address space. * @mm_start: Start address of GPU SVM. * @mm_range: Range of the GPU SVM. @@ -373,7 +452,9 @@ static const struct mmu_interval_notifier_ops drm_gpusvm_notifier_ops = { * This function initializes the GPU SVM. * * Note: If only using the simple drm_gpusvm_pages API (get/unmap/free), - * then only @gpusvm, @name, and @drm are expected. However, the same base + * then only @gpusvm and @name are expected. The @drm drm_device for dma + * mappings is bound per-pages via drm_gpusvm_init_pages() before the first + * drm_gpusvm_get_pages() call. However, the same base * @gpusvm can also be used with both modes together in which case the full * setup is needed, where the core drm_gpusvm_pages API will simply never use * the other fields. @@ -381,7 +462,7 @@ static const struct mmu_interval_notifier_ops drm_gpusvm_notifier_ops = { * Return: 0 on success, a negative error code on failure. */ int drm_gpusvm_init(struct drm_gpusvm *gpusvm, - const char *name, struct drm_device *drm, + const char *name, struct mm_struct *mm, unsigned long mm_start, unsigned long mm_range, unsigned long notifier_size, @@ -399,7 +480,6 @@ int drm_gpusvm_init(struct drm_gpusvm *gpusvm, } gpusvm->name = name; - gpusvm->drm = drm; gpusvm->mm = mm; gpusvm->mm_start = mm_start; gpusvm->mm_range = mm_range; @@ -640,8 +720,7 @@ drm_gpusvm_range_alloc(struct drm_gpusvm *gpusvm, range->itree.start = ALIGN_DOWN(fault_addr, chunk_size); range->itree.last = ALIGN(fault_addr + 1, chunk_size) - 1; INIT_LIST_HEAD(&range->entry); - range->pages.notifier_seq = LONG_MAX; - range->pages.flags.migrate_devmem = migrate_devmem ? 1 : 0; + range->flags.migrate_devmem = migrate_devmem ? 1 : 0; return range; } @@ -929,7 +1008,7 @@ retry: * mallocs 16k but the CPU VMA is ~128k which results in 64k SVM * ranges. When migrating the SVM ranges, some processes fail in * drm_pagemap_migrate_to_devmem with 'migrate.cpages != npages' - * and then upon drm_gpusvm_range_get_pages device pages from + * and then upon drm_gpusvm_get_pages device pages from * other processes are collected + faulted in which creates all * sorts of problems. Unsure exactly how this happening, also * problem goes away if 'xe_exec_system_allocator --r @@ -1135,11 +1214,16 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, unsigned long npages) { struct drm_pagemap *dpagemap = svm_pages->dpagemap; - struct device *dev = gpusvm->drm->dev; + struct device *dev; unsigned long i, j; lockdep_assert_held(&gpusvm->notifier_lock); + if (!svm_pages->drm) + return; + + dev = svm_pages->drm->dev; + if (svm_pages->flags.has_dma_mapping) { struct drm_gpusvm_pages_flags flags = { .__flags = svm_pages->flags.__flags, @@ -1222,12 +1306,17 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_free_pages); * This function removes the specified GPU SVM range and also removes the parent * GPU SVM notifier if no more ranges remain in the notifier. The caller must * hold a lock to protect range and notifier removal. + * + * This function does not unmap or free the drm_gpusvm_pages, the driver owns + * that lifecycle. The caller must DMA unmap the range's pages before calling + * this function, so a range is never removed from the MMU interval tree while + * still DMA mapped. Typically the driver calls drm_gpusvm_unmap_pages() first. + * And the range_free callback's drm_gpusvm_free_pages() is a final fallback safe + * net. */ void drm_gpusvm_range_remove(struct drm_gpusvm *gpusvm, struct drm_gpusvm_range *range) { - unsigned long npages = npages_in_range(drm_gpusvm_range_start(range), - drm_gpusvm_range_end(range)); struct drm_gpusvm_notifier *notifier; drm_gpusvm_driver_lock_held(gpusvm); @@ -1239,8 +1328,6 @@ void drm_gpusvm_range_remove(struct drm_gpusvm *gpusvm, return; drm_gpusvm_notifier_lock(gpusvm); - __drm_gpusvm_unmap_pages(gpusvm, &range->pages, npages); - __drm_gpusvm_free_pages(gpusvm, &range->pages); __drm_gpusvm_range_remove(notifier, range); drm_gpusvm_notifier_unlock(gpusvm); @@ -1319,34 +1406,14 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_range_put); * * Return: True if GPU SVM range has valid pages, False otherwise */ -static bool drm_gpusvm_pages_valid(struct drm_gpusvm *gpusvm, - struct drm_gpusvm_pages *svm_pages) +bool drm_gpusvm_pages_valid(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages) { lockdep_assert_held(&gpusvm->notifier_lock); return svm_pages->flags.has_devmem_pages || svm_pages->flags.has_dma_mapping; } - -/** - * drm_gpusvm_range_pages_valid() - GPU SVM range pages valid - * @gpusvm: Pointer to the GPU SVM structure - * @range: Pointer to the GPU SVM range structure - * - * This function determines if a GPU SVM range pages are valid. Expected be - * called holding gpusvm->notifier_lock and as the last step before committing a - * GPU binding. This is akin to a notifier seqno check in the HMM documentation - * but due to wider notifiers (i.e., notifiers which span multiple ranges) this - * function is required for finer grained checking (i.e., per range) if pages - * are valid. - * - * Return: True if GPU SVM range has valid pages, False otherwise - */ -bool drm_gpusvm_range_pages_valid(struct drm_gpusvm *gpusvm, - struct drm_gpusvm_range *range) -{ - return drm_gpusvm_pages_valid(gpusvm, &range->pages); -} -EXPORT_SYMBOL_GPL(drm_gpusvm_range_pages_valid); +EXPORT_SYMBOL_GPL(drm_gpusvm_pages_valid); /** * drm_gpusvm_pages_valid_unlocked() - GPU SVM pages valid unlocked @@ -1421,6 +1488,9 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, DMA_BIDIRECTIONAL; struct dma_iova_state *state = &svm_pages->state; + if (!svm_pages->drm) + return -EINVAL; + retry: if (time_after(jiffies, timeout)) return -EBUSY; @@ -1520,7 +1590,7 @@ map_pages: pagemap = page_pgmap(page); dpagemap = drm_pagemap_page_to_dpagemap(page); - if (drm_WARN_ON(gpusvm->drm, !dpagemap)) { + if (drm_WARN_ON(svm_pages->drm, !dpagemap)) { /* * Raced. This is not supposed to happen * since hmm_range_fault() should've migrated @@ -1532,10 +1602,10 @@ map_pages: } svm_pages->dma_addr[j] = dpagemap->ops->device_map(dpagemap, - gpusvm->drm->dev, + svm_pages->drm->dev, page, order, dma_dir); - if (dma_mapping_error(gpusvm->drm->dev, + if (dma_mapping_error(svm_pages->drm->dev, svm_pages->dma_addr[j].addr)) { err = -EFAULT; goto err_unmap; @@ -1555,11 +1625,11 @@ map_pages: } if (!i) - dma_iova_try_alloc(gpusvm->drm->dev, state, + dma_iova_try_alloc(svm_pages->drm->dev, state, 0, npages * PAGE_SIZE); if (dma_use_iova(state)) { - err = dma_iova_link(gpusvm->drm->dev, state, + err = dma_iova_link(svm_pages->drm->dev, state, hmm_pfn_to_phys(pfns[i]), svm_pages->state_offset, PAGE_SIZE << order, @@ -1570,11 +1640,11 @@ map_pages: addr = state->addr + svm_pages->state_offset; svm_pages->state_offset += PAGE_SIZE << order; } else { - addr = dma_map_page(gpusvm->drm->dev, + addr = dma_map_page(svm_pages->drm->dev, page, 0, PAGE_SIZE << order, dma_dir); - if (dma_mapping_error(gpusvm->drm->dev, addr)) { + if (dma_mapping_error(svm_pages->drm->dev, addr)) { err = -EFAULT; goto err_unmap; } @@ -1590,7 +1660,7 @@ map_pages: } if (dma_use_iova(state)) { - err = dma_iova_sync(gpusvm->drm->dev, state, 0, + err = dma_iova_sync(svm_pages->drm->dev, state, 0, svm_pages->state_offset); if (err) goto err_unmap; @@ -1626,28 +1696,6 @@ err_free: EXPORT_SYMBOL_GPL(drm_gpusvm_get_pages); /** - * drm_gpusvm_range_get_pages() - Get pages for a GPU SVM range - * @gpusvm: Pointer to the GPU SVM structure - * @range: Pointer to the GPU SVM range structure - * @ctx: GPU SVM context - * - * This function gets pages for a GPU SVM range and ensures they are mapped for - * DMA access. - * - * Return: 0 on success, negative error code on failure. - */ -int drm_gpusvm_range_get_pages(struct drm_gpusvm *gpusvm, - struct drm_gpusvm_range *range, - const struct drm_gpusvm_ctx *ctx) -{ - return drm_gpusvm_get_pages(gpusvm, &range->pages, gpusvm->mm, - &range->notifier->notifier, - drm_gpusvm_range_start(range), - drm_gpusvm_range_end(range), ctx); -} -EXPORT_SYMBOL_GPL(drm_gpusvm_range_get_pages); - -/** * drm_gpusvm_unmap_pages() - Unmap GPU svm pages * @gpusvm: Pointer to the GPU SVM structure * @svm_pages: Pointer to the GPU SVM pages structure @@ -1678,29 +1726,6 @@ void drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, EXPORT_SYMBOL_GPL(drm_gpusvm_unmap_pages); /** - * drm_gpusvm_range_unmap_pages() - Unmap pages associated with a GPU SVM range - * @gpusvm: Pointer to the GPU SVM structure - * @range: Pointer to the GPU SVM range structure - * @ctx: GPU SVM context - * - * This function unmaps pages associated with a GPU SVM range. If @in_notifier - * is set, it is assumed that gpusvm->notifier_lock is held in write mode; if it - * is clear, it acquires gpusvm->notifier_lock in read mode. Must be called on - * each GPU SVM range attached to notifier in gpusvm->ops->invalidate for IOMMU - * security model. - */ -void drm_gpusvm_range_unmap_pages(struct drm_gpusvm *gpusvm, - struct drm_gpusvm_range *range, - const struct drm_gpusvm_ctx *ctx) -{ - unsigned long npages = npages_in_range(drm_gpusvm_range_start(range), - drm_gpusvm_range_end(range)); - - return drm_gpusvm_unmap_pages(gpusvm, &range->pages, npages, ctx); -} -EXPORT_SYMBOL_GPL(drm_gpusvm_range_unmap_pages); - -/** * drm_gpusvm_range_evict() - Evict GPU SVM range * @gpusvm: Pointer to the GPU SVM structure * @range: Pointer to the GPU SVM range to be removed @@ -1784,20 +1809,40 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_has_mapping); /** * drm_gpusvm_range_set_unmapped() - Mark a GPU SVM range as unmapped * @range: Pointer to the GPU SVM range structure. + * @pages: Pointer to the GPU SVM pages structure(s). + * @pages_count: Number of GPU SVM pages structure(s) passed in. * @mmu_range: Pointer to the MMU notifier range structure. * * This function marks a GPU SVM range as unmapped and sets the partial_unmap flag * if the range partially falls within the provided MMU notifier range. */ void drm_gpusvm_range_set_unmapped(struct drm_gpusvm_range *range, + struct drm_gpusvm_pages *pages, + unsigned int pages_count, const struct mmu_notifier_range *mmu_range) { + struct drm_gpusvm_range_flags range_flags = { + .__flags = range->flags.__flags, + }; + unsigned int i; + lockdep_assert_held_write(&range->gpusvm->notifier_lock); - range->pages.flags.unmapped = true; + range_flags.unmapped = true; + for (i = 0; i < pages_count; ++i) { + struct drm_gpusvm_pages_flags flags = { + .__flags = pages[i].flags.__flags, + }; + + flags.unmapped = true; + /* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */ + WRITE_ONCE(pages[i].flags.__flags, flags.__flags); + } if (drm_gpusvm_range_start(range) < mmu_range->start || drm_gpusvm_range_end(range) > mmu_range->end) - range->pages.flags.partial_unmap = true; + range_flags.partial_unmap = true; + /* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */ + WRITE_ONCE(range->flags.__flags, range_flags.__flags); } EXPORT_SYMBOL_GPL(drm_gpusvm_range_set_unmapped); diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index d03ada82eac9..96eef327bf7e 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -7,6 +7,7 @@ #include <linux/of_graph.h> #include <drm/drm_bridge.h> +#include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_device.h> #include <drm/drm_encoder.h> @@ -221,6 +222,49 @@ int drm_of_encoder_active_endpoint(struct device_node *node, EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); /** + * drm_of_get_panel_orientation - look up the orientation of the panel through + * the "rotation" binding from a device tree node + * @np: device tree node of the panel + * @orientation: orientation enum to be filled in + * + * Looks up the rotation of a panel in the device tree. The orientation of the + * panel is expressed as a property name "rotation" in the device tree. The + * rotation in the device tree is counter clockwise. + * + * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the + * rotation property doesn't exist. Return a negative error code on failure. + */ +int drm_of_get_panel_orientation(const struct device_node *np, + enum drm_panel_orientation *orientation) +{ + int rotation, ret; + + ret = of_property_read_u32(np, "rotation", &rotation); + if (ret == -EINVAL) { + /* Don't return an error if there's no rotation property. */ + *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; + return 0; + } + + if (ret < 0) + return ret; + + if (rotation == 0) + *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; + else if (rotation == 90) + *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; + else if (rotation == 180) + *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; + else if (rotation == 270) + *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; + else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation); + +/** * drm_of_find_panel_or_bridge - return connected panel or bridge device * @np: device tree node containing encoder output ports * @port: port in the device tree node diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 2c5649e433df..d7c6f4824b2d 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -28,6 +28,7 @@ #include <linux/of.h> #include <drm/drm_crtc.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> @@ -491,49 +492,6 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) return ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL(of_drm_find_panel); - -/** - * of_drm_get_panel_orientation - look up the orientation of the panel through - * the "rotation" binding from a device tree node - * @np: device tree node of the panel - * @orientation: orientation enum to be filled in - * - * Looks up the rotation of a panel in the device tree. The orientation of the - * panel is expressed as a property name "rotation" in the device tree. The - * rotation in the device tree is counter clockwise. - * - * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the - * rotation property doesn't exist. Return a negative error code on failure. - */ -int of_drm_get_panel_orientation(const struct device_node *np, - enum drm_panel_orientation *orientation) -{ - int rotation, ret; - - ret = of_property_read_u32(np, "rotation", &rotation); - if (ret == -EINVAL) { - /* Don't return an error if there's no rotation property. */ - *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; - return 0; - } - - if (ret < 0) - return ret; - - if (rotation == 0) - *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; - else if (rotation == 90) - *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; - else if (rotation == 180) - *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; - else if (rotation == 270) - *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; - else - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL(of_drm_get_panel_orientation); #endif /* Find panel by fwnode. This should be identical to of_drm_find_panel(). */ diff --git a/drivers/gpu/drm/i915/.kunitconfig b/drivers/gpu/drm/i915/.kunitconfig new file mode 100644 index 000000000000..1b47fe62b285 --- /dev/null +++ b/drivers/gpu/drm/i915/.kunitconfig @@ -0,0 +1,9 @@ +CONFIG_EXPERT=y +CONFIG_MODULES=y +CONFIG_KUNIT=y +CONFIG_PCI=y +CONFIG_DEBUG_FS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DRM=y +CONFIG_DRM_I915=y +CONFIG_DRM_I915_KUNIT_TEST=y diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug index 52a3a59b4ba2..dc43dcfbadb6 100644 --- a/drivers/gpu/drm/i915/Kconfig.debug +++ b/drivers/gpu/drm/i915/Kconfig.debug @@ -220,6 +220,18 @@ config DRM_I915_SELFTEST_BROKEN If in doubt, say "N". +config DRM_I915_KUNIT_TEST + tristate "KUnit tests for the drm i915 driver" if !KUNIT_ALL_TESTS + depends on DRM_I915 && KUNIT && DEBUG_FS + default KUNIT_ALL_TESTS + help + Choose this option to allow the driver to perform selftests under + the kunit framework + + Recommended for driver developers only. + + If in doubt, say "N". + config DRM_I915_LOW_LEVEL_TRACEPOINTS bool "Enable low level request tracing events" depends on DRM_I915 diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index c4de717505d7..a83fa8be0aba 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -386,6 +386,8 @@ i915-y += \ i915-$(CONFIG_DRM_I915_DP_TUNNEL) += \ display/intel_dp_tunnel.o +obj-$(CONFIG_DRM_I915_KUNIT_TEST) += display/tests/ + i915-$(CONFIG_DRM_I915_GVT) += \ display/intel_gvt_api.o diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index 9729f1837d2c..eae76e961105 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -38,6 +38,7 @@ #include "intel_de.h" #include "intel_display_types.h" #include "intel_display_wa.h" +#include "intel_dp.h" #include "intel_lpe_audio.h" /** @@ -696,6 +697,13 @@ static void ibx_audio_codec_enable(struct intel_encoder *encoder, mutex_unlock(&display->audio.mutex); } +static +bool intel_audio_needs_cpu_transcoder_id(const struct intel_crtc_state *crtc_state) +{ + return intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST) || + intel_dp_is_uhbr(crtc_state); +} + bool intel_audio_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) @@ -762,6 +770,8 @@ void intel_audio_codec_enable(struct intel_encoder *encoder, audio_state = &display->audio.state[cpu_transcoder]; audio_state->encoder = encoder; + audio_state->needs_cpu_transcoder_id = + intel_audio_needs_cpu_transcoder_id(crtc_state); BUILD_BUG_ON(sizeof(audio_state->eld) != sizeof(crtc_state->eld)); memcpy(audio_state->eld, crtc_state->eld, sizeof(audio_state->eld)); @@ -769,8 +779,12 @@ void intel_audio_codec_enable(struct intel_encoder *encoder, if (acomp && acomp->base.audio_ops && acomp->base.audio_ops->pin_eld_notify) { - /* audio drivers expect cpu_transcoder = -1 to indicate Non-MST cases */ - if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) + /* + * Audio drivers expect cpu_transcoder = -1 to indicate + * Non-MST/HBR cases. MST and UHBR SST are addressed by + * a real cpu_transcoder. + */ + if (!intel_audio_needs_cpu_transcoder_id(crtc_state)) cpu_transcoder = -1; acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr, (int)port, (int)cpu_transcoder); @@ -819,14 +833,19 @@ void intel_audio_codec_disable(struct intel_encoder *encoder, audio_state = &display->audio.state[cpu_transcoder]; audio_state->encoder = NULL; + audio_state->needs_cpu_transcoder_id = false; memset(audio_state->eld, 0, sizeof(audio_state->eld)); mutex_unlock(&display->audio.mutex); if (acomp && acomp->base.audio_ops && acomp->base.audio_ops->pin_eld_notify) { - /* audio drivers expect cpu_transcoder = -1 to indicate Non-MST cases */ - if (!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST)) + /* + * Audio drivers expect cpu_transcoder = -1 to indicate + * Non-MST/HBR cases. MST and UHBR SST are addressed by + * a real cpu_transcoder. + */ + if (!intel_audio_needs_cpu_transcoder_id(old_crtc_state)) cpu_transcoder = -1; acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr, (int)port, (int)cpu_transcoder); @@ -1118,18 +1137,24 @@ static int intel_audio_component_get_cdclk_freq(struct device *kdev) } /* - * get the intel audio state according to the parameter port and cpu_transcoder - * MST & (cpu_transcoder >= 0): return the audio.state[cpu_transcoder].encoder], - * when port is matched - * MST & (cpu_transcoder < 0): this is invalid - * Non-MST & (cpu_transcoder >= 0): only cpu_transcoder = 0 (the first device entry) - * will get the right intel_encoder with port matched - * Non-MST & (cpu_transcoder < 0): get the right intel_encoder with port matched + * Get the intel audio state for a given (port, cpu_transcoder). + * + * Streams are addressed either by a real cpu_transcoder (DP MST and UHBR SST, + * i.e. entries whose stored needs_cpu_transcoder_id is true) or by port alone + * (legacy SST). Both the signalling side (pin_eld_notify()) and the lookup + * side use the same predicate, so the two are symmetric. + * + * cpu_transcoder >= 0 & needs_cpu_transcoder_id: return audio.state[cpu_transcoder] + * when the port matches. + * cpu_transcoder < 0 & !needs_cpu_transcoder_id: return the first port-matching + * entry. + * cpu_transcoder = 0 & !needs_cpu_transcoder_id: falls to the port-only + * loop so the first device entry of a legacy SST port is still found. */ static struct intel_audio_state *find_audio_state(struct intel_display *display, int port, int cpu_transcoder) { - /* MST */ + /* MST, or UHBR SST. */ if (cpu_transcoder >= 0) { struct intel_audio_state *audio_state; struct intel_encoder *encoder; @@ -1142,11 +1167,11 @@ static struct intel_audio_state *find_audio_state(struct intel_display *display, encoder = audio_state->encoder; if (encoder && encoder->port == port && - encoder->type == INTEL_OUTPUT_DP_MST) + audio_state->needs_cpu_transcoder_id) return audio_state; } - /* Non-MST */ + /* Legacy SST. */ if (cpu_transcoder > 0) return NULL; @@ -1158,7 +1183,7 @@ static struct intel_audio_state *find_audio_state(struct intel_display *display, encoder = audio_state->encoder; if (encoder && encoder->port == port && - encoder->type != INTEL_OUTPUT_DP_MST) + !audio_state->needs_cpu_transcoder_id) return audio_state; } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 90c05ad08f86..214454f419e9 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -45,6 +45,7 @@ #include <drm/drm_probe_helper.h> #include <drm/drm_rect.h> #include <drm/drm_vblank.h> +#include <drm/intel/step.h> #include "g4x_dp.h" #include "g4x_hdmi.h" @@ -2737,6 +2738,10 @@ void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, HSYNC_START(adjusted_mode->crtc_hsync_start - 1) | HSYNC_END(adjusted_mode->crtc_hsync_end - 1)); + if (display->platform.novalake && + IS_DISPLAY_STEP(display, STEP_A0, STEP_C0)) + crtc_vtotal = 1; + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); @@ -2830,6 +2835,10 @@ void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, * The double buffer latch point for TRANS_VTOTAL * is the transcoder's undelayed vblank. */ + if (display->platform.novalake && + IS_DISPLAY_STEP(display, STEP_A0, STEP_C0)) + crtc_vtotal = 1; + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 17f7d3abdb9c..7e988b7b1fe7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -97,6 +97,8 @@ struct intel_wm_funcs { struct intel_audio_state { struct intel_encoder *encoder; u8 eld[MAX_ELD_BYTES]; + /* MST, or SST on UHBR link */ + bool needs_cpu_transcoder_id; }; struct intel_audio { diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index ade7e51e7590..0922d23b284c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -354,44 +354,6 @@ static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp) return min3(source_max, sink_max, lane_max); } -int intel_dp_max_lane_count(struct intel_dp *intel_dp) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config max_link_limits; - struct intel_dp_link_config forced_params; - int lane_count; - - intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - - if (forced_params.lane_count) - lane_count = forced_params.lane_count; - else - lane_count = max_link_limits.lane_count; - - switch (lane_count) { - case 1: - case 2: - case 4: - return lane_count; - default: - MISSING_CASE(lane_count); - return 1; - } -} - -static int intel_dp_min_lane_count(struct intel_dp *intel_dp) -{ - struct intel_dp_link_config forced_params; - - intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - - if (forced_params.lane_count) - return forced_params.lane_count; - - return 1; -} - int intel_dp_link_bw_overhead(int link_clock, int lane_count, int hdisplay, int dsc_slice_count, int bpp_x16, unsigned long flags) { @@ -701,7 +663,8 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); if (intel_dp_link_caps_update(intel_dp->link.caps, common_rates, num_common_rates, - intel_dp_get_max_common_lane_count(intel_dp))) + intel_dp_get_max_common_lane_count(intel_dp), + intel_dp->reset_link_params)) params_changed = true; return params_changed; @@ -1330,6 +1293,7 @@ intel_dp_mode_valid_format(struct intel_connector *connector, struct intel_dp *intel_dp = intel_attached_dp(connector); enum intel_output_format output_format; int max_rate, mode_rate, max_lanes, max_link_clock; + struct intel_dp_link_config max_bw_config; u16 dsc_max_compressed_bpp = 0; enum drm_mode_status status; bool dsc = false; @@ -1342,8 +1306,9 @@ intel_dp_mode_valid_format(struct intel_connector *connector, output_format = intel_dp_output_format(connector, sink_format); - max_link_clock = intel_dp_max_link_rate(intel_dp); - max_lanes = intel_dp_max_lane_count(intel_dp); + intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config); + max_link_clock = max_bw_config.rate; + max_lanes = max_bw_config.lane_count; max_rate = intel_dp_max_link_data_rate(intel_dp, max_link_clock, max_lanes); @@ -1537,39 +1502,6 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) intel_dp_link_caps_print_common_rates(intel_dp->link.caps); } -int -intel_dp_max_link_rate(struct intel_dp *intel_dp) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config max_link_limits; - struct intel_dp_link_config forced_params; - int len; - - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - - if (forced_params.rate) - return forced_params.rate; - - intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); - len = intel_dp_common_len_rate_limit(link_caps, max_link_limits.rate); - - return intel_dp_common_rate(link_caps, len - 1); -} - -static int -intel_dp_min_link_rate(struct intel_dp *intel_dp) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config forced_params; - - intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - - if (forced_params.rate) - return forced_params.rate; - - return intel_dp_common_rate(link_caps, 0); -} - int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) { struct intel_display *display = to_intel_display(intel_dp); @@ -1751,43 +1683,42 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state, const struct link_config_limits *limits) { + struct intel_connector *connector = to_intel_connector(conn_state->connector); + int bpp, clock = intel_dp_mode_clock(pipe_config, conn_state); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state); - int link_rate, link_avail; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + int link_avail; for (bpp = fxp_q4_to_int(limits->link.max_bpp_x16); bpp >= fxp_q4_to_int(limits->link.min_bpp_x16); bpp -= 2 * 3) { int link_bpp_x16 = intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp); + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_iter iter; - for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { - link_rate = intel_dp_common_rate(link_caps, i); - if (link_rate < limits->min_rate || - link_rate > limits->max_rate) - continue; - - for (lane_count = limits->min_lane_count; - lane_count <= limits->max_lane_count; - lane_count <<= 1) { - const struct drm_display_mode *adjusted_mode = + intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter); + for_each_dp_link_config(&iter, &link_config) { + const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - int mode_rate = - intel_dp_link_required(link_rate, lane_count, - clock, adjusted_mode->hdisplay, - link_bpp_x16, 0); - - link_avail = intel_dp_max_link_data_rate(intel_dp, - link_rate, - lane_count); - - if (mode_rate <= link_avail) { - pipe_config->lane_count = lane_count; - pipe_config->pipe_bpp = bpp; - pipe_config->port_clock = link_rate; - - return 0; - } + int mode_rate; + + mode_rate = intel_dp_link_required(link_config.rate, + link_config.lane_count, + clock, adjusted_mode->hdisplay, + link_bpp_x16, 0); + + link_avail = intel_dp_max_link_data_rate(intel_dp, + link_config.rate, + link_config.lane_count); + + if (mode_rate <= link_avail) { + pipe_config->lane_count = link_config.lane_count; + pipe_config->pipe_bpp = bpp; + pipe_config->port_clock = link_config.rate; + + return 0; } } } @@ -1986,60 +1917,56 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, const struct link_config_limits *limits, int dsc_bpp_x16) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - int link_rate, lane_count; - int i; - - for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { - link_rate = intel_dp_common_rate(link_caps, i); - if (link_rate < limits->min_rate || link_rate > limits->max_rate) - continue; + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_iter iter; - for (lane_count = limits->min_lane_count; - lane_count <= limits->max_lane_count; - lane_count <<= 1) { + intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter); + for_each_dp_link_config(&iter, &link_config) { + /* + * FIXME: intel_dp_mtp_tu_compute_config() requires + * ->lane_count and ->port_clock set before we know + * they'll work. If we end up failing altogether, + * they'll remain in crtc state. This shouldn't matter, + * as we'd then bail out from compute config, but it's + * just ugly. + */ + pipe_config->lane_count = link_config.lane_count; + pipe_config->port_clock = link_config.rate; - /* - * FIXME: intel_dp_mtp_tu_compute_config() requires - * ->lane_count and ->port_clock set before we know - * they'll work. If we end up failing altogether, - * they'll remain in crtc state. This shouldn't matter, - * as we'd then bail out from compute config, but it's - * just ugly. - */ - pipe_config->lane_count = lane_count; - pipe_config->port_clock = link_rate; - - if (drm_dp_is_uhbr_rate(link_rate)) { - int ret; - - ret = intel_dp_mtp_tu_compute_config(intel_dp, - pipe_config, - conn_state, - dsc_bpp_x16, - dsc_bpp_x16, - 0, true); - if (ret) - continue; - } else { - unsigned long bw_overhead_flags = - pipe_config->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0; - int line_slice_count = - intel_dsc_line_slice_count(&pipe_config->dsc.slice_config); - - if (!is_bw_sufficient_for_dsc_config(intel_dp, - link_rate, lane_count, - adjusted_mode->crtc_clock, - adjusted_mode->hdisplay, - line_slice_count, - dsc_bpp_x16, - bw_overhead_flags)) - continue; - } + if (drm_dp_is_uhbr_rate(link_config.rate)) { + int ret; - return 0; + ret = intel_dp_mtp_tu_compute_config(intel_dp, + pipe_config, + conn_state, + dsc_bpp_x16, + dsc_bpp_x16, + 0, true); + if (ret) + continue; + } else { + unsigned long bw_overhead_flags = + pipe_config->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0; + int line_slice_count = + intel_dsc_line_slice_count(&pipe_config->dsc.slice_config); + + if (!is_bw_sufficient_for_dsc_config(intel_dp, + link_config.rate, + link_config.lane_count, + adjusted_mode->crtc_clock, + adjusted_mode->hdisplay, + line_slice_count, + dsc_bpp_x16, + bw_overhead_flags)) + continue; } + + return 0; } return -EINVAL; @@ -2250,7 +2177,7 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp, int pipe_bpp) { struct intel_display *display = to_intel_display(intel_dp); - const struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_connector *connector = to_intel_connector(conn_state->connector); int min_bpp_x16, max_bpp_x16, bpp_step_x16; int bpp_x16; int ret; @@ -2262,8 +2189,19 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp, max_bpp_x16 = align_max_compressed_bpp_x16(connector, pipe_config->output_format, pipe_bpp, max_bpp_x16); if (intel_dp_is_edp(intel_dp)) { - pipe_config->port_clock = limits->max_rate; - pipe_config->lane_count = limits->max_lane_count; + struct intel_dp_link_config max_link_config; + + /* + * FIXME: Clarify why eDP does not use the regular SST BW + * check and instead always uses the maximum link config, + * regardless of intel_dp::use_max_params. Then unify this eDP + * path with the regular DP path. + */ + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return -EINVAL; + + pipe_config->port_clock = max_link_config.rate; + pipe_config->lane_count = max_link_config.lane_count; pipe_config->dsc.compressed_bpp_x16 = max_bpp_x16; @@ -2576,6 +2514,20 @@ bool intel_dp_mode_valid_with_dsc(struct intel_connector *connector, bw_overhead_flags); } +bool +intel_dp_get_connector_max_link_config(struct intel_connector *connector, + const struct link_config_limits *limits, + struct intel_dp_link_config *max_link_config) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + + return intel_dp_link_caps_get_max_config(link_caps, order.key, limits->link_config_filter, + max_link_config); +} + /* * Calculate the output link min, max bpp values in limits based on the pipe bpp * range, crtc_state and dsc mode. Return true on success. @@ -2592,6 +2544,7 @@ intel_dp_compute_config_link_bpp_limits(struct intel_connector *connector, &crtc_state->hw.adjusted_mode; const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); const struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_dp_link_config max_link_config; int max_link_bpp_x16; max_link_bpp_x16 = min(crtc_state->max_link_bpp_x16, @@ -2621,14 +2574,17 @@ intel_dp_compute_config_link_bpp_limits(struct intel_connector *connector, limits->link.max_bpp_x16 = max_link_bpp_x16; + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return false; + drm_dbg_kms(display->drm, - "[ENCODER:%d:%s][CRTC:%d:%s] DP link limits: pixel clock %d kHz DSC %s max lanes %d max rate %d max pipe_bpp %d min link_bpp " FXP_Q4_FMT " max link_bpp " FXP_Q4_FMT "\n", + "[ENCODER:%d:%s][CRTC:%d:%s] DP link limits: pixel clock %d kHz DSC %s max link %dx%d max pipe_bpp %d min link_bpp " FXP_Q4_FMT " max link_bpp " FXP_Q4_FMT "\n", encoder->base.base.id, encoder->base.name, crtc->base.base.id, crtc->base.name, adjusted_mode->crtc_clock, str_on_off(dsc), - limits->max_lane_count, - limits->max_rate, + max_link_config.lane_count, + max_link_config.rate, limits->pipe.max_bpp, FXP_Q4_ARGS(limits->link.min_bpp_x16), FXP_Q4_ARGS(limits->link.max_bpp_x16)); @@ -2679,17 +2635,12 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, struct link_config_limits *limits) { struct intel_display *display = to_intel_display(intel_dp); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST); struct intel_connector *connector = to_intel_connector(conn_state->connector); - limits->min_rate = intel_dp_min_link_rate(intel_dp); - limits->max_rate = intel_dp_max_link_rate(intel_dp); - - limits->min_rate = min(limits->min_rate, limits->max_rate); - - limits->min_lane_count = intel_dp_min_lane_count(intel_dp); - limits->max_lane_count = intel_dp_max_lane_count(intel_dp); + limits->link_config_filter = INTEL_DP_LINK_CAPS_FILTER_ALL; limits->pipe.min_bpp = intel_dp_min_bpp(crtc_state->output_format); if (is_mst) { @@ -2754,6 +2705,9 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, crtc_state->pipe_bpp, limits->pipe.max_bpp); if (is_mst || intel_dp->use_max_params) { + struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE; + struct intel_dp_link_config max_config; + /* * For MST we always configure max link bw - the spec doesn't * seem to suggest we should do otherwise. @@ -2765,11 +2719,17 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, * configuration, and typically on older panels these * values correspond to the native resolution of the panel. */ - limits->min_lane_count = limits->max_lane_count; - limits->min_rate = limits->max_rate; + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_config)) + return false; + + if (!intel_dp_link_caps_filter_add(link_caps, &new_filter, &max_config)) + return false; + + limits->link_config_filter = new_filter; } - intel_dp_test_compute_config(intel_dp, crtc_state, limits); + if (!intel_dp_test_compute_config(connector, crtc_state, limits)) + return false; return intel_dp_compute_config_link_bpp_limits(connector, crtc_state, @@ -3652,6 +3612,11 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, void intel_dp_reset_link_params(struct intel_dp *intel_dp) { + /* + * TODO: Remove the following reset of link capabilities, as + * this isn't needed after intel_dp_link_caps_update(reset=true) + * was called. + */ intel_dp_link_caps_reset(intel_dp->link.caps); intel_dp->link.mst_probed_lane_count = 0; intel_dp->link.mst_probed_rate = 0; diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 02b691df6755..0ec519fa1236 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -8,6 +8,8 @@ #include <linux/types.h> +#include "intel_dp_link_caps.h" + enum intel_output_format; enum pipe; enum port; @@ -22,11 +24,11 @@ struct intel_crtc_state; struct intel_digital_port; struct intel_display; struct intel_dp; +struct intel_dp_link_config; struct intel_encoder; struct link_config_limits { - int min_rate, max_rate; - int min_lane_count, max_lane_count; + struct intel_dp_link_caps_filter link_config_filter; struct { /* Uncompressed DSC input or link output bpp in 1 bpp units */ int min_bpp, max_bpp; @@ -103,8 +105,6 @@ void intel_dp_mst_suspend(struct intel_display *display); void intel_dp_mst_resume(struct intel_display *display); int intel_dp_rate_limit_len(const int *rates, int len, int max_rate); int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port); -int intel_dp_max_link_rate(struct intel_dp *intel_dp); -int intel_dp_max_lane_count(struct intel_dp *intel_dp); int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); int intel_dp_rate_index(const int *rates, int len, int rate); @@ -144,6 +144,9 @@ int intel_dp_dsc_compute_max_bpp(const struct intel_connector *connector, u8 dsc_max_bpc); int intel_dp_compute_min_compressed_bpp_x16(struct intel_connector *connector, enum intel_output_format output_format); +bool intel_dp_get_connector_max_link_config(struct intel_connector *connector, + const struct link_config_limits *limits, + struct intel_dp_link_config *max_link_config); bool intel_dp_mode_valid_with_dsc(struct intel_connector *connector, int link_clock, int lane_count, int mode_clock, int mode_hdisplay, diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 1c34ba6c49c3..7b6cc6055da8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -16,9 +16,91 @@ #include "intel_display_core.h" #include "intel_display_types.h" +#include "intel_display_utils.h" #include "intel_dp.h" #include "intel_dp_link_caps.h" +/** + * DOC: DisplayPort link capabilities + * + * The Intel DP link caps API tracks the supported and allowed + * DisplayPort link configurations for a DP encoder and its attached + * connectors, and provides helpers to iterate over the allowed + * configurations and constrain them by filtering, disabling, or + * limiting them to maximum link parameters. + * + * Locking + * ------- + * + * All accesses to this API must be serialized. The only exception + * is intel_dp_link_caps_get_max_limits(), which allow lockless + * lookup. Such lookups may observe an out-of-sync &struct + * intel_dp_link_config tuple, i.e. a rate from one state and a lane + * count from another. + * + * The Intel i915/xe drivers ensure the above serialization by holding + * &drm_mode_config.connection_mutex and, while holding the lock, + * waiting for any pending asynchronous atomic commits. This also allows + * use of the API from the tails of asynchronous atomic commits, which + * cannot hold the lock. + * + * Iterating and restricting link configurations + * --------------------------------------------- + * + * The link configuration iterators can iterate the ``allowed + * configurations`` during modeset configuration selection or link + * training fallback handling in a configurable order. + * + * The iteration order can depend on connector type (eDP, DP SST, + * DP MST) and modeset-specific conditions or driver policies, such + * as DSC vs. non-DSC modes, power saving vs. better user experience, + * or policy changes after a link training failure. + * + * The configurations exposed via the iterators can be additionally + * constrained in the following ways: + * + * - Filtered for a given modeset based on modeset-specific conditions. + * Examples for such conditions include driver policies preferring + * power saving or better user experience, post-link training failure + * preference changes, or sink automated test requests limiting the + * usable configurations. + * + * - Disabled permanently for the connected sink. Examples of reasons + * to disable a configuration include a link training failure for a + * given configuration or a driver workaround preventing the use of + * a particular configuration. + * + * - Limited via a maximum link rate and lane count. For example, after + * a link training failure, subsequent modesets may be limited to + * configurations at or below the failed parameters. + * + * This mechanism exists for backward compatibility only. Eventually, + * it will be removed in favor of relying solely on individually + * disabled configurations, as described above. + * + * Terminology + * ----------- + * + * ``Common link capabilities`` (or ``common caps``) refer to the link + * rates and maximum lane count supported by both the source and the + * sink, i.e. the intersection of their respective capabilities. + * + * ``Supported configurations`` are all configurations defined by the + * ``Common link capabilities``' link rates and maximum lane count. + * + * ``Disabled configurations`` are ``Supported configurations`` disabled + * via this API. + * + * ``Enabled configurations`` are ``Supported configurations`` that are + * not disabled. + * + * ``Forced configurations`` are ``Enabled configurations`` forced via + * forced link parameter debugfs entries. + * + * ``Allowed configurations`` are the ``Enabled configurations``, or if + * forcing is in effect the ``Forced configurations``, constrained by a + * maximum rate and lane count set via the API. + */ struct intel_dp_link_caps { struct intel_dp *dp; @@ -42,6 +124,44 @@ struct intel_dp_link_caps { } configs[INTEL_DP_MAX_LINK_CONFIGS]; /* + * Indices to intel_dp_link_caps::configs[] in rate/lane count, + * lane_count/rate order. + */ + u8 rate_lane_map[INTEL_DP_MAX_LINK_CONFIGS]; + u8 lane_rate_map[INTEL_DP_MAX_LINK_CONFIGS]; + + /* + * Filter of configurations enabled for the current sink + * connection. + * + * Each bit in the filter's configuration mask corresponds to a + * configuration index in the intel_dp_link_caps::configs[] array. + * + * All configurations start out enabled in the filter after a + * new sink is connected. Users disable configurations afterwards + * via the link caps API. All configurations get re-enabled + * internally in the following cases: + * - when forcing a link rate or lane count + * - when intel_dp_link_caps_update(reset=true) is called after + * a new sink is connected + * - when intel_dp_link_caps_update(reset=false) with changed + * link capabilities is called + * - when intel_dp_link_caps_reset() is called after a new sink + * is connected + */ + struct intel_dp_link_caps_filter enabled_configs; + + /* + * Allowed configurations are the supported configurations defined by + * config_table.rates and config_table.max_lane_count, constrained by + * config_table.enabled_configs and the forced_params and + * max_limits values below. + * + * See get_allowed_config_filter() for the filter of these + * configurations. + */ + + /* * Forced parameters requested via debugfs. Remains set across sink * disconnects. */ @@ -58,16 +178,78 @@ struct intel_dp_link_caps { */ struct intel_dp_link_config max_limits; }; +static_assert(BITS_PER_TYPE(((struct intel_dp_link_caps_filter *)NULL)->config_mask) >= + ARRAY_SIZE(((struct intel_dp_link_caps *)NULL)->configs)); + +static struct intel_dp_link_caps_order bw_desc_config_order(void) +{ + struct intel_dp_link_caps_order order = { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }; + + return order; +} + +static enum intel_dp_link_caps_order_key +connector_compute_order_key(bool is_mst) +{ + if (is_mst) + return INTEL_DP_LINK_CAPS_ORDER_KEY_BW; + else + return INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE; +} + +static enum intel_dp_link_caps_order_key +connector_fallback_order_key(bool is_mst) +{ + if (is_mst) + return INTEL_DP_LINK_CAPS_ORDER_KEY_BW; + else + return INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE; +} + +static enum intel_dp_link_caps_order_direction +connector_compute_order_dir(bool is_mst, bool use_max_params) +{ + if (is_mst || use_max_params) + return INTEL_DP_LINK_CAPS_ORDER_DIR_DESC; + else + return INTEL_DP_LINK_CAPS_ORDER_DIR_ASC; +} + +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_compute_order(struct intel_connector *connector) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps_order order = { + .key = connector_compute_order_key(connector->mst.dp), + .dir = connector_compute_order_dir(connector->mst.dp, intel_dp->use_max_params) + }; + + return order; +} + +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_fallback_order(bool is_mst) +{ + struct intel_dp_link_caps_order order = { + .key = connector_fallback_order_key(is_mst), + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }; + + return order; +} /* Get length of common rates array potentially limited by max_rate. */ -int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, - int max_rate) +static int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, + int max_rate) { return intel_dp_rate_limit_len(link_caps->rates, link_caps->num_rates, max_rate); } -int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) +static int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) { struct intel_display *display = to_intel_display(link_caps->dp); @@ -78,24 +260,12 @@ int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) return link_caps->rates[index]; } -int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate) -{ - return intel_dp_rate_index(link_caps->rates, - link_caps->num_rates, - rate); -} - /* Theoretical max between source and sink */ -int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) +static int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) { return intel_dp_common_rate(link_caps, link_caps->num_rates - 1); } -int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps) -{ - return link_caps->num_rates; -} - void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) { struct intel_display *display = to_intel_display(link_caps->dp); @@ -108,7 +278,7 @@ void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); } -int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) +static int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) { return link_caps->max_lane_count; } @@ -154,20 +324,353 @@ static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_ent return 1 << lce->lane_count_exp; } -static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, - const struct intel_dp_link_config *max_link_limits) +static void +to_intel_dp_link_config(struct intel_dp_link_caps *link_caps, + int config_idx, struct intel_dp_link_config *config) +{ + const struct intel_dp_link_config_entry *lce = &link_caps->configs[config_idx]; + + config->rate = intel_dp_link_config_rate(link_caps, lce); + config->lane_count = intel_dp_link_config_lane_count(lce); +} + +static int +iter_pos_to_idx(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order config_order, + int iter_pos) +{ + int config_idx; + + if (!in_range(iter_pos, 0, link_caps->num_configs)) + return -1; + + switch (config_order.dir) { + case INTEL_DP_LINK_CAPS_ORDER_DIR_ASC: + break; + case INTEL_DP_LINK_CAPS_ORDER_DIR_DESC: + iter_pos = link_caps->num_configs - 1 - iter_pos; + + break; + default: + MISSING_CASE(config_order.dir); + + return -1; + } + + switch (config_order.key) { + case INTEL_DP_LINK_CAPS_ORDER_KEY_BW: + config_idx = iter_pos; + + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: + config_idx = link_caps->rate_lane_map[iter_pos]; + + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: + config_idx = link_caps->lane_rate_map[iter_pos]; + + break; + default: + MISSING_CASE(config_order.key); + + return -1; + } + + return config_idx; +} + +static bool iter_get_next_config(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_config *config) +{ + while (true) { + int config_idx; + + iter->pos++; + + config_idx = iter_pos_to_idx(iter->link_caps, iter->order, iter->pos); + if (config_idx < 0) { + iter->pos = -1; + *config = INTEL_DP_LINK_CONFIG_NULL; + + break; + } + + if (!(BIT(config_idx) & iter->filter.config_mask)) + continue; + + to_intel_dp_link_config(iter->link_caps, config_idx, config); + + break; + } + + return iter->pos >= 0; +} + +static void iter_start(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order order, + struct intel_dp_link_caps_filter filter) +{ + iter->link_caps = link_caps; + iter->pos = -1; + iter->order = order; + iter->filter = filter; + + iter->get_next_config = iter_get_next_config; +} + +static struct intel_dp_link_caps_filter +calc_allowed_config_filter(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter enabled_configs, + const struct intel_dp_link_config *max_limits, + const struct intel_dp_link_config *forced_params) +{ + struct intel_dp_link_caps_filter allowed_configs = INTEL_DP_LINK_CAPS_FILTER_NONE; + struct intel_dp_link_caps_order order = bw_desc_config_order(); + struct intel_dp_link_caps_iter iter; + struct intel_dp_link_config config; + + iter_start(&iter, link_caps, order, enabled_configs); + for_each_dp_link_config(&iter, &config) { + if (forced_params->rate && + forced_params->rate != config.rate) + continue; + + if (forced_params->lane_count && + forced_params->lane_count != config.lane_count) + continue; + + if (config.rate > max_limits->rate) + continue; + + if (config.lane_count > max_limits->lane_count) + continue; + + allowed_configs.config_mask |= BIT(iter_pos_to_idx(link_caps, order, iter.pos)); + } + intel_dp_link_caps_iter_end(&iter); + + return allowed_configs; +} + +/* + * get_allowed_config_filter - get filter for the currently allowed configs + * @link_caps: link capabilities state + * + * Return: + * Filter of link configurations allowed after applying the current + * maximum link limits, and further narrowing them by removing any disabled + * configuration and limiting to forced link parameters. + * + * See also: + * - intel_dp_link_caps_get_max_limits() + * - intel_dp_link_caps_get_forced_params() + */ +static struct intel_dp_link_caps_filter +get_allowed_config_filter(struct intel_dp_link_caps *link_caps) +{ + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + return calc_allowed_config_filter(link_caps, link_caps->enabled_configs, + &link_caps->max_limits, &forced_params); +} + +void intel_dp_link_caps_iter_start(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order order, + struct intel_dp_link_caps_filter filter) +{ + filter.config_mask &= get_allowed_config_filter(link_caps).config_mask; + + iter_start(iter, link_caps, order, filter); +} + +void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter) +{ + memset(iter, 0, sizeof(*iter)); +} + +/** + * intel_dp_link_caps_get_max_config - get the maximum config in a given order + * @link_caps: link capabilities state + * @order_key: ordering key used to rank candidate configurations + * @filter: filter for candidate configurations + * @max_config: returned maximum link configuration + * + * Find the last configuration among the currently allowed + * configurations filtered by @filter in the iteration order + * selected by @order_key, and store it in @max_config. + * + * See also: + * - &enum intel_dp_link_caps_order_key + * + * Returns: + * %true if a maximum config is returned + * %false otherwise. + */ +bool intel_dp_link_caps_get_max_config(struct intel_dp_link_caps *link_caps, + enum intel_dp_link_caps_order_key order_key, + struct intel_dp_link_caps_filter filter, + struct intel_dp_link_config *max_config) +{ + struct intel_dp_link_caps_order order = { + .key = order_key, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC + }; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + bool found = false; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, filter); + for_each_dp_link_config(&iter, &iter_config) { + found = true; + break; + } + intel_dp_link_caps_iter_end(&iter); + + if (!found) + return false; + + *max_config = iter_config; + + return true; +} + +/** + * intel_dp_link_caps_get_max_bw_config - get maximum BW link configuration + * @link_caps: link capabilities state + * @max_config: returned maximum link configuration + * + * Return the maximum BW link configuration among the currently + * allowed configurations. + */ +void intel_dp_link_caps_get_max_bw_config(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_config) +{ + if (!intel_dp_link_caps_get_max_config(link_caps, + bw_desc_config_order().key, INTEL_DP_LINK_CAPS_FILTER_ALL, + max_config)) + *max_config = INTEL_DP_LINK_CONFIG_NULL; +} + +static int find_config_idx(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter filter, + const struct intel_dp_link_config *link_config) +{ + struct intel_dp_link_caps_order order = bw_desc_config_order(); + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int pos = -1; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, filter); + for_each_dp_link_config(&iter, &iter_config) { + if (iter_config.rate == link_config->rate && + iter_config.lane_count == link_config->lane_count) { + pos = iter.pos; + + break; + } + } + intel_dp_link_caps_iter_end(&iter); + + if (pos < 0) + return pos; + + return iter_pos_to_idx(link_caps, order, pos); +} + +bool intel_dp_link_caps_filter_add(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter *filter, + const struct intel_dp_link_config *config) +{ + int idx; + + idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config); + if (idx < 0) + return false; + + filter->config_mask |= BIT(idx); + + return true; +} + +static bool intel_dp_link_caps_filter_remove(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter *filter, + const struct intel_dp_link_config *config) +{ + int idx; + + idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config); + if (idx < 0) + return false; + + filter->config_mask &= ~BIT(idx); + + return true; +} + +static void set_max_link_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) { link_caps->max_limits = *max_link_limits; } -static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps) +static void reset_max_link_limits(struct intel_dp_link_caps *link_caps) { struct intel_dp_link_config max_link_limits = { .rate = intel_dp_max_common_rate(link_caps), .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps), }; - set_max_link_limits_no_update(link_caps, &max_link_limits); + set_max_link_limits(link_caps, &max_link_limits); +} + +static void reset_max_link_limits_reenable_all(struct intel_dp_link_caps *link_caps) +{ + link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL; + reset_max_link_limits(link_caps); +} + +/** + * intel_dp_link_caps_disable_config - disable a configuration + * @link_caps: link capabilities state + * @config: configuration to disable + * + * Disable the configuration identified by @config. This removes the + * configuration from the set of allowed configurations. The disabling + * shouldn't leave the remaining configuration set empty. + * + * The configuration remains disallowed until intel_dp_link_caps() with + * reset=%true or changed sink capabilities is called, or + * intel_dp_link_caps_reset() is called. Each of these happens after a + * new sink is connected or the currently connected sink changes its + * capabilities. + * + * Return: + * - %true if @config was valid and the derived state was updated. + * - %false if @config was invalid or the remaining configuration set + * would remain empty. + */ +bool intel_dp_link_caps_disable_config(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *config) +{ + struct intel_dp_link_caps_filter enabled_configs = link_caps->enabled_configs; + struct intel_dp_link_config forced_params; + + if (!intel_dp_link_caps_filter_remove(link_caps, &enabled_configs, config)) + return false; + + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + if (!calc_allowed_config_filter(link_caps, enabled_configs, + &link_caps->max_limits, &forced_params).config_mask) + return false; + + link_caps->enabled_configs = enabled_configs; + + return true; } /** @@ -197,6 +700,25 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, *max_link_limits = link_caps->max_limits; } +static bool max_link_limits_valid(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + struct intel_dp_link_caps_filter allowed_configs; + struct intel_dp_link_config forced_params; + + if (max_link_limits->lane_count > INTEL_DP_MAX_LANE_COUNT || + !is_power_of_2(max_link_limits->lane_count)) + return false; + + /* TODO: Validate max_link_limits->rate against the source supported rates. */ + + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + allowed_configs = calc_allowed_config_filter(link_caps, link_caps->enabled_configs, + max_link_limits, &forced_params); + + return allowed_configs.config_mask != 0; +} + /** * intel_dp_link_caps_set_max_limits - set the current maximum link limits * @link_caps: link capabilities state @@ -205,6 +727,10 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, * Set the current maximum rate and lane count limits to @max_link_limits, * constraining the set of allowed configurations. * + * The new limits must leave at least one configuration allowed: the limits + * must not be below the currently active forced parameters or below all the + * configurations that remain after disabled configurations are excluded. + * * Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize * this call against concurrent queries and updates to @link_caps, in line * with the rest of the API. @@ -217,9 +743,11 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits) { - set_max_link_limits_no_update(link_caps, max_link_limits); + if (!max_link_limits_valid(link_caps, max_link_limits)) + return false; + + set_max_link_limits(link_caps, max_link_limits); - /* TODO: validate max_link_limits */ return true; } @@ -232,7 +760,7 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, */ void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) { - reset_max_link_limits_no_update(link_caps); + reset_max_link_limits(link_caps); } static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps, @@ -259,14 +787,70 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) intel_dp_link_config_rate(link_caps, lce_b); } -/* Return %true if the supported link parameters have changed. */ +static int link_config_cmp_by_rate_lane(const void *a, const void *b, const void *p) +{ + const struct intel_dp_link_caps *link_caps = p; + u8 *lce_a_idx = (u8 *)a; + u8 *lce_b_idx = (u8 *)b; + const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx]; + const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx]; + + if (lce_a->link_rate_idx != lce_b->link_rate_idx) + return lce_a->link_rate_idx - lce_b->link_rate_idx; + + return lce_a->lane_count_exp - lce_b->lane_count_exp; +} + +static int link_config_cmp_by_lane_rate(const void *a, const void *b, const void *p) +{ + const struct intel_dp_link_caps *link_caps = p; + u8 *lce_a_idx = (u8 *)a; + u8 *lce_b_idx = (u8 *)b; + const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx]; + const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx]; + + if (lce_a->lane_count_exp != lce_b->lane_count_exp) + return lce_a->lane_count_exp - lce_b->lane_count_exp; + + return lce_a->link_rate_idx - lce_b->link_rate_idx; +} + +/** + * intel_dp_link_caps_update - rebuild the supported link configuration state + * @link_caps: link capabilities state + * @rates: supported common link rates + * @num_rates: number of entries in @rates + * @max_lane_count: supported maximum lane count + * @reset: reset limits and disabled configs + * + * Rebuild the supported link configuration state from @rates and + * @max_lane_count. + * + * If @reset is %true, reset the maximum link limits to the maximum + * supported rate and lane count, and re-enable all configurations. + * + * This function is called regularly, at least after a sink is connected, + * but it may also be called later whenever the sink capabilities may have + * changed, for example in response to HPD IRQ / RX_CAP_CHANGED signaling. + * + * In the Intel driver this function is currently called whenever the + * connector detect handler runs, after reading the sink capabilities. This + * may change if those capabilities are cached until the sink is + * disconnected, or until RX_CAP_CHANGED is signaled. In any case, this + * function should be called whenever the sink capabilities were read out + * and may have changed. + * + * Returns: + * - %true if the link capabilities have changed, %false otherwise. + */ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, - const int *rates, int num_rates, int max_lane_count) + const int *rates, int num_rates, int max_lane_count, + bool reset) { struct intel_dp *intel_dp = link_caps->dp; struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_config_entry *lce; - bool link_params_changed = false; + bool link_params_changed = reset; int num_common_lane_configs; int i; int j; @@ -313,41 +897,25 @@ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, link_config_cmp_by_bw, NULL, intel_dp); - return link_params_changed; -} - -void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, - int idx, int *link_rate, int *lane_count) -{ - struct intel_display *display = to_intel_display(link_caps->dp); - const struct intel_dp_link_config_entry *lce; - - if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs)) - idx = 0; - - lce = &link_caps->configs[idx]; - - *link_rate = intel_dp_link_config_rate(link_caps, lce); - *lane_count = intel_dp_link_config_lane_count(lce); -} + for (i = 0; i < link_caps->num_configs; i++) { + link_caps->rate_lane_map[i] = i; + link_caps->lane_rate_map[i] = i; + } -int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, - int link_rate, int lane_count) -{ - int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates, - link_rate); - int lane_count_exp = ilog2(lane_count); - int i; + sort_r(link_caps->rate_lane_map, link_caps->num_configs, + sizeof(link_caps->rate_lane_map[0]), + link_config_cmp_by_rate_lane, NULL, + link_caps); - for (i = 0; i < link_caps->num_configs; i++) { - const struct intel_dp_link_config_entry *lce = &link_caps->configs[i]; + sort_r(link_caps->lane_rate_map, link_caps->num_configs, + sizeof(link_caps->lane_rate_map[0]), + link_config_cmp_by_lane_rate, NULL, + link_caps); - if (lce->lane_count_exp == lane_count_exp && - lce->link_rate_idx == link_rate_idx) - return i; - } + if (link_params_changed) + reset_max_link_limits_reenable_all(link_caps); - return -1; + return link_params_changed; } /** @@ -364,8 +932,7 @@ int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, */ void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps) { - /* TODO: Update the maximum link information. */ - reset_max_link_limits_no_update(link_caps); + reset_max_link_limits_reenable_all(link_caps); } static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) @@ -628,6 +1195,43 @@ static int i915_dp_max_lane_count_show(void *data, u64 *val) } DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); +static int intel_dp_allowed_link_configs_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_iter iter; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + i = 0; + intel_dp_link_caps_iter_start(&iter, + link_caps, + intel_dp_link_caps_connector_compute_order(connector), + INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &link_config) { + seq_printf(m, "%s%dx%d", + i ? " " : "", + link_config.lane_count, link_config.rate); + i++; + } + intel_dp_link_caps_iter_end(&iter); + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_putc(m, '\n'); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(intel_dp_allowed_link_configs); /** * intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector @@ -654,6 +1258,9 @@ void intel_dp_link_caps_debugfs_add(struct intel_connector *connector) debugfs_create_file("i915_dp_max_lane_count", 0444, root, connector, &i915_dp_max_lane_count_fops); + + debugfs_create_file("intel_dp_allowed_link_configs", 0444, root, + connector, &intel_dp_allowed_link_configs_fops); } struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) @@ -665,6 +1272,7 @@ struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) return NULL; link_caps->dp = intel_dp; + link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL; return link_caps; } @@ -673,3 +1281,32 @@ void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps) { kfree(link_caps); } + +#if IS_ENABLED(CONFIG_KUNIT) + +#define __INIT_MEMBER(__name, __fn) \ + .__name = __fn, + +#define INTEL_DP_LINK_CAPS_TEST_OPS_INIT \ + INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__INIT_MEMBER) + +#ifdef I915 + +const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops = { + INTEL_DP_LINK_CAPS_TEST_OPS_INIT +}; +EXPORT_SYMBOL(i915_display_dp_link_caps_test_ops); + +#else + +const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops = { + INTEL_DP_LINK_CAPS_TEST_OPS_INIT +}; +EXPORT_SYMBOL(intel_display_dp_link_caps_test_ops); + +#endif /* I915 */ + +#undef INTEL_DP_LINK_CAPS_TEST_OPS_INIT +#undef __INIT_MEMBER + +#endif /* CONFIG_KUNIT */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index af9028e7cb98..a0a88efb9546 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -4,6 +4,7 @@ #ifndef __INTEL_DP_LINK_CAPS_H__ #define __INTEL_DP_LINK_CAPS_H__ +#include <linux/bitops.h> #include <linux/types.h> struct intel_connector; @@ -11,23 +12,132 @@ struct intel_dp; struct intel_dp_link_caps; struct intel_dp_link_config; -int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, - int max_rate); -int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index); -int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate); -int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps); -int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); -int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps); +/** + * enum intel_dp_link_caps_order_key - key used to order configurations + * @INTEL_DP_LINK_CAPS_ORDER_KEY_BW: + * Order configurations by bandwidth, then by link rate. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: + * Order configurations by link rate, then by lane count. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: + * Order configurations by lane count, then by link rate. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_NUM: + * Number of ordering keys. + * + * Selects how a caller wants the configuration table to be ordered, + * together with an &enum intel_dp_link_caps_order_direction, for + * iteration queries. + * + * See also: + * - &struct intel_dp_link_caps_order + * - intel_dp_link_caps_get_max_config() + */ +enum intel_dp_link_caps_order_key { + INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE, + + INTEL_DP_LINK_CAPS_ORDER_KEY_NUM +}; + +/** + * enum intel_dp_link_caps_order_direction - iteration direction + * @INTEL_DP_LINK_CAPS_ORDER_DIR_ASC: + * Iterate in ascending order according to the selected ordering key. + * @INTEL_DP_LINK_CAPS_ORDER_DIR_DESC: + * Iterate in descending order according to the selected ordering key. + * @INTEL_DP_LINK_CAPS_ORDER_DIR_NUM: + * Number of ordering directions. + * + * Selects the direction associated with an + * &enum intel_dp_link_caps_order_key for iteration queries. + * + * See also: + * - &struct intel_dp_link_caps_order + */ +enum intel_dp_link_caps_order_direction { + INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + + INTEL_DP_LINK_CAPS_ORDER_DIR_NUM +}; + +/** + * struct intel_dp_link_caps_order - configuration ordering + * @key: + * Key used to order configurations. + * @dir: + * Direction of the selected ordering. + * + * Describes an iteration order for link configurations. + * + * See also: + * - for_each_dp_link_config() + */ +struct intel_dp_link_caps_order { + enum intel_dp_link_caps_order_key key; + enum intel_dp_link_caps_order_direction dir; +}; + +struct intel_dp_link_caps_filter { + u32 config_mask; +}; + +#define INTEL_DP_LINK_CAPS_FILTER_NONE \ + ((struct intel_dp_link_caps_filter){ .config_mask = 0 }) +#define INTEL_DP_LINK_CAPS_FILTER_ALL \ + ((struct intel_dp_link_caps_filter){ .config_mask = (u32)-1 }) + +struct intel_dp_link_caps_iter { + struct intel_dp_link_caps *link_caps; + int pos; + struct intel_dp_link_caps_order order; + struct intel_dp_link_caps_filter filter; + + bool (*get_next_config)(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_config *config); +}; + +/** + * for_each_dp_link_config - iterate allowed link configurations + * @__iter: + * &struct intel_dp_link_caps_iter being iterated + * @__config: + * pointer to &struct intel_dp_link_config filled for each match + */ +#define for_each_dp_link_config(__iter, __config) \ + while ((__iter)->get_next_config((__iter), (__config))) + +void intel_dp_link_caps_iter_start(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order order, + struct intel_dp_link_caps_filter filter); + +void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter); + +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_compute_order(struct intel_connector *connector); +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_fallback_order(bool is_mst); void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps); void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params); -int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, - int link_rate, int lane_count); -void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, - int idx, int *link_rate, int *lane_count); +bool intel_dp_link_caps_filter_add(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter *filter, + const struct intel_dp_link_config *config); + +bool intel_dp_link_caps_get_max_config(struct intel_dp_link_caps *link_caps, + enum intel_dp_link_caps_order_key order_key, + struct intel_dp_link_caps_filter filter, + struct intel_dp_link_config *max_config); + +void intel_dp_link_caps_get_max_bw_config(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_config); + +bool intel_dp_link_caps_disable_config(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *config); void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits); @@ -36,7 +146,8 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps); bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, - const int *rates, int num_rates, int max_lane_count); + const int *rates, int num_rates, int max_lane_count, + bool reset); void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps); void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); @@ -44,4 +155,41 @@ void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); +#if IS_ENABLED(CONFIG_KUNIT) + +#define INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__X) \ + __X(connector_compute_order, intel_dp_link_caps_connector_compute_order) \ + __X(connector_fallback_order, intel_dp_link_caps_connector_fallback_order) \ + __X(iter_start, intel_dp_link_caps_iter_start) \ + __X(iter_end, intel_dp_link_caps_iter_end) \ + __X(set_max_limits, intel_dp_link_caps_set_max_limits) \ + __X(get_max_limits, intel_dp_link_caps_get_max_limits) \ + __X(get_max_bw_config, intel_dp_link_caps_get_max_bw_config) \ + __X(reset_max_limits, intel_dp_link_caps_reset_max_limits) \ + __X(disable_config, intel_dp_link_caps_disable_config) \ + __X(update, intel_dp_link_caps_update) \ + __X(init, intel_dp_link_caps_init) \ + __X(cleanup, intel_dp_link_caps_cleanup) + +#define __DECLARE_MEMBER(__name, __fn) \ + typeof(__fn) *__name; + +#define INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE \ + INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__DECLARE_MEMBER) + +struct intel_dp_link_caps_test_ops { + INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE +}; + +#undef INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE +#undef __DECLARE_MEMBER + +#ifdef I915 +extern const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops; +#else +extern const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops; +#endif /* I915 */ + +#endif /* CONFIG_KUNIT */ + #endif /* __INTEL_DP_LINK_CAPS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index b521dd11b62a..fa55664c9d98 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -21,6 +21,8 @@ * IN THE SOFTWARE. */ +#include <kunit/visibility.h> + #include <linux/debugfs.h> #include <linux/iopoll.h> @@ -1846,124 +1848,62 @@ static bool intel_dp_can_link_train_fallback_for_edp(struct intel_dp *intel_dp, return true; } -static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - int *new_link_rate, int *new_lane_count) +static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, + int *new_link_rate, int *new_lane_count) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config forced_params; - int link_rate; - int lane_count; - int i; - - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST); + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_fallback_order(is_mst); + struct intel_dp_link_config old_config = { + .rate = crtc_state->port_clock, + .lane_count = crtc_state->lane_count, + }; + struct intel_dp_link_caps_iter iter; + struct intel_dp_link_config config; + bool old_found = false; + bool new_found = false; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &config) { + if (!old_found) { + if (config.rate == old_config.rate && + config.lane_count == old_config.lane_count) + old_found = true; - i = intel_dp_link_config_index(intel_dp->link.caps, - crtc_state->port_clock, crtc_state->lane_count); - for (i--; i >= 0; i--) { - intel_dp_link_config_get(intel_dp->link.caps, i, &link_rate, &lane_count); + continue; + } - if ((forced_params.rate && - forced_params.rate != link_rate) || - (forced_params.lane_count && - forced_params.lane_count != lane_count)) + if (!is_mst && + drm_dp_is_uhbr_rate(config.rate) != + drm_dp_is_uhbr_rate(old_config.rate)) continue; + *new_link_rate = config.rate; + *new_lane_count = config.lane_count; + new_found = true; + break; } + intel_dp_link_caps_iter_end(&iter); - if (i < 0) - return false; - - *new_link_rate = link_rate; - *new_lane_count = lane_count; - - return true; + return new_found; } -static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config forced_params; - int rate_index; - int new_rate; - - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - if (forced_params.rate) - return -1; - - rate_index = intel_dp_link_caps_common_rate_idx(link_caps, - current_rate); - - if (rate_index <= 0) - return -1; - - new_rate = intel_dp_common_rate(link_caps, rate_index - 1); - - /* TODO: Make switching from UHBR to non-UHBR rates work. */ - if (drm_dp_is_uhbr_rate(current_rate) != drm_dp_is_uhbr_rate(new_rate)) - return -1; - - return new_rate; -} - -static int reduce_lane_count(struct intel_dp *intel_dp, int current_lane_count) -{ - struct intel_dp_link_config forced_params; - - intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - if (forced_params.lane_count) - return -1; - - if (current_lane_count == 1) - return -1; - - return current_lane_count >> 1; -} - -static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - int *new_link_rate, int *new_lane_count) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int link_rate; - int lane_count; - - lane_count = crtc_state->lane_count; - link_rate = reduce_link_rate(intel_dp, crtc_state->port_clock); - if (link_rate < 0) { - lane_count = reduce_lane_count(intel_dp, crtc_state->lane_count); - link_rate = intel_dp_max_common_rate(link_caps); - } - - if (lane_count < 0) - return false; - - *new_link_rate = link_rate; - *new_lane_count = lane_count; - - return true; -} - -static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, - int *new_link_rate, int *new_lane_count) -{ - /* TODO: Use the same fallback logic on SST as on MST. */ - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) - return reduce_link_params_in_bw_order(intel_dp, crtc_state, - new_link_rate, new_lane_count); - else - return reduce_link_params_in_rate_lane_order(intel_dp, crtc_state, - new_link_rate, new_lane_count); -} - -static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) +VISIBLE_IF_KUNIT +int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) { + struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_dp_link_config max_link_limits; + struct intel_dp_link_config current_config = { + .rate = crtc_state->port_clock, + .lane_count = crtc_state->lane_count, + }; int new_link_rate; int new_lane_count; + int err = -1; if (intel_dp_is_edp(intel_dp) && !intel_dp->use_max_params) { lt_dbg(intel_dp, DP_PHY_DPRX, @@ -1972,16 +1912,49 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, return 0; } + /* + * Temporarily reset the max link limit before selecting the fallback + * config. + * + * After fallback, the current logic narrows the allowed configurations + * to the selected config's rate and lane count. That can make a later + * fallback candidate fall outside the current max_limit, so reset it + * before searching. + * + * TODO: Constrain the allowed configurations by only disabling individual + * configurations and remove setting maximum link parameters. + */ + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); + intel_dp_link_caps_reset_max_limits(link_caps); + + /* + * TODO: Make fallback depend only on disabling the current config, + * once max_limit no longer constrains the allowed config set. Then + * disabling the current config will define the allowed configs for + * the subsequent modeset, so there will be no need to select a + * reduced config separately here. + */ if (!reduce_link_params(intel_dp, crtc_state, &new_link_rate, &new_lane_count)) - return -1; + goto out_restore_max_limits; if (intel_dp_is_edp(intel_dp) && !intel_dp_can_link_train_fallback_for_edp(intel_dp, new_link_rate, new_lane_count)) { lt_dbg(intel_dp, DP_PHY_DPRX, "Retrying Link training for eDP with same parameters\n"); - return 0; + + err = 0; + + goto out_restore_max_limits; } + /* + * Shouldn't fail: the current config was enabled, and reducing the + * link parameters should still leave the fallback config allowed. + */ + if (drm_WARN_ON(display->drm, + !intel_dp_link_caps_disable_config(link_caps, ¤t_config))) + return -1; + lt_dbg(intel_dp, DP_PHY_DPRX, "Reducing link parameters from %dx%d to %dx%d\n", crtc_state->lane_count, crtc_state->port_clock, @@ -1990,10 +1963,19 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, max_link_limits.rate = new_link_rate; max_link_limits.lane_count = new_lane_count; - /* TODO: handle an update failure */ - intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits); + err = 0; - return 0; +out_restore_max_limits: + /* + * Shouldn't fail: setting max_limits can only fail if they drop below + * the optionally forced rate/lane-count parameters, but the reduced + * config was chosen to satisfy those constraints. + */ + if (drm_WARN_ON(display->drm, + !intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits))) + err = -1; + + return err; } static bool intel_dp_schedule_fallback_link_training(struct intel_atomic_state *state, @@ -2834,3 +2816,32 @@ void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training { kfree(link_training); } + +#if IS_ENABLED(CONFIG_KUNIT) + +#define __INIT_MEMBER(__name, __fn) \ + .__name = __fn, + +#define INTEL_DP_LINK_TRAINING_TEST_OPS_INIT \ + INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__INIT_MEMBER) + +#ifdef I915 + +const struct intel_dp_link_training_test_ops i915_display_dp_link_training_test_ops = { + INTEL_DP_LINK_TRAINING_TEST_OPS_INIT +}; +EXPORT_SYMBOL(i915_display_dp_link_training_test_ops); + +#else + +const struct intel_dp_link_training_test_ops intel_display_dp_link_training_test_ops = { + INTEL_DP_LINK_TRAINING_TEST_OPS_INIT +}; +EXPORT_SYMBOL(intel_display_dp_link_training_test_ops); + +#endif /* I915 */ + +#undef INTEL_DP_LINK_TRAINING_TEST_OPS_INIT +#undef __INIT_MEMBER + +#endif /* CONFIG_KUNIT */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index ef16fcabd6da..581f2361fdfd 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -8,6 +8,8 @@ #include <drm/display/drm_dp_helper.h> +#include "intel_dp_link_caps.h" + struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; @@ -71,4 +73,33 @@ void intel_dp_link_training_reset(struct intel_dp_link_training *link_training); struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp); void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training); +#if IS_ENABLED(CONFIG_KUNIT) + +int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state); + +#define INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__X) \ + __X(get_fallback_values, intel_dp_get_link_train_fallback_values) + +#define __DECLARE_MEMBER(__name, __fn) \ + typeof(__fn) *__name; + +#define INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE \ + INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__DECLARE_MEMBER) + +struct intel_dp_link_training_test_ops { + INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE +}; + +#undef INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE +#undef __DECLARE_MEMBER + +#ifdef I915 +extern const struct intel_dp_link_training_test_ops i915_display_dp_link_training_test_ops; +#else +extern const struct intel_dp_link_training_test_ops intel_display_dp_link_training_test_ops; +#endif /* I915 */ + +#endif /* CONFIG_KUNIT */ + #endif /* __INTEL_DP_LINK_TRAINING_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index ecc90e8faee1..df6e54508e5d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -47,6 +47,7 @@ #include "intel_display_wa.h" #include "intel_dp.h" #include "intel_dp_hdcp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -444,8 +445,20 @@ static int mst_stream_compute_link_config(struct intel_dp *intel_dp, struct drm_connector_state *conn_state, const struct link_config_limits *limits) { - crtc_state->lane_count = limits->max_lane_count; - crtc_state->port_clock = limits->max_rate; + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp_link_config max_link_config; + + /* + * FIXME: Use a proper iteration over the link configurations, instead + * of using only the max BW config. For instance UHBR rate configs may + * have additional limitations over non-UHBR ones, due to the DSC DPT + * bpp maximum limit. + */ + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return -EINVAL; + + crtc_state->port_clock = max_link_config.rate; + crtc_state->lane_count = max_link_config.lane_count; /* * FIXME: allocate the BW according to link_bpp, which in the case of @@ -464,6 +477,7 @@ static int mst_stream_dsc_compute_link_config(struct intel_dp *intel_dp, { struct intel_display *display = to_intel_display(intel_dp); struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp_link_config max_link_config; crtc_state->pipe_bpp = limits->pipe.max_bpp; @@ -471,8 +485,17 @@ static int mst_stream_dsc_compute_link_config(struct intel_dp *intel_dp, "DSC Sink supported compressed min bpp " FXP_Q4_FMT " compressed max bpp " FXP_Q4_FMT "\n", FXP_Q4_ARGS(limits->link.min_bpp_x16), FXP_Q4_ARGS(limits->link.max_bpp_x16)); - crtc_state->lane_count = limits->max_lane_count; - crtc_state->port_clock = limits->max_rate; + /* + * FIXME: Use a proper iteration over the link configurations, instead + * of using only the max BW config. For instance UHBR rate configs may + * have additional limitations over non-UHBR ones, due to the DSC DPT + * bpp maximum limit. + */ + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return -EINVAL; + + crtc_state->port_clock = max_link_config.rate; + crtc_state->lane_count = max_link_config.lane_count; return intel_dp_mtp_tu_compute_config(intel_dp, crtc_state, conn_state, limits->link.min_bpp_x16, @@ -488,6 +511,20 @@ static int mode_hblank_period_ns(const struct drm_display_mode *mode) mode->crtc_clock); } +static int get_connector_max_rate(const struct intel_connector *connector, + const struct link_config_limits *limits) +{ + struct intel_dp *intel_dp = intel_attached_dp((struct intel_connector *)connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_config; + + intel_dp_link_caps_get_max_config(link_caps, + INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + limits->link_config_filter, &max_link_config); + + return max_link_config.rate; +} + static bool hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector, const struct intel_crtc_state *crtc_state, @@ -498,11 +535,13 @@ hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector, bool is_uhbr_sink = connector->mst.dp && drm_dp_128b132b_supported(connector->mst.dp->dpcd); int hblank_limit = is_uhbr_sink ? 500 : 300; + int max_rate; if (!connector->dp.dsc_hblank_expansion_quirk) return false; - if (is_uhbr_sink && !drm_dp_is_uhbr_rate(limits->max_rate)) + max_rate = get_connector_max_rate(connector, limits); + if (is_uhbr_sink && !drm_dp_is_uhbr_rate(max_rate)) return false; if (mode_hblank_period_ns(adjusted_mode) > hblank_limit) @@ -524,6 +563,7 @@ adjust_limits_for_dsc_hblank_expansion_quirk(struct intel_dp *intel_dp, struct intel_display *display = to_intel_display(connector); const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); int min_bpp_x16 = limits->link.min_bpp_x16; + int max_rate; if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state, limits)) return true; @@ -550,11 +590,10 @@ adjust_limits_for_dsc_hblank_expansion_quirk(struct intel_dp *intel_dp, return true; } - drm_WARN_ON(display->drm, limits->min_rate != limits->max_rate); - - if (limits->max_rate < 540000) + max_rate = get_connector_max_rate(connector, limits); + if (max_rate < 540000) min_bpp_x16 = fxp_q4_from_int(13); - else if (limits->max_rate < 810000) + else if (max_rate < 810000) min_bpp_x16 = fxp_q4_from_int(10); if (limits->link.min_bpp_x16 >= min_bpp_x16) @@ -1476,6 +1515,7 @@ mst_connector_mode_valid_ctx(struct drm_connector *_connector, unsigned long bw_overhead_flags = DRM_DP_BW_OVERHEAD_MST | DRM_DP_BW_OVERHEAD_SSC_REF_CLK; int min_link_bpp_x16 = fxp_q4_from_int(18); + struct intel_dp_link_config max_bw_config; static bool supports_dsc; int ret; bool dsc = false; @@ -1508,8 +1548,9 @@ mst_connector_mode_valid_ctx(struct drm_connector *_connector, min_link_bpp_x16 = intel_dp_compute_min_compressed_bpp_x16(connector, INTEL_OUTPUT_FORMAT_RGB); - max_link_clock = intel_dp_max_link_rate(intel_dp); - max_lanes = intel_dp_max_lane_count(intel_dp); + intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config); + max_link_clock = max_bw_config.rate; + max_lanes = max_bw_config.lane_count; max_rate = intel_dp_max_link_data_rate(intel_dp, max_link_clock, max_lanes); @@ -2135,14 +2176,19 @@ bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state, */ void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp) { - int link_rate = intel_dp_max_link_rate(intel_dp); - int lane_count = intel_dp_max_lane_count(intel_dp); + struct intel_dp_link_config max_bw_config; + int link_rate; + int lane_count; u8 rate_select; u8 link_bw; if (intel_dp->link.active) return; + intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config); + link_rate = max_bw_config.rate; + lane_count = max_bw_config.lane_count; + if (intel_mst_probed_link_params_valid(intel_dp, link_rate, lane_count)) return; diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.c b/drivers/gpu/drm/i915/display/intel_dp_test.c index da7632536dac..0551a1ce60d3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_test.c +++ b/drivers/gpu/drm/i915/display/intel_dp_test.c @@ -28,12 +28,92 @@ void intel_dp_test_reset(struct intel_dp *intel_dp) memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance)); } +static bool set_filter_for_lane_count(struct intel_connector *connector, + struct intel_dp_link_caps *link_caps, + int lane_count, + struct link_config_limits *limits) +{ + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE; + struct intel_dp_link_caps_iter iter; + bool found = false; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter); + for_each_dp_link_config(&iter, &link_config) { + if (link_config.lane_count != lane_count) + continue; + + intel_dp_link_caps_filter_add(link_caps, &new_filter, &link_config); + found = true; + } + intel_dp_link_caps_iter_end(&iter); + + if (!found) + return false; + + limits->link_config_filter = new_filter; + + return true; +} + +static bool set_filter_for_link_config(struct intel_connector *connector, + struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *link_params, + struct link_config_limits *limits) +{ + struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE; + + if (!intel_dp_link_caps_filter_add(link_caps, &new_filter, link_params)) + return false; + + limits->link_config_filter = new_filter; + + return true; +} + +static bool set_filter_for_link_params(struct intel_connector *connector, + int link_rate, int lane_count, + struct link_config_limits *limits) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_display *display = to_intel_display(intel_dp); + struct intel_dp_link_config requested_config; + + requested_config.rate = link_rate; + requested_config.lane_count = lane_count; + + if (set_filter_for_link_config(connector, link_caps, &requested_config, limits)) + return true; + + /* + * Preserve the legacy behavior: if the requested (rate, lane_count) + * combination is not an allowed config, fall back to all configs + * matching the requested lane count. + * + * TODO: Recheck whether this behavior is actually correct. + */ + if (set_filter_for_lane_count(connector, link_caps, lane_count, limits)) + return true; + + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] Invalid autotest link config parameters: %dx%d\n", + encoder->base.base.id, encoder->base.name, + requested_config.lane_count, + requested_config.rate); + + return false; +} + /* Adjust link config limits based on compliance test requests. */ -void intel_dp_test_compute_config(struct intel_dp *intel_dp, +bool intel_dp_test_compute_config(struct intel_connector *connector, struct intel_crtc_state *pipe_config, struct link_config_limits *limits) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_display *display = to_intel_display(intel_dp); /* For DP Compliance we override the computed bpp for the pipe */ @@ -49,23 +129,14 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, /* Use values requested by Compliance Test Request */ if (intel_dp->compliance.test_type == DP_TEST_LINK_TRAINING) { - int index; - - /* Validate the compliance test data since max values - * might have changed due to link train fallback. - */ - if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate, - intel_dp->compliance.test_lane_count)) { - index = intel_dp_link_caps_common_rate_idx(link_caps, - intel_dp->compliance.test_link_rate); - if (index >= 0) { - limits->min_rate = intel_dp->compliance.test_link_rate; - limits->max_rate = intel_dp->compliance.test_link_rate; - } - limits->min_lane_count = intel_dp->compliance.test_lane_count; - limits->max_lane_count = intel_dp->compliance.test_lane_count; - } + if (!set_filter_for_link_params(connector, + intel_dp->compliance.test_link_rate, + intel_dp->compliance.test_lane_count, + limits)) + return false; } + + return true; } /* Compliance test status bits */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.h b/drivers/gpu/drm/i915/display/intel_dp_test.h index dcc167e4c7f6..a08f37a63dc9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_test.h +++ b/drivers/gpu/drm/i915/display/intel_dp_test.h @@ -6,6 +6,7 @@ #include <linux/types.h> +struct intel_connector; struct intel_crtc_state; struct intel_display; struct intel_dp; @@ -13,7 +14,7 @@ struct link_config_limits; void intel_dp_test_reset(struct intel_dp *intel_dp); void intel_dp_test_request(struct intel_dp *intel_dp); -void intel_dp_test_compute_config(struct intel_dp *intel_dp, +bool intel_dp_test_compute_config(struct intel_connector *connector, struct intel_crtc_state *pipe_config, struct link_config_limits *limits); bool intel_dp_test_phy(struct intel_dp *intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index 76e9753766b9..49fa4c9699b6 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -58,10 +58,12 @@ static int kbytes_to_mbits(int kbytes) static int get_current_link_bw(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int rate = intel_dp_max_common_rate(link_caps); - int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps); + struct intel_dp_link_config max_bw_config; - return intel_dp_max_link_data_rate(intel_dp, rate, lane_count); + intel_dp_link_caps_get_max_bw_config(link_caps, &max_bw_config); + + return intel_dp_max_link_data_rate(intel_dp, max_bw_config.rate, + max_bw_config.lane_count); } static int __update_tunnel_state(struct intel_dp *intel_dp, bool force_sink_update) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index bffbdee76ee1..51e4f3309b8b 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -5,6 +5,7 @@ */ #include <drm/drm_print.h> +#include <drm/intel/step.h> #include "intel_alpm.h" #include "intel_cmtg.h" @@ -1106,6 +1107,11 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display); } + if (display->platform.novalake && + IS_DISPLAY_STEP(display, STEP_A0, STEP_C0)) + crtc_state->hw.adjusted_mode.crtc_vtotal = + intel_vrr_vmin_vtotal(crtc_state); + if (HAS_AS_SDP(display)) { trans_vrr_vsync = intel_de_read(display, diff --git a/drivers/gpu/drm/i915/display/tests/Makefile b/drivers/gpu/drm/i915/display/tests/Makefile new file mode 100644 index 000000000000..ad250974160f --- /dev/null +++ b/drivers/gpu/drm/i915/display/tests/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +subdir-ccflags-y += -I$(srctree)/drivers/gpu/drm/i915/display/ + +obj-$(CONFIG_DRM_I915_KUNIT_TEST) += i915_display_test.o +i915_display_test-y = \ + intel_dp_link_test.o diff --git a/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c b/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c new file mode 100644 index 000000000000..e1c356ba11b5 --- /dev/null +++ b/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c @@ -0,0 +1,1440 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include <kunit/test.h> + +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/log2.h> +#include <linux/prandom.h> +#include <linux/random.h> + +#include <drm/display/drm_dp_helper.h> + +#include <drm/intel/display_member.h> + +#include "intel_connector.h" +#include "intel_display_core.h" +#include "intel_display_types.h" +#include "intel_dp_link_caps.h" +#include "intel_dp_link_training.h" + +#define LINK_TEST_NUM_LANE_CONFIGS(__max_lane_count) \ + (ilog2(__max_lane_count) + 1) + +#define LINK_TEST_NUM_CONFIGS(__num_rates, __max_lane_count) \ + ((__num_rates) * LINK_TEST_NUM_LANE_CONFIGS(__max_lane_count)) + +#define LINK_TEST_MAX_LANE_COUNT ((u32)4) +#define LINK_TEST_MAX_CONFIGS LINK_TEST_NUM_CONFIGS(DP_MAX_SUPPORTED_RATES, \ + LINK_TEST_MAX_LANE_COUNT) + +#define LINK_TEST_NUM_RANDOM_ITERATIONS 50 + +struct test_ctx { + struct { + struct intel_display display; + struct device device; + struct __intel_generic_device generic_device; + + struct intel_connector connector; + struct intel_digital_port dig_port; + + struct intel_crtc_state crtc_state; + } dev; + + const struct intel_dp_link_caps_test_ops *link_caps_ops; + const struct intel_dp_link_training_test_ops *link_training_ops; + + struct rnd_state rnd; +}; + +struct link_rate_set { + const int *entries; + int size; +}; + +struct link_config_set { + struct intel_dp_link_config entries[LINK_TEST_MAX_CONFIGS]; + int size; +}; + +struct test_config_table { + struct kunit *test; + + struct link_rate_set rates; + int max_lane_count; + struct link_config_set disabled_configs; +}; + +static const int standard_dp_link_rates[] = { + 162000, 270000, 540000, 810000, 1000000, 1350000, 2000000 +}; + +#define LINK_TEST_NUM_STANDARD_RATES (ARRAY_SIZE(standard_dp_link_rates)) + +#define INIT_STANDARD_TABLE(__test, __num_rates, __max_lane_count) { \ + .test = (__test), \ + .rates = { \ + .entries = standard_dp_link_rates, \ + .size = (__num_rates), \ + }, \ + .max_lane_count = (__max_lane_count), \ +} + +static const struct link_config_set standard_dp_link_configs[] = { + [INTEL_DP_LINK_CAPS_ORDER_KEY_BW] = { /* MBps PBN */ + .entries = { + { .rate = 162000, .lane_count = 1 }, /* 162.0 3.00 */ + { .rate = 270000, .lane_count = 1 }, /* 270.0 5.00 */ + { .rate = 162000, .lane_count = 2 }, /* 324.0 6.00 */ + { .rate = 270000, .lane_count = 2 }, /* 540.0 10.00 */ + { .rate = 540000, .lane_count = 1 }, /* 540.0 10.00 */ + { .rate = 162000, .lane_count = 4 }, /* 648.0 12.00 */ + { .rate = 810000, .lane_count = 1 }, /* 810.0 15.00 */ + { .rate = 270000, .lane_count = 4 }, /* 1080.0 20.00 */ + { .rate = 540000, .lane_count = 2 }, /* 1080.0 20.00 */ + { .rate = 1000000, .lane_count = 1 }, /* 1208.9 22.39 */ + { .rate = 810000, .lane_count = 2 }, /* 1620.0 30.00 */ + { .rate = 1350000, .lane_count = 1 }, /* 1632.0 30.22 */ + { .rate = 540000, .lane_count = 4 }, /* 2160.0 40.00 */ + { .rate = 1000000, .lane_count = 2 }, /* 2417.8 44.77 */ + { .rate = 2000000, .lane_count = 1 }, /* 2417.8 44.77 */ + { .rate = 810000, .lane_count = 4 }, /* 3240.0 60.00 */ + { .rate = 1350000, .lane_count = 2 }, /* 3264.0 60.44 */ + { .rate = 1000000, .lane_count = 4 }, /* 4835.6 89.55 */ + { .rate = 2000000, .lane_count = 2 }, /* 4835.6 89.55 */ + { .rate = 1350000, .lane_count = 4 }, /* 6527.9 120.89 */ + { .rate = 2000000, .lane_count = 4 }, /* 9671.1 179.09 */ + }, + .size = LINK_TEST_NUM_CONFIGS(ARRAY_SIZE(standard_dp_link_rates), + LINK_TEST_MAX_LANE_COUNT), + }, + [INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE] = { + .entries = { + { .rate = 162000, .lane_count = 1 }, + { .rate = 162000, .lane_count = 2 }, + { .rate = 162000, .lane_count = 4 }, + + { .rate = 270000, .lane_count = 1 }, + { .rate = 270000, .lane_count = 2 }, + { .rate = 270000, .lane_count = 4 }, + + { .rate = 540000, .lane_count = 1 }, + { .rate = 540000, .lane_count = 2 }, + { .rate = 540000, .lane_count = 4 }, + + { .rate = 810000, .lane_count = 1 }, + { .rate = 810000, .lane_count = 2 }, + { .rate = 810000, .lane_count = 4 }, + + { .rate = 1000000, .lane_count = 1 }, + { .rate = 1000000, .lane_count = 2 }, + { .rate = 1000000, .lane_count = 4 }, + + { .rate = 1350000, .lane_count = 1 }, + { .rate = 1350000, .lane_count = 2 }, + { .rate = 1350000, .lane_count = 4 }, + + { .rate = 2000000, .lane_count = 1 }, + { .rate = 2000000, .lane_count = 2 }, + { .rate = 2000000, .lane_count = 4 }, + }, + .size = LINK_TEST_NUM_CONFIGS(ARRAY_SIZE(standard_dp_link_rates), + LINK_TEST_MAX_LANE_COUNT), + }, + [INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE] = { + .entries = { + { .rate = 162000, .lane_count = 1 }, + { .rate = 270000, .lane_count = 1 }, + { .rate = 540000, .lane_count = 1 }, + { .rate = 810000, .lane_count = 1 }, + { .rate = 1000000, .lane_count = 1 }, + { .rate = 1350000, .lane_count = 1 }, + { .rate = 2000000, .lane_count = 1 }, + + { .rate = 162000, .lane_count = 2 }, + { .rate = 270000, .lane_count = 2 }, + { .rate = 540000, .lane_count = 2 }, + { .rate = 810000, .lane_count = 2 }, + { .rate = 1000000, .lane_count = 2 }, + { .rate = 1350000, .lane_count = 2 }, + { .rate = 2000000, .lane_count = 2 }, + + { .rate = 162000, .lane_count = 4 }, + { .rate = 270000, .lane_count = 4 }, + { .rate = 540000, .lane_count = 4 }, + { .rate = 810000, .lane_count = 4 }, + { .rate = 1000000, .lane_count = 4 }, + { .rate = 1350000, .lane_count = 4 }, + { .rate = 2000000, .lane_count = 4 }, + }, + .size = LINK_TEST_NUM_CONFIGS(ARRAY_SIZE(standard_dp_link_rates), + LINK_TEST_MAX_LANE_COUNT), + }, +}; + +static int lookup_rate(const struct link_rate_set *rate_set, int rate) +{ + int i; + + for (i = 0; i < rate_set->size; i++) + if (rate_set->entries[i] == rate) + return i; + + return -1; +} + +static bool has_rate(const struct link_rate_set *rate_set, int rate) +{ + return lookup_rate(rate_set, rate) >= 0; +} + +static bool link_configs_match(const struct intel_dp_link_config *a, + const struct intel_dp_link_config *b) +{ + return a->rate == b->rate && a->lane_count == b->lane_count; +} + +static int lookup_config(const struct link_config_set *config_set, + const struct intel_dp_link_config *config) +{ + int i; + + for (i = 0; i < config_set->size; i++) + if (link_configs_match(&config_set->entries[i], config)) + return i; + + return -1; +} + +static bool has_config(const struct link_config_set *config_set, + const struct intel_dp_link_config *config) +{ + return lookup_config(config_set, config) >= 0; +} + +static void add_config(struct kunit *test, + struct link_config_set *config_set, + const struct intel_dp_link_config *config) +{ + KUNIT_ASSERT_LT(test, config_set->size, ARRAY_SIZE(config_set->entries)); + + config_set->entries[config_set->size] = *config; + config_set->size++; +} + +static const struct intel_dp_link_caps_order config_orders[] = { + { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + } +}; + +static const struct link_config_set * +link_caps_config_order_key_to_set(struct kunit *test, enum intel_dp_link_caps_order_key key) +{ + return &standard_dp_link_configs[key]; +} + +/* + * TEST: Baseline with fixed reference table + * ----------------------------------------- + * Verify the link_caps config iterator using fixed standard DP config tables. + */ +static void baseline_test_for_order(struct kunit *test, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order config_order) +{ + struct test_ctx *ctx = test->priv; + const struct link_config_set *config_set = + link_caps_config_order_key_to_set(test, config_order.key); + const struct intel_dp_link_caps_test_ops *ops = ctx->link_caps_ops; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int pos = 0; + + ops->iter_start(&iter, link_caps, config_order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) { + int idx = pos; + + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_DESC) + idx = config_set->size - idx - 1; + + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, + &config_set->entries[idx])); + + pos++; + } + ops->iter_end(&iter); +} + +static void intel_dp_link_caps_test_baseline(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + int i; + + ops->update(link_caps, + standard_dp_link_rates, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT, + true); + + for (i = 0; i < ARRAY_SIZE(config_orders); i++) + baseline_test_for_order(test, link_caps, config_orders[i]); +} + +static int get_num_configs(int num_rates, int max_lane_count) +{ + return num_rates * LINK_TEST_NUM_LANE_CONFIGS(max_lane_count); +} + +static int rand_in_range(struct test_ctx *ctx, int min, int max) +{ + return min + (prandom_u32_state(&ctx->rnd) % (max - min + 1)); +} + +/* + * TEST: Update reset + * ------------------ + * Verify that resetting link_caps with the DP standard rates/lane + * counts updates the configuration table accordingly for all + * combinations. + */ +static void verify_bw_asc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + int config_bw = drm_dp_max_dprx_data_rate(config->rate, + config->lane_count); + int last_config_bw = drm_dp_max_dprx_data_rate(last_config->rate, + last_config->lane_count); + + KUNIT_EXPECT_GE(test, config_bw, last_config_bw); + if (config_bw == last_config_bw) + KUNIT_EXPECT_GT(test, config->rate, last_config->rate); +} + +static void verify_bw_desc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + int config_bw = drm_dp_max_dprx_data_rate(config->rate, + config->lane_count); + int last_config_bw = drm_dp_max_dprx_data_rate(last_config->rate, + last_config->lane_count); + + KUNIT_EXPECT_LE(test, config_bw, last_config_bw); + if (config_bw == last_config_bw) + KUNIT_EXPECT_LT(test, config->rate, last_config->rate); +} + +static void verify_rate_lane_asc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_GE(test, config->rate, last_config->rate); + if (config->rate == last_config->rate) + KUNIT_EXPECT_GT(test, config->lane_count, last_config->lane_count); +} + +static void verify_rate_lane_desc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_LE(test, config->rate, last_config->rate); + if (config->rate == last_config->rate) + KUNIT_EXPECT_LT(test, config->lane_count, last_config->lane_count); +} + +static void verify_lane_rate_asc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_GE(test, config->lane_count, last_config->lane_count); + if (config->lane_count == last_config->lane_count) + KUNIT_EXPECT_GT(test, config->rate, last_config->rate); +} + +static void verify_lane_rate_desc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_LE(test, config->lane_count, last_config->lane_count); + if (config->lane_count == last_config->lane_count) + KUNIT_EXPECT_LT(test, config->rate, last_config->rate); +} + +static void verify_config_order(struct kunit *test, + struct intel_dp_link_caps_order config_order, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + switch (config_order.key) { + case INTEL_DP_LINK_CAPS_ORDER_KEY_BW: + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_ASC) + verify_bw_asc_config_order(test, last_config, config); + else + verify_bw_desc_config_order(test, last_config, config); + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_ASC) + verify_rate_lane_asc_config_order(test, last_config, config); + else + verify_rate_lane_desc_config_order(test, last_config, config); + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_ASC) + verify_lane_rate_asc_config_order(test, last_config, config); + else + verify_lane_rate_desc_config_order(test, last_config, config); + break; + default: + KUNIT_FAIL_AND_ABORT(test, "Missing order key: %d", config_order.key); + } +} + +static int expected_num_configs(const struct test_config_table *expected_table, + const struct intel_dp_link_config *max_limits) +{ + int num_configs = 0; + int lane_count; + int rate_idx; + + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (config.rate > max_limits->rate || + config.lane_count > max_limits->lane_count) + continue; + + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + num_configs++; + } + } + + return num_configs; +} + +static void +verify_link_caps_for_order(const struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order config_order, + const struct intel_dp_link_config *max_limits) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + struct intel_dp_link_config expected_max_bw_config = {}; + struct intel_dp_link_config actual_max_bw_config; + struct intel_dp_link_config last_config = {}; + struct intel_dp_link_config old_max_limits; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int num_actual_configs = 0; + int max_bw = 0; + + ops->get_max_limits(link_caps, &old_max_limits); + ops->set_max_limits(link_caps, max_limits); + + ops->iter_start(&iter, link_caps, config_order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) { + int bw; + + KUNIT_EXPECT_LE(test, iter_config.rate, max_limits->rate); + KUNIT_EXPECT_LE(test, iter_config.lane_count, max_limits->lane_count); + + num_actual_configs++; + + /* + * Verify the config's rate/lane-count values and its ordering relative + * to the previous config. + */ + if (last_config.rate) + verify_config_order(test, config_order, &last_config, &iter_config); + last_config = iter_config; + + KUNIT_EXPECT_TRUE(test, has_rate(&expected_table->rates, + iter_config.rate)); + KUNIT_EXPECT_LE(test, iter_config.lane_count, + expected_table->max_lane_count); + KUNIT_EXPECT_TRUE(test, is_power_of_2(iter_config.lane_count)); + + /* Verify the config's disabled state */ + KUNIT_EXPECT_FALSE(test, has_config(&expected_table->disabled_configs, + &iter_config)); + + /* + * Update the max limits for allowed configs, verified at the + * end for the whole config table. + */ + + bw = drm_dp_max_dprx_data_rate(iter_config.rate, iter_config.lane_count); + if (bw > max_bw || + (bw == max_bw && iter_config.rate > expected_max_bw_config.rate)) { + max_bw = bw; + expected_max_bw_config = iter_config; + } + } + ops->iter_end(&iter); + + KUNIT_EXPECT_EQ(test, num_actual_configs, expected_num_configs(expected_table, max_limits)); + + ops->get_max_bw_config(link_caps, &actual_max_bw_config); + KUNIT_EXPECT_TRUE(test, link_configs_match(&expected_max_bw_config, + &actual_max_bw_config)); + + KUNIT_ASSERT_TRUE(test, ops->set_max_limits(link_caps, &old_max_limits)); +} + +static bool max_limits_valid(const struct test_config_table *expected_table, + const struct intel_dp_link_config *max_limits) +{ + int lane_count; + int rate_idx; + + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + if (config.rate <= max_limits->rate && + config.lane_count <= max_limits->lane_count) + return true; + } + } + + return false; +} + +static void get_max_limits(const struct test_config_table *expected_table, + struct intel_dp_link_config *max_limits) +{ + int lane_count; + int rate_idx; + + max_limits->rate = 0; + max_limits->lane_count = 0; + + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + max_limits->rate = max(max_limits->rate, config.rate); + max_limits->lane_count = max(max_limits->lane_count, config.lane_count); + } + } +} + +static void verify_link_caps(const struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = ctx->link_caps_ops; + struct intel_dp_link_config max_limits; + int i; + + get_max_limits(expected_table, &max_limits); + + for (i = 0; i < ARRAY_SIZE(config_orders); i++) { + int lane_count; + int rate_idx; + + verify_link_caps_for_order(expected_table, link_caps, config_orders[i], &max_limits); + /* + * Verify iteration after setting the max limits to each + * configurations. + */ + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (!max_limits_valid(expected_table, &config)) { + /* Verify that invalid max limits are rejected. */ + KUNIT_EXPECT_FALSE(test, ops->set_max_limits(link_caps, &config)); + + continue; + } + + verify_link_caps_for_order(expected_table, link_caps, config_orders[i], + &config); + } + } + } +} + +static void update_link_caps_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps, + bool reset) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + bool link_params_changed; + + link_params_changed = ops->update(link_caps, + expected_table->rates.entries, + expected_table->rates.size, + expected_table->max_lane_count, + reset); + KUNIT_EXPECT_TRUE(test, !reset || link_params_changed); + + /* + * ops->update() re-enables all configurations when called with + * reset=true, or changed link parameters. + */ + if (link_params_changed) + expected_table->disabled_configs.size = 0; + + verify_link_caps(expected_table, link_caps); +} + +static void intel_dp_link_caps_test_update_reset(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + int max_lane_count; + int num_rates; + + for (max_lane_count = 1; + max_lane_count <= LINK_TEST_MAX_LANE_COUNT; + max_lane_count <<= 1) { + for (num_rates = 1; + num_rates <= LINK_TEST_NUM_STANDARD_RATES; + num_rates++) { + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, num_rates, + max_lane_count); + + update_link_caps_and_verify(&expected_table, link_caps, true); + } + } +} + +/* + * TEST: Update shrink and expand + * ------------------------------ + * Verify that removing or adding supported rates/lane counts updates + * the configuration table accordingly. + */ +static void disable_configs_and_verify(struct kunit *test, + struct intel_dp_link_caps *link_caps, + struct test_config_table *expected_table, + const struct link_config_set *config_set) +{ + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + int i; + + for (i = 0; i < config_set->size; i++) { + KUNIT_ASSERT_FALSE(test, has_config(&expected_table->disabled_configs, + &config_set->entries[i])); + add_config(test, &expected_table->disabled_configs, &config_set->entries[i]); + + KUNIT_ASSERT_TRUE(test, ops->disable_config(link_caps, &config_set->entries[i])); + + verify_link_caps(expected_table, link_caps); + } +} + +static void disable_configs_for_shrink_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct link_config_set config_set = {}; + struct intel_dp_link_config max_config; + + /* + * When configs shrink disable the config with the + * second-highest rate, lane params, so the disabled config + * stays around after the configs got shrunk. + */ + KUNIT_ASSERT_GE(test, expected_table->rates.size, 2); + KUNIT_ASSERT_GE(test, expected_table->max_lane_count, 2); + + max_config.rate = expected_table->rates.entries[expected_table->rates.size - 2]; + max_config.lane_count = expected_table->max_lane_count >> 1; + + add_config(test, &config_set, &max_config); + disable_configs_and_verify(test, link_caps, expected_table, + &config_set); +} + +static void disable_configs_for_expand_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct link_config_set config_set = {}; + struct intel_dp_link_config max_config; + + KUNIT_ASSERT_GE(test, expected_table->rates.size, 1); + + max_config.rate = expected_table->rates.entries[expected_table->rates.size - 1]; + max_config.lane_count = expected_table->max_lane_count; + + add_config(test, &config_set, &max_config); + disable_configs_and_verify(test, link_caps, expected_table, + &config_set); +} + +static void get_nth_rate_lane_config(const struct test_config_table *expected_table, int n, + struct intel_dp_link_config *config) +{ + int num_lane_configs = LINK_TEST_NUM_LANE_CONFIGS(expected_table->max_lane_count); + int rate_idx = n / num_lane_configs; + int lane_count_exp = n % num_lane_configs; + + config->rate = expected_table->rates.entries[rate_idx]; + config->lane_count = 1 << lane_count_exp; +} + +static void test_update_rates_shrink(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size > 1) { + if (disable_configs) + disable_configs_for_shrink_and_verify(&expected_table, link_caps); + + expected_table.rates.size--; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_rates_shrink(struct kunit *test) +{ + test_update_rates_shrink(test, false); +} + +static void intel_dp_link_caps_test_update_rates_shrink_disable(struct kunit *test) +{ + test_update_rates_shrink(test, true); +} + +static void test_update_rates_expand(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, 1, LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size < LINK_TEST_NUM_STANDARD_RATES) { + if (disable_configs) + disable_configs_for_expand_and_verify(&expected_table, link_caps); + + expected_table.rates.size++; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_rates_expand(struct kunit *test) +{ + test_update_rates_expand(test, false); +} + +static void intel_dp_link_caps_test_update_rates_expand_disable(struct kunit *test) +{ + test_update_rates_expand(test, true); +} + +static void test_update_lanes_shrink(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.max_lane_count > 1) { + if (disable_configs) + disable_configs_for_shrink_and_verify(&expected_table, link_caps); + + expected_table.max_lane_count >>= 1; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_lanes_shrink(struct kunit *test) +{ + test_update_lanes_shrink(test, false); +} + +static void intel_dp_link_caps_test_update_lanes_shrink_disable(struct kunit *test) +{ + test_update_lanes_shrink(test, true); +} + +static void test_update_lanes_expand(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, 1); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.max_lane_count < LINK_TEST_MAX_LANE_COUNT) { + if (disable_configs) + disable_configs_for_expand_and_verify(&expected_table, link_caps); + + expected_table.max_lane_count <<= 1; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_lanes_expand(struct kunit *test) +{ + test_update_lanes_expand(test, false); +} + +static void intel_dp_link_caps_test_update_lanes_expand_disable(struct kunit *test) +{ + test_update_lanes_expand(test, true); +} + +static void disable_random_configs_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + struct link_config_set config_set = {}; + u32 disabled_config_mask; + int num_configs; + int i; + + num_configs = get_num_configs(expected_table->rates.size, + expected_table->max_lane_count); + disabled_config_mask = prandom_u32_state(&ctx->rnd) & + GENMASK_U32(num_configs - 1, 0); + + for (i = 0; i < num_configs; i++) { + struct intel_dp_link_config config; + + /* At least one config must remain enabled. */ + if (expected_table->disabled_configs.size + + config_set.size + 1 >= num_configs) + break; + + if (!(BIT(i) & disabled_config_mask)) + continue; + + get_nth_rate_lane_config(expected_table, i, &config); + /* Don't disable a config twice. */ + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + add_config(test, &config_set, &config); + } + + disable_configs_and_verify(test, link_caps, expected_table, + &config_set); +} + +static void get_params_shrink_step(struct test_ctx *ctx, + int num_rates, int max_lane_count, + int *rates_step, int *lanes_step) +{ + int shrink_mask; + + *rates_step = 0; + *lanes_step = 0; + + if (num_rates == 1) + shrink_mask = BIT(0); /* shrink only lanes */ + else if (max_lane_count == 1) + shrink_mask = BIT(1); /* shrink only rates */ + else + shrink_mask = rand_in_range(ctx, + BIT(0), + BIT(0) | BIT(1)); /* shrink one or both params */ + + if (shrink_mask & BIT(1)) + *rates_step = rand_in_range(ctx, 1, num_rates - 1); + + if (shrink_mask & BIT(0)) + *lanes_step = rand_in_range(ctx, 1, ilog2(max_lane_count)); +} + +static void get_params_expand_step(struct test_ctx *ctx, + int max_num_rates, int num_rates, + int max_supported_lane_count, int max_lane_count, + int *rates_step, int *lanes_step) +{ + int expand_mask; + + *rates_step = 0; + *lanes_step = 0; + + if (num_rates == max_num_rates) + expand_mask = BIT(0); /* expand only lanes */ + else if (max_lane_count == max_supported_lane_count) + expand_mask = BIT(1); /* expand only rates */ + else + expand_mask = rand_in_range(ctx, + BIT(0), + BIT(0) | BIT(1)); /* expand one or both params */ + + if (expand_mask & BIT(1)) + *rates_step = rand_in_range(ctx, 1, max_num_rates - num_rates); + + if (expand_mask & BIT(0)) + *lanes_step = rand_in_range(ctx, 1, ilog2(max_supported_lane_count / + max_lane_count)); +} + +static void test_update_params_shrink_random(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size > 1 || expected_table.max_lane_count > 1) { + int rates_step; + int lanes_step; + + if (disable_configs) + disable_random_configs_and_verify(&expected_table, link_caps); + + get_params_shrink_step(ctx, + expected_table.rates.size, + expected_table.max_lane_count, + &rates_step, &lanes_step); + + expected_table.rates.size -= rates_step; + expected_table.max_lane_count >>= lanes_step; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_params_shrink_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_shrink_random(test, false); +} + +static void intel_dp_link_caps_test_update_params_shrink_disable_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_shrink_random(test, true); +} + +static void test_update_params_expand_random(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, 1, 1); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size < LINK_TEST_NUM_STANDARD_RATES || + expected_table.max_lane_count < LINK_TEST_MAX_LANE_COUNT) { + int rates_step; + int lanes_step; + + if (disable_configs) + disable_random_configs_and_verify(&expected_table, link_caps); + + get_params_expand_step(ctx, + LINK_TEST_NUM_STANDARD_RATES, + expected_table.rates.size, + LINK_TEST_MAX_LANE_COUNT, + expected_table.max_lane_count, + &rates_step, &lanes_step); + + expected_table.rates.size += rates_step; + expected_table.max_lane_count <<= lanes_step; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_params_expand_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_expand_random(test, false); +} + +static void intel_dp_link_caps_test_update_params_expand_disable_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_expand_random(test, true); +} + +/* + * TEST: Fallback sequence + * ----------------------- + * Verify the eDP fallback logic to set the maximum supported configuration + * as a preference. + * + * For DP SST and MST verify fallback selection from the connector's + * maximum configuration and iteration of the resulting allowed + * configurations. + */ +static void intel_dp_link_test_fallback_for_edp(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + struct intel_digital_port *dig_port = &ctx->dev.dig_port; + const struct intel_dp_link_training_test_ops *lt_ops = + ctx->link_training_ops; + const struct intel_dp_link_caps_test_ops *lc_ops = + ctx->link_caps_ops; + struct intel_dp_link_config min_config = { + .rate = expected_table.rates.entries[0], + .lane_count = 1, + }; + struct intel_dp_link_config max_config = { + .rate = expected_table.rates.entries[expected_table.rates.size - 1], + .lane_count = LINK_TEST_MAX_LANE_COUNT, + }; + struct intel_dp_link_caps_order order; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int fallback_err; + + dig_port->base.type = INTEL_OUTPUT_EDP; + ctx->dev.dig_port.dp.use_max_params = false; + + update_link_caps_and_verify(&expected_table, link_caps, true); + + order = lc_ops->connector_compute_order(&ctx->dev.connector); + + lc_ops->iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) + break; + lc_ops->iter_end(&iter); + + KUNIT_EXPECT_FALSE(test, ctx->dev.dig_port.dp.use_max_params); + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, &min_config)); + + ctx->dev.crtc_state.output_types = BIT(dig_port->base.type); + ctx->dev.crtc_state.port_clock = min_config.rate; + ctx->dev.crtc_state.lane_count = min_config.lane_count; + + fallback_err = lt_ops->get_fallback_values(&ctx->dev.dig_port.dp, &ctx->dev.crtc_state); + KUNIT_EXPECT_EQ(test, fallback_err, 0); + + /* The fallback should've changed the order. */ + order = lc_ops->connector_compute_order(&ctx->dev.connector); + + lc_ops->iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) + break; + lc_ops->iter_end(&iter); + + KUNIT_EXPECT_TRUE(test, ctx->dev.dig_port.dp.use_max_params); + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, &max_config)); +} + +static bool test_fallback_from_target(struct test_config_table *expected_table, + enum intel_output_type output_type, int max_rate, + const struct intel_dp_link_config *expected_target_config, + const struct intel_dp_link_config *expected_fallback_config) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct intel_dp_link_config iter_config; + const struct intel_dp_link_training_test_ops *lt_ops = + ctx->link_training_ops; + const struct intel_dp_link_caps_test_ops *lc_ops = + ctx->link_caps_ops; + /* Modify default order direction for max config lookup. */ + struct intel_dp_link_caps_order fallback_order = + lc_ops->connector_fallback_order(ctx->dev.connector.mst.dp); + struct intel_dp_link_caps_iter iter; + int expected_fallback_err = 0; + int fallback_err; + + /* Get the max connector config, optionally filtered to the max_rate limit. */ + lc_ops->iter_start(&iter, link_caps, fallback_order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) + if (max_rate == 0 || iter_config.rate <= max_rate) + break; + lc_ops->iter_end(&iter); + + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, + expected_target_config)); + KUNIT_EXPECT_FALSE(test, link_configs_match(&iter_config, + &INTEL_DP_LINK_CONFIG_NULL)); + + ctx->dev.crtc_state.output_types = BIT(output_type); + ctx->dev.crtc_state.port_clock = expected_target_config->rate; + ctx->dev.crtc_state.lane_count = expected_target_config->lane_count; + + if (link_configs_match(expected_fallback_config, &INTEL_DP_LINK_CONFIG_NULL)) + expected_fallback_err = -1; + + fallback_err = lt_ops->get_fallback_values(&ctx->dev.dig_port.dp, &ctx->dev.crtc_state); + KUNIT_EXPECT_EQ(test, fallback_err, expected_fallback_err); + + if (!fallback_err) { + /* + * NOTE: This test does not verify any implied fallback + * target selection. + * + * The current driver behavior may still select a fallback + * configuration indirectly via max_limits, but that is an + * implementation artifact rather than part of the intended + * fallback API behavior, and is therefore not verified here. + * + * Instead, the effect of the fallback logic is verified by + * checking that the failed target configuration is disabled. + * Selecting the next target configuration from the remaining + * allowed configurations belongs to the modeset link target + * selection logic. + */ + add_config(test, &expected_table->disabled_configs, + expected_target_config); + } + + verify_link_caps(expected_table, link_caps); + + return !fallback_err; +} + +static const struct link_config_set * +get_target_configs_for_output_type(struct kunit *test, + enum intel_output_type output_type) +{ + switch (output_type) { + case INTEL_OUTPUT_DDI: + case INTEL_OUTPUT_DP: + case INTEL_OUTPUT_EDP: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE]; + case INTEL_OUTPUT_DP_MST: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_BW]; + default: + KUNIT_FAIL_AND_ABORT(test, "Missing output type: %d", output_type); + } +} + +static const struct link_config_set * +get_fallback_configs_for_output_type(struct kunit *test, + enum intel_output_type output_type) +{ + switch (output_type) { + case INTEL_OUTPUT_DDI: + case INTEL_OUTPUT_DP: + case INTEL_OUTPUT_EDP: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE]; + case INTEL_OUTPUT_DP_MST: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_BW]; + default: + KUNIT_FAIL_AND_ABORT(test, "Missing output type: %d", output_type); + } +} + +static bool output_type_allows_uhbr_fallback(enum intel_output_type output_type) +{ + return output_type == INTEL_OUTPUT_DP_MST; +} + +static void assert_config_is_supported(const struct test_config_table *expected_table, + const struct intel_dp_link_config *config) +{ + struct kunit *test = expected_table->test; + + KUNIT_ASSERT_TRUE(test, has_rate(&expected_table->rates, config->rate)); + KUNIT_ASSERT_LE(test, config->lane_count, expected_table->max_lane_count); +} + +static bool get_fallback_config(const struct test_config_table *expected_table, + enum intel_output_type output_type, + const struct intel_dp_link_config *target_config, + struct intel_dp_link_config *fallback_config) +{ + struct kunit *test = expected_table->test; + const struct link_config_set *config_set = + get_fallback_configs_for_output_type(test, output_type); + int i; + + i = lookup_config(config_set, target_config); + KUNIT_ASSERT_GE(test, i, 0); + + for (i--; i >= 0; i--) { + const struct intel_dp_link_config *config = + &config_set->entries[i]; + + if (output_type_allows_uhbr_fallback(output_type) || + (drm_dp_is_uhbr_rate(target_config->rate) == + drm_dp_is_uhbr_rate(config->rate))) { + assert_config_is_supported(expected_table, config); + *fallback_config = *config; + + return true; + } + } + + return false; +} + +static bool get_target_config(const struct test_config_table *expected_table, + enum intel_output_type output_type, + int max_rate, + struct intel_dp_link_config *target) +{ + struct kunit *test = expected_table->test; + const struct link_config_set *config_set = + get_target_configs_for_output_type(test, output_type); + int i; + + for (i = config_set->size - 1; i >= 0; i--) { + const struct intel_dp_link_config *config = + &config_set->entries[i]; + + if (config->rate <= max_rate) { + assert_config_is_supported(expected_table, config); + *target = *config; + + return true; + } + } + + return false; +} + +static void test_fallback_seq(struct kunit *test, + enum intel_output_type output_type, + bool uhbr) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + struct intel_digital_port *dig_port = &ctx->dev.dig_port; + struct intel_dp_link_config fallback_config = {}; + struct intel_dp_link_config target_config; + int fallback_count = 0; + bool target_found; + int max_rate; + + if (uhbr) + max_rate = expected_table.rates.entries[expected_table.rates.size - 1]; + else + max_rate = 810000; + + dig_port->base.type = output_type; + ctx->dev.dig_port.dp.use_max_params = false; + + update_link_caps_and_verify(&expected_table, link_caps, true); + + /* Get the initial target config. */ + target_found = get_target_config(&expected_table, output_type, + max_rate, &target_config); + KUNIT_ASSERT_TRUE(test, target_found); + + for (;;) { + /* Also test the case where no fallback is available. */ + if (!get_fallback_config(&expected_table, output_type, + &target_config, &fallback_config)) + fallback_config = INTEL_DP_LINK_CONFIG_NULL; + + if (!test_fallback_from_target(&expected_table, output_type, max_rate, + &target_config, &fallback_config)) + break; + + /* + * The fallback changed the max rate allowed for the next + * target. + */ + max_rate = fallback_config.rate; + + /* Simply select the fallback config as the next target. */ + target_config = fallback_config; + + fallback_count++; + KUNIT_ASSERT_LT(test, fallback_count, LINK_TEST_MAX_CONFIGS); + } +} + +static void intel_dp_link_test_fallback_for_sst_max_non_uhbr(struct kunit *test) +{ + test_fallback_seq(test, INTEL_OUTPUT_DP, false); +} + +static void intel_dp_link_test_fallback_for_sst_max_uhbr(struct kunit *test) +{ + test_fallback_seq(test, INTEL_OUTPUT_DP, true); +} + +static void intel_dp_link_test_fallback_for_mst(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + + ctx->dev.connector.mst.dp = &ctx->dev.dig_port.dp; + + test_fallback_seq(test, INTEL_OUTPUT_DP_MST, true); +} + +static struct kunit_case intel_dp_link_test_cases[] = { + KUNIT_CASE(intel_dp_link_caps_test_baseline), + + KUNIT_CASE(intel_dp_link_caps_test_update_reset), + + KUNIT_CASE(intel_dp_link_caps_test_update_rates_shrink), + KUNIT_CASE(intel_dp_link_caps_test_update_rates_shrink_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_rates_expand), + KUNIT_CASE(intel_dp_link_caps_test_update_rates_expand_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_shrink), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_shrink_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_expand), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_expand_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_params_shrink_random), + KUNIT_CASE(intel_dp_link_caps_test_update_params_shrink_disable_random), + KUNIT_CASE(intel_dp_link_caps_test_update_params_expand_random), + KUNIT_CASE(intel_dp_link_caps_test_update_params_expand_disable_random), + + KUNIT_CASE(intel_dp_link_test_fallback_for_edp), + KUNIT_CASE(intel_dp_link_test_fallback_for_sst_max_non_uhbr), + KUNIT_CASE(intel_dp_link_test_fallback_for_sst_max_uhbr), + KUNIT_CASE(intel_dp_link_test_fallback_for_mst), + + {} +}; + +static struct test_ctx test_ctx; + +static int intel_dp_link_test_init(struct kunit *test) +{ + struct intel_digital_port *dig_port; + struct intel_encoder *encoder; + struct intel_dp *intel_dp; + + /* Reset the dev state for each test. */ + memset(&test_ctx.dev, 0, sizeof(test_ctx.dev)); + + test_ctx.dev.generic_device.drm.dev = &test_ctx.dev.device; + + test_ctx.dev.display.drm = &test_ctx.dev.generic_device.drm; + test_ctx.dev.generic_device.display = &test_ctx.dev.display; + + encoder = &test_ctx.dev.dig_port.base; + encoder->base.dev = &test_ctx.dev.generic_device.drm; + + dig_port = &test_ctx.dev.dig_port; + dig_port->base.type = INTEL_OUTPUT_DP; + + test_ctx.dev.connector.encoder = encoder; + + intel_dp = &dig_port->dp; + intel_dp->attached_connector = &test_ctx.dev.connector; + + intel_dp->link.caps = test_ctx.link_caps_ops->init(intel_dp); + + test->priv = &test_ctx; + + return 0; +} + +static void intel_dp_link_test_exit(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + + ctx->link_caps_ops->cleanup(ctx->dev.dig_port.dp.link.caps); +} + +static int intel_dp_link_test_suite_init(struct kunit_suite *test_suite) +{ +#ifdef I915 + test_ctx.link_caps_ops = &i915_display_dp_link_caps_test_ops; + test_ctx.link_training_ops = &i915_display_dp_link_training_test_ops; +#else + test_ctx.link_caps_ops = &intel_display_dp_link_caps_test_ops; + test_ctx.link_training_ops = &intel_display_dp_link_training_test_ops; +#endif + prandom_seed_state(&test_ctx.rnd, 0); + + return 0; +} + +static struct kunit_suite intel_dp_link_test_suite = { + .name = "intel_dp_link", + .suite_init = intel_dp_link_test_suite_init, + .init = intel_dp_link_test_init, + .exit = intel_dp_link_test_exit, + .test_cases = intel_dp_link_test_cases, +}; + +kunit_test_suites(&intel_dp_link_test_suite); + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Intel DP link KUnit tests"); diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c index 52e16c1e7af0..406e0758e860 100644 --- a/drivers/gpu/drm/imagination/pvr_context.c +++ b/drivers/gpu/drm/imagination/pvr_context.c @@ -309,8 +309,8 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co goto err_free_ctx; ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); - if (IS_ERR(ctx->vm_ctx)) { - err = PTR_ERR(ctx->vm_ctx); + if (!ctx->vm_ctx) { + err = -EINVAL; goto err_free_ctx; } diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c index 6bb5baa6c41b..805d9f9bc1dd 100644 --- a/drivers/gpu/drm/imagination/pvr_fw_trace.c +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c @@ -71,7 +71,7 @@ pvr_fw_trace_init_mask_set(const char *val, const struct kernel_param *kp) return 0; } -const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { +static const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { .set = pvr_fw_trace_init_mask_set, .get = param_get_hexint, }; diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c index 9a1b4525423c..3b6de1b2fbc6 100644 --- a/drivers/gpu/drm/panel/panel-anbernic-td4310.c +++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c @@ -12,6 +12,7 @@ #include <linux/regulator/consumer.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -165,7 +166,7 @@ static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi) if (!ctx->panel_info) return -EINVAL; - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret < 0) return dev_err_probe(dev, ret, "Failed to get panel orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c b/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c index 01b4458e55ad..a70a2e58f88c 100644 --- a/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c +++ b/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c @@ -15,6 +15,7 @@ #include <drm/drm_connector.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -380,7 +381,7 @@ static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), "Failed to get reset GPIO\n"); - ret = of_drm_get_panel_orientation(dsi->dev.of_node, + ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); if (ret) return dev_err_probe(&dsi->dev, ret, diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index 658ce64c71eb..150dff3ab6c3 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -13,6 +13,7 @@ #include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <video/mipi_display.h> @@ -1722,7 +1723,7 @@ static int boe_panel_add(struct boe_panel *boe) boe->base.prepare_prev_first = true; - err = of_drm_get_panel_orientation(dev->of_node, &boe->orientation); + err = drm_of_get_panel_orientation(dev->of_node, &boe->orientation); if (err < 0) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err); return err; diff --git a/drivers/gpu/drm/panel/panel-chipone-icna35xx.c b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c index 86d096455caa..2cc16494a4ce 100644 --- a/drivers/gpu/drm/panel/panel-chipone-icna35xx.c +++ b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c @@ -21,6 +21,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -374,7 +375,7 @@ static int icna35xx_probe(struct mipi_dsi_device *dsi) pinfo->dsi = dsi; mipi_dsi_set_drvdata(dsi, pinfo); - ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &pinfo->orientation); if (ret < 0) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret); return ret; diff --git a/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c index be76bc825c3f..562dc573528d 100644 --- a/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c +++ b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c @@ -13,6 +13,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <video/mipi_display.h> @@ -268,7 +269,7 @@ static int ch13726a_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret < 0) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret); return ret; diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index bd7acc31b292..f66474d3ef65 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -40,6 +40,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_device.h> #include <drm/drm_edid.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> /** @@ -865,7 +866,7 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, return dev_err_probe(dev, PTR_ERR(panel->enable_gpio), "failed to request GPIO\n"); - err = of_drm_get_panel_orientation(dev->of_node, &panel->orientation); + err = drm_of_get_panel_orientation(dev->of_node, &panel->orientation); if (err) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err); return err; diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c index 1f177834d629..d23002b5a2d7 100644 --- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c +++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c @@ -21,6 +21,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> /* Manufacturer specific Commands send via DSI */ @@ -233,7 +234,7 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi) return ret; } - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret < 0) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret); return ret; diff --git a/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c b/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c index ed02a8daf96f..bbc870b8fda8 100644 --- a/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c +++ b/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c @@ -17,6 +17,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -181,7 +182,7 @@ static int ota7290b_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->vdd), "Couldn't get our VDD supply\n"); - ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); if (ret) { dev_err(&dsi->dev, "%pOF: failed to get orientation: %d\n", dsi->dev.of_node, ret); diff --git a/drivers/gpu/drm/panel/panel-himax-hx83102.c b/drivers/gpu/drm/panel/panel-himax-hx83102.c index d7e5664a5838..6a0851ccf9bb 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83102.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83102.c @@ -17,6 +17,7 @@ #include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <video/mipi_display.h> @@ -1264,7 +1265,7 @@ static int hx83102_panel_add(struct hx83102 *ctx) ctx->base.prepare_prev_first = true; - err = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + err = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (err < 0) return dev_err_probe(dev, err, "failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c index 416203da2f45..09124610fdc8 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx8394.c +++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c @@ -21,6 +21,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #define DRV_NAME "panel-himax-hx8394" @@ -991,7 +992,7 @@ static int hx8394_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset gpio\n"); - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret < 0) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret); return ret; diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9488.c b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c index 7302766034fc..6ab8ae72abbf 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9488.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c @@ -13,6 +13,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -237,7 +238,7 @@ static int ili9488_dsi_probe(struct mipi_dsi_device *dsi) if (ret < 0) return dev_err_probe(dev, ret, "failed to get regulators\n"); - ret = of_drm_get_panel_orientation(dev->of_node, &ili->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ili->orientation); if (ret) return dev_err_probe(dev, ret, "failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c index ad33414719fc..0fb06c425545 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c @@ -9,6 +9,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -131,7 +132,7 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi) dsi->format = ctx->desc->format; dsi->lanes = ctx->desc->lanes; - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret) return dev_err_probe(dev, ret, "Failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 0652cdb57d11..3abdd0870e83 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -16,6 +16,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <video/mipi_display.h> @@ -2571,7 +2572,7 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), "Couldn't get our reset GPIO\n"); - ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); if (ret) { dev_err(&dsi->dev, "%pOF: failed to get orientation: %d\n", dsi->dev.of_node, ret); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c index 5f4e0d82ee67..6d07fe901357 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c @@ -13,6 +13,7 @@ #include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <video/mipi_display.h> @@ -795,7 +796,7 @@ static int ili9882t_add(struct ili9882t *ili) gpiod_set_value_cansleep(ili->enable_gpio, 0); - err = of_drm_get_panel_orientation(dev->of_node, &ili->orientation); + err = drm_of_get_panel_orientation(dev->of_node, &ili->orientation); if (err < 0) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err); return err; diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index f6b04de1182e..67ef9e0fea03 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -2992,7 +2992,7 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(jadard->vccio), "failed to get vccio regulator\n"); - ret = of_drm_get_panel_orientation(dev->of_node, &jadard->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &jadard->orientation); if (ret < 0) return dev_err_probe(dev, ret, "failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index 46b07f38559f..37f7498449ec 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -126,7 +126,7 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds) struct device_node *np = lvds->dev->of_node; int ret; - ret = of_drm_get_panel_orientation(np, &lvds->orientation); + ret = drm_of_get_panel_orientation(np, &lvds->orientation); if (ret < 0) { dev_err(lvds->dev, "%pOF: failed to get orientation %d\n", np, ret); return ret; diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36523.c b/drivers/gpu/drm/panel/panel-novatek-nt36523.c index 226d91daf8c7..34b9123d3fd7 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36523.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36523.c @@ -19,6 +19,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #define DSI_NUM_MIN 1 @@ -1206,7 +1207,7 @@ static int nt36523_probe(struct mipi_dsi_device *dsi) pinfo->dsi[0] = dsi; mipi_dsi_set_drvdata(dsi, pinfo); - ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &pinfo->orientation); if (ret < 0) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret); return ret; diff --git a/drivers/gpu/drm/panel/panel-renesas-r63419.c b/drivers/gpu/drm/panel/panel-renesas-r63419.c index 59ac323f5276..ff3846d574e5 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r63419.c +++ b/drivers/gpu/drm/panel/panel-renesas-r63419.c @@ -20,6 +20,7 @@ #include <drm/drm_connector.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -292,7 +293,7 @@ static int renesas_r63419_probe(struct mipi_dsi_device *dsi) mipi_dsi_set_drvdata(dsi, ctx); /* Get panel orientation */ - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret < 0 && ret != -ENODEV) return dev_err_probe(dev, ret, "Failed to get panel orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index c09bf3db5e78..5c169fdf265c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -693,7 +693,7 @@ static struct panel_simple *panel_simple_probe(struct device *dev) return dev_err_cast_probe(dev, panel->enable_gpio, "failed to request GPIO\n"); - err = of_drm_get_panel_orientation(dev->of_node, &panel->orientation); + err = drm_of_get_panel_orientation(dev->of_node, &panel->orientation); if (err) { dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err); return ERR_PTR(err); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 2f79ec4a2063..f16e0de1ea60 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -7,6 +7,7 @@ #include <drm/drm_mipi_dbi.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <linux/bitfield.h> @@ -1296,7 +1297,7 @@ static int st7701_probe(struct device *dev, int connector_type) return PTR_ERR(st7701->reset); } - ret = of_drm_get_panel_orientation(dev->of_node, &st7701->orientation); + ret = drm_of_get_panel_orientation(dev->of_node, &st7701->orientation); if (ret < 0) return dev_err_probe(dev, ret, "Failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c index f1641c9c7d13..fcbb0b7d50e4 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -20,6 +20,7 @@ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #define DRV_NAME "panel-sitronix-st7703" @@ -873,7 +874,7 @@ static int st7703_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->iovcc), "Failed to request iovcc regulator\n"); - ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation); if (ret < 0) return dev_err_probe(&dsi->dev, ret, "Failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index d5f821d6b23c..da6ed31d151e 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -14,6 +14,7 @@ #include <drm/drm_device.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #define ST7789V_RAMCTRL_CMD 0xb0 @@ -646,7 +647,7 @@ static int st7789v_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); - ret = of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation); + ret = drm_of_get_panel_orientation(spi->dev.of_node, &ctx->orientation); if (ret) return dev_err_probe(&spi->dev, ret, "Failed to get orientation\n"); diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 298b046c95ed..5832dccfc093 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -3446,7 +3446,6 @@ queue_timedout_job(struct drm_sched_job *sched_job) static void queue_free_job(struct drm_sched_job *sched_job) { - drm_sched_job_cleanup(sched_job); panthor_job_put(sched_job); } diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c index 9e0711e0095a..21ddf4502ecc 100644 --- a/drivers/gpu/drm/sysfb/simpledrm.c +++ b/drivers/gpu/drm/sysfb/simpledrm.c @@ -28,6 +28,7 @@ #include <drm/drm_managed.h> #include <drm/drm_modeset_helper.h> #include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_of.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -623,6 +624,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, int width, height, stride; u16 width_mm = 0, height_mm = 0; struct device_node *panel_node; + enum drm_panel_orientation orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; const struct drm_format_info *format; u64 size; struct resource *res, *mem = NULL; @@ -698,6 +700,12 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, ret = simplefb_get_panel_height_mm_of(dev, panel_node); if (ret > 0) height_mm = ret; + /* + * Ignore errors from parsing the panel orientation. With + * the orientation initialized to UNKNOWN, the connector + * helpers will do the right thing. + */ + drm_of_get_panel_orientation(panel_node, &orientation); of_node_put(panel_node); } } else { @@ -861,8 +869,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, if (ret) return ERR_PTR(ret); drm_connector_helper_add(connector, &simpledrm_connector_helper_funcs); - drm_connector_set_panel_orientation_with_quirk(connector, - DRM_MODE_PANEL_ORIENTATION_UNKNOWN, + drm_connector_set_panel_orientation_with_quirk(connector, orientation, width, height); ret = drm_connector_attach_encoder(connector, encoder); diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 4a0493c6283d..119a81c9e1c4 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -5,6 +5,7 @@ */ #include <linux/dma-fence-unwrap.h> +#include <linux/overflow.h> #include <drm/drm_print.h> #include <drm/drm_syncobj.h> @@ -1379,6 +1380,126 @@ static const unsigned int cpu_job_bo_handle_count[] = { [V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY] = 1, }; +/* Reject offset + (count - 1) * stride + write_size if it leaves the BO. */ +static int +v3d_check_copy_extent(struct drm_device *dev, size_t bo_size, + u32 offset, u32 stride, u32 count, u64 write_size) +{ + u64 last; + + if (!count) + return 0; + + /* + * The executors walk a u8 * cursor, so the furthest written byte is + * offset + (count - 1) * stride + write_size, matching the pointer + * arithmetic in v3d_copy_query_results()/v3d_copy_performance_query(). + * (count - 1) * stride is a u32 * u32 product that is exact in u64, + * and offset + write_size stays far below the u64 range, so a single + * overflow check guards the total. + */ + last = write_size + offset; + if (check_add_overflow((u64)(count - 1) * stride, last, &last) || + last > bo_size) { + drm_dbg(dev, "CPU job copy buffer exceeds the destination BO.\n"); + return -EINVAL; + } + + return 0; +} + +/* Reject a query CPU job whose writes would land outside their BO. */ +static int +v3d_cpu_job_bounds_check(struct v3d_cpu_job *job) +{ + struct drm_device *dev = &job->base.v3d->drm; + struct v3d_timestamp_query_info *tquery = &job->timestamp_query; + struct v3d_copy_query_results_info *copy = &job->copy; + u32 elem = copy->do_64bit ? sizeof(u64) : sizeof(u32); + struct v3d_bo *dst, *src; + u64 slots, write_size; + u32 i; + + switch (job->job_type) { + case V3D_CPU_JOB_TYPE_TIMESTAMP_QUERY: + case V3D_CPU_JOB_TYPE_RESET_TIMESTAMP_QUERY: + /* Each query writes one u64 timestamp slot into bo[0]. */ + dst = to_v3d_bo(job->base.bo[0]); + + for (i = 0; i < tquery->count; i++) { + if ((u64)tquery->queries[i].offset + sizeof(u64) > + dst->base.base.size) + goto err_range; + } + return 0; + case V3D_CPU_JOB_TYPE_COPY_TIMESTAMP_QUERY: + /* Copies one u64 per query from bo[1] into bo[0]. */ + dst = to_v3d_bo(job->base.bo[0]); + src = to_v3d_bo(job->base.bo[1]); + + for (i = 0; i < tquery->count; i++) { + if ((u64)tquery->queries[i].offset + sizeof(u64) > + src->base.base.size) + goto err_range; + } + + write_size = (copy->availability_bit ? 2 : 1) * elem; + return v3d_check_copy_extent(dev, dst->base.base.size, + copy->offset, copy->stride, + tquery->count, write_size); + case V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY: + /* + * Each query writes nperfmons * DRM_V3D_MAX_PERF_COUNTERS + * counter slots into bo[0], plus an availability slot at + * index ncounters. nperfmons and ncounters are user values, + * so the slot count is computed overflow-safe. + */ + dst = to_v3d_bo(job->base.bo[0]); + + slots = (u64)job->performance_query.nperfmons * + DRM_V3D_MAX_PERF_COUNTERS; + if (copy->availability_bit) + slots = max(slots, + (u64)job->performance_query.ncounters + 1); + + write_size = slots * elem; + return v3d_check_copy_extent(dev, dst->base.base.size, + copy->offset, copy->stride, + job->performance_query.count, + write_size); + case V3D_CPU_JOB_TYPE_INDIRECT_CSD: { + struct v3d_indirect_csd_info *indirect_csd = &job->indirect_csd; + + /* 3 is the three dimensions (x, y, z) of the workgroup counts. */ + src = to_v3d_bo(job->base.bo[0]); + if ((u64)indirect_csd->offset + 3 * sizeof(u32) > + src->base.base.size) + goto err_range; + + dst = to_v3d_bo(indirect_csd->indirect); + for (i = 0; i < 3; i++) { + u32 uidx = indirect_csd->wg_uniform_offsets[i]; + + /* + * 0xffffffff means "skip this rewrite", so the exec + * path never writes that index and it needs no check. + */ + if (uidx != 0xffffffff && + (u64)uidx * sizeof(u32) + sizeof(u32) > + dst->base.base.size) + goto err_range; + } + return 0; + } + default: + return 0; + } + +err_range: + drm_dbg(dev, "CPU job query offset exceeds the BO.\n"); + return -EINVAL; +} + /** * v3d_submit_cpu_ioctl() - Submits a CPU job to the V3D. * @dev: DRM device @@ -1441,6 +1562,10 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count); if (ret) goto fail; + + ret = v3d_cpu_job_bounds_check(cpu_job); + if (ret) + goto fail; } if (cpu_job->job_type == V3D_CPU_JOB_TYPE_INDIRECT_CSD) { diff --git a/drivers/gpu/drm/xe/.gitignore b/drivers/gpu/drm/xe/.gitignore index 8778bf132674..6dad8a5a2135 100644 --- a/drivers/gpu/drm/xe/.gitignore +++ b/drivers/gpu/drm/xe/.gitignore @@ -2,3 +2,4 @@ *.hdrtest /generated /xe_gen_wa_oob +!.kunitconfig-display diff --git a/drivers/gpu/drm/xe/.kunitconfig-display b/drivers/gpu/drm/xe/.kunitconfig-display new file mode 100644 index 000000000000..17020aa4ded3 --- /dev/null +++ b/drivers/gpu/drm/xe/.kunitconfig-display @@ -0,0 +1,11 @@ +CONFIG_EXPERT=y +CONFIG_MODULES=y +CONFIG_KUNIT=y +CONFIG_PCI=y +CONFIG_DEBUG_FS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DRM=m +CONFIG_DRM_XE=m +CONFIG_DRM_XE_DISPLAY=y +CONFIG_DRM_XE_KUNIT_TEST=m diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 6d728f8c4c39..67ada1d6c2fb 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -374,6 +374,9 @@ xe-$(CONFIG_DRM_XE_DP_TUNNEL) += \ obj-$(CONFIG_DRM_XE) += xe.o obj-$(CONFIG_DRM_XE_KUNIT_TEST) += tests/ +ifeq ($(CONFIG_DRM_XE_DISPLAY),y) + obj-$(CONFIG_DRM_XE_KUNIT_TEST) += display/tests/ +endif # header test hdrtest_find_args := -not -path xe_rtp_helpers.h diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h index 83a6e7794982..f5c9b37038d4 100644 --- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h @@ -152,6 +152,7 @@ enum xe_guc_action { XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC = 0x6002, XE_GUC_ACTION_PAGE_FAULT_RES_DESC = 0x6003, XE_GUC_ACTION_ACCESS_COUNTER_NOTIFY = 0x6004, + XE_GUC_ACTION_NOTIFY_UNCORRECTABLE_LOCAL_ERROR = 0x6005, XE_GUC_ACTION_TLB_INVALIDATION = 0x7000, XE_GUC_ACTION_TLB_INVALIDATION_DONE = 0x7001, XE_GUC_ACTION_TLB_INVALIDATION_ALL = 0x7002, diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h index 644f5a4226d7..ec9c22dc21be 100644 --- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h @@ -154,6 +154,11 @@ enum { * (instead of waiting the full timeslice duration). The bit is instead set * to one if a single context is queued on the engine, to avoid it being * switched out if there isn't another context that can run in its place. + * + * _`GUC_KLV_OPT_IN_FEATURE_UNCORRECTABLE_LOCAL_ERROR_NOTIFICATION` : 0x4004 + * This flag will enable notification from GuC to KMD via G2H message + * GUC_ACTION_GUC2HOST_NOTIFY_UNCORRECTABLE_LOCAL_ERROR upon receiving the + * same interrupt from the CS. */ #define GUC_KLV_OPT_IN_FEATURE_EXT_CAT_ERR_TYPE_KEY 0x4001 @@ -162,6 +167,9 @@ enum { #define GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH_KEY 0x4003 #define GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH_LEN 0u +#define GUC_KLV_OPT_IN_FEATURE_UNCORRECTABLE_LOCAL_ERROR_NOTIFICATION_KEY 0x4004 +#define GUC_KLV_OPT_IN_FEATURE_UNCORRECTABLE_LOCAL_ERROR_NOTIFICATION_LEN 0u + /** * DOC: GuC Scheduling Policies KLVs * @@ -504,6 +512,8 @@ enum xe_guc_klv_ids { GUC_WA_KLV_RESET_BB_STACK_PTR_ON_VF_SWITCH = 0x900b, GUC_WA_KLV_RESTORE_UNSAVED_MEDIA_CONTROL_REG = 0x900c, GUC_WA_KLV_CLR_CS_INDIRECT_RING_STATE_IF_IDLE_AT_CTX_REG = 0x900e, + GUC_WA_KLV_REMAP_RANGED_TLB_INV = 0x900f, + GUC_WA_KLV_IGNORE_MMIO_READ_SEM_TOKEN_64 = 0x9010, }; #endif diff --git a/drivers/gpu/drm/xe/display/tests/Makefile b/drivers/gpu/drm/xe/display/tests/Makefile new file mode 100644 index 000000000000..c71686458072 --- /dev/null +++ b/drivers/gpu/drm/xe/display/tests/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +subdir-ccflags-$(CONFIG_DRM_XE_DISPLAY) += \ + -I$(srctree)/drivers/gpu/drm/i915/display/ + +# Rule to build display code shared with i915 +$(obj)/i915-display/tests/%.o: $(srctree)/drivers/gpu/drm/i915/display/tests/%.c FORCE + $(call cmd,force_checksrc) + $(call if_changed_rule,cc_o_c) + +obj-$(CONFIG_DRM_XE_KUNIT_TEST) += xe_display_test.o +xe_display_test-y = \ + i915-display/tests/intel_dp_link_test.o diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h index e5588c88800a..0d234160ee3a 100644 --- a/drivers/gpu/drm/xe/xe_gt_types.h +++ b/drivers/gpu/drm/xe/xe_gt_types.h @@ -145,6 +145,11 @@ struct xe_gt { /** @info.has_indirect_ring_state: GT has indirect ring state support */ u8 has_indirect_ring_state:1; /** + * @info.has_uncorrectable_error_reporting: GT has uncorrectable + * error reporting support + */ + u8 has_uncorrectable_error_reporting:1; + /** * @info.has_xe2_blt_instructions: GT supports Xe2-style MEM_SET * and MEM_COPY blitter functionality. Note that despite the * name, some Xe1 platforms may also support this "Xe2-style" diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 4023700ff2a9..543f3992bccd 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -12,6 +12,7 @@ #include "abi/guc_actions_abi.h" #include "abi/guc_errors_abi.h" +#include "abi/guc_klvs_abi.h" #include "regs/xe_gt_regs.h" #include "regs/xe_gtt_defs.h" #include "regs/xe_guc_regs.h" @@ -641,6 +642,15 @@ int xe_guc_opt_in_features_enable(struct xe_guc *guc) if (GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 7, 0)) klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_EXT_CAT_ERR_TYPE); + /* + * The uncorrectable local error notification opt-in was added in + * GuC v70.38.0, which maps to compatibility version v1.18.0. + */ + if (GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 18, 0) && + guc_to_gt(guc)->info.has_uncorrectable_error_reporting) + klvs[count++] = + PREP_GUC_KLV_TAG(OPT_IN_FEATURE_UNCORRECTABLE_LOCAL_ERROR_NOTIFICATION); + if (supports_dynamic_ics(guc)) klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH); diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index c98454545a85..79d6fd8d919b 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -364,6 +364,15 @@ static void guc_waklv_init(struct xe_guc_ads *ads) guc_waklv_enable(ads, NULL, 0, &offset, &remain, GUC_WA_KLV_CLR_CS_INDIRECT_RING_STATE_IF_IDLE_AT_CTX_REG); + if (XE_GT_WA(gt, 22022079272) && GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 62)) + guc_waklv_enable(ads, NULL, 0, &offset, &remain, GUC_WA_KLV_REMAP_RANGED_TLB_INV); + + /* The GuC does not enable the sem_tok_64 feature on NVL-S */ + if (XE_GT_WA(gt, 16029897822) && gt_to_xe(gt)->info.platform != XE_NOVALAKE_S && + GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 69)) + guc_waklv_enable(ads, NULL, 0, &offset, &remain, + GUC_WA_KLV_IGNORE_MMIO_READ_SEM_TOKEN_64); + size = guc_ads_waklv_size(ads) - remain; if (!size) return; diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c index 21e0dad9a481..fe70c0fd85c5 100644 --- a/drivers/gpu/drm/xe/xe_guc_ct.c +++ b/drivers/gpu/drm/xe/xe_guc_ct.c @@ -1065,6 +1065,11 @@ static int __guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, xe_gt_assert(gt, g2h_len || !num_g2h); lockdep_assert_held(&ct->lock); + if (xe_device_wedged(ct_to_xe(ct))) { + ret = -ENOTRECOVERABLE; + goto out; + } + if (unlikely(ct->ctbs.h2g.info.broken)) { ret = -EPIPE; goto out; @@ -1236,6 +1241,36 @@ static int guc_ct_send(struct xe_guc_ct *ct, const u32 *action, u32 len, return ret; } +/** + * xe_guc_ct_send - Send an HXG message to the GuC over CT + * @ct: the &xe_guc_ct + * @action: dword array with the HXG message (can't be NULL) + * @len: length of the HXG message in dwords (can't be 0) + * @g2h_len: G2H response space to reserve in dwords, or 0 + * @num_g2h: number of G2H messages expected, or 0 + * + * Return codes from the non-blocking send helpers are: + * + * * -ENOTRECOVERABLE: the xe device is wedged. Stop submitting new GuC work; the + * request cannot make progress until the device is recovered. + * * -EPIPE: the H2G CTB is marked broken. The channel stays unusable until the + * CT is restarted, which clears the broken flag. + * * -ENODEV: the CT channel is disabled, messages not expected in this state. + * Don't retry until it is enabled again. + * * -ECANCELED: the CT channel is stopped or a GT recovery is pending; the + * message was dropped. Often benign. Cancel-tolerant callers (e.g. TLB + * invalidations, GuC submission) rely on the stop/start flow to recover; + * others should retry once the CT is re-enabled or the reset/recovery + * completes. + * * -EDEADLK: no CTB room and the wait for space timed out. The send helpers + * have already requested an async GT reset before returning this error. + * + * -ENOMEM may also be returned if an internal allocation fails; the blocking + * xe_guc_ct_send_recv() path retries that allocation. -EBUSY and + * -EAGAIN are internal flow-control results handled by the send helpers. + * + * Return: 0 on success, or a negative error code on failure. + */ int xe_guc_ct_send(struct xe_guc_ct *ct, const u32 *action, u32 len, u32 g2h_len, u32 num_g2h) { @@ -1388,7 +1423,7 @@ wait_again: if (g2h_fence.fail) { if (g2h_fence.cancel) { xe_gt_dbg(gt, "H2G request %#x canceled!\n", action[0]); - ret = -ECANCELED; + ret = xe_device_wedged(ct_to_xe(ct)) ? -ENOTRECOVERABLE : -ECANCELED; goto unlock; } xe_gt_err(gt, "H2G request %#x failed: error %#x hint %#x\n", @@ -1661,6 +1696,9 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len) ret = xe_guc_exec_queue_memory_cat_error_handler(guc, payload, adj_len); break; + case XE_GUC_ACTION_NOTIFY_UNCORRECTABLE_LOCAL_ERROR: + ret = xe_guc_uncorrectable_error_handler(guc, payload, adj_len); + break; case XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC: ret = xe_guc_pagefault_handler(guc, payload, adj_len); break; @@ -1724,6 +1762,9 @@ static int g2h_read(struct xe_guc_ct *ct, u32 *msg, bool fast_path) xe_gt_assert(gt, xe_guc_ct_initialized(ct)); lockdep_assert_held(&ct->fast_lock); + if (xe_device_wedged(xe)) + return -ENOTRECOVERABLE; + if (ct->state == XE_GUC_CT_STATE_DISABLED) return -ENODEV; diff --git a/drivers/gpu/drm/xe/xe_guc_engine_activity.c b/drivers/gpu/drm/xe/xe_guc_engine_activity.c index 2b99c1ebdd58..f43ca1c76f75 100644 --- a/drivers/gpu/drm/xe/xe_guc_engine_activity.c +++ b/drivers/gpu/drm/xe/xe_guc_engine_activity.c @@ -473,7 +473,7 @@ void xe_guc_engine_activity_enable_stats(struct xe_guc *guc) ret = enable_engine_activity_stats(guc); if (ret) - xe_gt_err(guc_to_gt(guc), "failed to enable activity stats%d\n", ret); + xe_gt_err(guc_to_gt(guc), "failed to enable activity stats: %pe\n", ERR_PTR(ret)); else engine_activity_set_cpu_ts(guc, 0); } diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 12416bfa3255..e5fcb1f21115 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -3022,6 +3022,38 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg, return 0; } +int xe_guc_uncorrectable_error_handler(struct xe_guc *guc, u32 *msg, u32 len) +{ + struct xe_gt *gt = guc_to_gt(guc); + struct xe_exec_queue *q; + u32 guc_id; + + if (unlikely(!len || len > 1)) + return -EPROTO; + + guc_id = msg[0]; + + if (guc_id == GUC_ID_UNKNOWN) { + xe_gt_err(gt, "GuC: Uncorrectable local error with unknown GuC id\n"); + return 0; + } + + q = g2h_exec_queue_lookup(guc, guc_id); + if (unlikely(!q)) + return -EPROTO; + + xe_gt_err(gt, + "GuC: Uncorrectable local error! guc_id=%d class=%s, logical_mask=0x%x", + guc_id, xe_hw_engine_class_to_str(q->class), q->logical_mask); + + trace_xe_guc_uncorrectable_error(q); + + /* Treat the same as engine reset */ + xe_guc_exec_queue_reset_trigger_cleanup(q); + + return 0; +} + int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len) { struct xe_gt *gt = guc_to_gt(guc); diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h index b3839a90c142..ccade320dc69 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.h +++ b/drivers/gpu/drm/xe/xe_guc_submit.h @@ -34,6 +34,7 @@ int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len); int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len); int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg, u32 len); +int xe_guc_uncorrectable_error_handler(struct xe_guc *guc, u32 *msg, u32 len); int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len); int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len); int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 len); diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 91af603e9431..11a69dffdce7 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -120,6 +120,7 @@ static const struct xe_graphics_desc graphics_xe2 = { static const struct xe_graphics_desc graphics_xe3p_lpg = { XE2_GFX_FEATURES, .has_indirect_ring_state = 1, + .has_uncorrectable_error_reporting = 1, .multi_queue_engine_class_mask = BIT(XE_ENGINE_CLASS_COPY) | BIT(XE_ENGINE_CLASS_COMPUTE), .num_geometry_xecore_fuse_regs = 3, .num_compute_xecore_fuse_regs = 3, @@ -129,6 +130,7 @@ static const struct xe_graphics_desc graphics_xe3p_xpc = { XE2_GFX_FEATURES, .has_access_counter = 0, .has_indirect_ring_state = 1, + .has_uncorrectable_error_reporting = 1, .hw_engine_mask = GENMASK(XE_HW_ENGINE_BCS8, XE_HW_ENGINE_BCS1) | GENMASK(XE_HW_ENGINE_CCS3, XE_HW_ENGINE_CCS0), @@ -151,6 +153,14 @@ static const struct xe_media_desc media_xelpmp = { BIT(XE_HW_ENGINE_GSCCS0) }; +static const struct xe_media_desc media_xe3p_hpm = { + .has_uncorrectable_error_reporting = 1, + .hw_engine_mask = + GENMASK(XE_HW_ENGINE_VCS7, XE_HW_ENGINE_VCS0) | + GENMASK(XE_HW_ENGINE_VECS3, XE_HW_ENGINE_VECS0) | + BIT(XE_HW_ENGINE_GSCCS0) +}; + /* Pre-GMDID Graphics IPs */ static const struct xe_ip graphics_ip_xelp = { 1200, "Xe_LP", &graphics_xelp }; static const struct xe_ip graphics_ip_xelpp = { 1210, "Xe_LP+", &graphics_xelp }; @@ -186,7 +196,7 @@ static const struct xe_ip media_ips[] = { { 3000, "Xe3_LPM", &media_xelpmp }, { 3002, "Xe3_LPM", &media_xelpmp }, { 3500, "Xe3p_LPM", &media_xelpmp }, - { 3503, "Xe3p_HPM", &media_xelpmp }, + { 3503, "Xe3p_HPM", &media_xe3p_hpm }, }; #define MULTI_LRC_MASK \ @@ -875,6 +885,8 @@ static struct xe_gt *alloc_primary_gt(struct xe_tile *tile, gt->info.type = XE_GT_TYPE_MAIN; gt->info.id = tile->id * xe->info.max_gt_per_tile; gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state; + gt->info.has_uncorrectable_error_reporting = + graphics_desc->has_uncorrectable_error_reporting; gt->info.multi_queue_engine_class_mask = graphics_desc->multi_queue_engine_class_mask; gt->info.engine_mask = graphics_desc->hw_engine_mask; gt->info.num_geometry_xecore_fuse_regs = graphics_desc->num_geometry_xecore_fuse_regs; @@ -920,6 +932,7 @@ static struct xe_gt *alloc_media_gt(struct xe_tile *tile, gt->info.type = XE_GT_TYPE_MEDIA; gt->info.id = tile->id * xe->info.max_gt_per_tile + 1; gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state; + gt->info.has_uncorrectable_error_reporting = media_desc->has_uncorrectable_error_reporting; gt->info.engine_mask = media_desc->hw_engine_mask; return gt; diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h index 24d4a3d00517..fed509ff601e 100644 --- a/drivers/gpu/drm/xe/xe_pci_types.h +++ b/drivers/gpu/drm/xe/xe_pci_types.h @@ -79,12 +79,14 @@ struct xe_graphics_desc { u8 has_ctx_tlb_inval:1; u8 has_usm:1; u8 has_64bit_timestamp:1; + u8 has_uncorrectable_error_reporting:1; }; struct xe_media_desc { u64 hw_engine_mask; /* hardware engines provided by media IP */ u8 has_indirect_ring_state:1; + u8 has_uncorrectable_error_reporting:1; }; struct xe_ip { diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 5fdad444009f..7111b5d0f6aa 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -760,7 +760,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, return -EAGAIN; } if (xe_svm_range_has_dma_mapping(range)) { - xe_res_first_dma(range->base.pages.dma_addr, 0, + xe_res_first_dma(range->pages.dma_addr, 0, xe_svm_range_size(range), &curs); xe_svm_range_debug(range, "BIND PREPARE - MIXED"); @@ -1418,6 +1418,7 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +#if IS_ENABLED(CONFIG_DRM_GPUSVM) /* * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() * and the matching late release in xe_pt_update_ops_run(). Read mode by @@ -1444,6 +1445,10 @@ static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) xe_svm_notifier_unlock(vm); #endif } +#else +static inline void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) { } +static inline void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) { } +#endif #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index e0f0c23d172d..b228a737cfd6 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -66,7 +66,7 @@ static bool xe_svm_range_in_vram(struct xe_svm_range *range) struct drm_gpusvm_pages_flags flags = { /* Pairs with WRITE_ONCE in drm_gpusvm.c */ - .__flags = READ_ONCE(range->base.pages.flags.__flags), + .__flags = READ_ONCE(range->pages.flags.__flags), }; return flags.has_devmem_pages; @@ -96,7 +96,7 @@ static struct xe_vm *range_to_vm(struct drm_gpusvm_range *r) (r__)->base.gpusvm, \ xe_svm_range_in_vram((r__)) ? 1 : 0, \ xe_svm_range_has_vram_binding((r__)) ? 1 : 0, \ - (r__)->base.pages.notifier_seq, \ + (r__)->pages.notifier_seq, \ xe_svm_range_start((r__)), xe_svm_range_end((r__)), \ xe_svm_range_size((r__))) @@ -115,6 +115,7 @@ xe_svm_range_alloc(struct drm_gpusvm *gpusvm) return NULL; INIT_LIST_HEAD(&range->garbage_collector_link); + drm_gpusvm_init_pages(&range->pages, &gpusvm_to_vm(gpusvm)->xe->drm); xe_vm_get(gpusvm_to_vm(gpusvm)); return &range->base; @@ -122,8 +123,10 @@ xe_svm_range_alloc(struct drm_gpusvm *gpusvm) static void xe_svm_range_free(struct drm_gpusvm_range *range) { + drm_gpusvm_free_pages(range->gpusvm, &(to_xe_range(range)->pages), + drm_gpusvm_range_size(range) >> PAGE_SHIFT); xe_vm_put(range_to_vm(range)); - kfree(range); + kfree(to_xe_range(range)); } static void @@ -134,7 +137,8 @@ xe_svm_garbage_collector_add_range(struct xe_vm *vm, struct xe_svm_range *range, range_debug(range, "GARBAGE COLLECTOR ADD"); - drm_gpusvm_range_set_unmapped(&range->base, mmu_range); + drm_gpusvm_range_set_unmapped(&range->base, &range->pages, 1, + mmu_range); spin_lock(&vm->svm.garbage_collector.lock); if (list_empty(&range->garbage_collector_link)) @@ -166,7 +170,7 @@ xe_svm_range_notifier_event_begin(struct xe_vm *vm, struct drm_gpusvm_range *r, range_debug(range, "NOTIFIER"); /* Skip if already unmapped or if no binding exist */ - if (range->base.pages.flags.unmapped || !range->tile_present) + if (range->base.flags.unmapped || !range->tile_present) return 0; range_debug(range, "NOTIFIER - EXECUTE"); @@ -208,7 +212,8 @@ xe_svm_range_notifier_event_end(struct xe_vm *vm, struct drm_gpusvm_range *r, xe_svm_assert_in_notifier(vm); - drm_gpusvm_range_unmap_pages(&vm->svm.gpusvm, r, &ctx); + drm_gpusvm_unmap_pages(&vm->svm.gpusvm, &(to_xe_range(r)->pages), + drm_gpusvm_range_size(r) >> PAGE_SHIFT, &ctx); if (!xe_vm_is_closed(vm) && mmu_range->event == MMU_NOTIFY_UNMAP) xe_svm_garbage_collector_add_range(vm, to_xe_range(r), mmu_range); @@ -299,6 +304,7 @@ range_notifier_event_end: static int __xe_svm_garbage_collector(struct xe_vm *vm, struct xe_svm_range *range) { + struct drm_gpusvm_ctx ctx = { .in_notifier = false, }; struct dma_fence *fence; range_debug(range, "GARBAGE COLLECTOR"); @@ -310,6 +316,10 @@ static int __xe_svm_garbage_collector(struct xe_vm *vm, return PTR_ERR(fence); dma_fence_put(fence); + drm_gpusvm_unmap_pages(&vm->svm.gpusvm, &range->pages, + drm_gpusvm_range_size(&range->base) >> PAGE_SHIFT, + &ctx); + drm_gpusvm_range_remove(&vm->svm.gpusvm, &range->base); return 0; @@ -901,7 +911,7 @@ int xe_svm_init(struct xe_vm *vm) return err; } - err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM", &vm->xe->drm, + err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM", current->mm, 0, vm->size, xe_modparam.svm_notifier_size * SZ_1M, &gpusvm_ops, fault_chunk_sizes, @@ -915,7 +925,7 @@ int xe_svm_init(struct xe_vm *vm) } } else { err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM (simple)", - &vm->xe->drm, NULL, 0, 0, 0, NULL, + NULL, 0, 0, 0, NULL, NULL, 0); } @@ -944,15 +954,28 @@ void xe_svm_close(struct xe_vm *vm) */ void xe_svm_fini(struct xe_vm *vm) { + struct drm_gpusvm_notifier *notifier, *next; + struct drm_gpusvm_ctx ctx = { .in_notifier = false, }; + xe_assert(vm->xe, xe_vm_is_closed(vm)); + drm_gpusvm_for_each_notifier_safe(notifier, next, &vm->svm.gpusvm, 0, LONG_MAX) { + struct drm_gpusvm_range *range, *__next; + + drm_gpusvm_for_each_range_safe(range, __next, notifier, 0, LONG_MAX) + drm_gpusvm_unmap_pages(&vm->svm.gpusvm, + &(to_xe_range(range)->pages), + drm_gpusvm_range_size(range) >> PAGE_SHIFT, + &ctx); + } + drm_gpusvm_fini(&vm->svm.gpusvm); } static bool xe_svm_range_has_pagemap_locked(const struct xe_svm_range *range, const struct drm_pagemap *dpagemap) { - return range->base.pages.dpagemap == dpagemap; + return range->pages.dpagemap == dpagemap; } static bool xe_svm_range_has_pagemap(struct xe_svm_range *range, @@ -1017,7 +1040,7 @@ bool xe_svm_range_validate(struct xe_vm *vm, if (dpagemap) ret = ret && xe_svm_range_has_pagemap_locked(range, dpagemap); else - ret = ret && !range->base.pages.dpagemap; + ret = ret && !range->pages.dpagemap; xe_svm_notifier_unlock(vm); @@ -1135,8 +1158,12 @@ bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vm { struct xe_vm *vm = range_to_vm(&range->base); u64 range_size = xe_svm_range_size(range); + struct drm_gpusvm_range_flags flags = { + /* READ_ONCE pairs with WRITE_ONCE in drm_gpusvm_range_set_unmapped() */ + .__flags = READ_ONCE(range->base.flags.__flags), + }; - if (!range->base.pages.flags.migrate_devmem || !dpagemap) + if (!flags.migrate_devmem || !dpagemap) return false; xe_assert(vm->xe, IS_DGFX(vm->xe)); @@ -1220,6 +1247,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, struct xe_validation_ctx vctx; struct drm_exec exec; struct xe_svm_range *range; + struct drm_gpusvm_range_flags range_flags; struct dma_fence *fence; struct drm_pagemap *dpagemap; struct xe_tile *tile = gt_to_tile(gt); @@ -1248,7 +1276,9 @@ retry: xe_svm_range_fault_count_stats_incr(gt, range); - if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) + /* READ_ONCE pairs with WRITE_ONCE in drm_gpusvm_range_set_unmapped() */ + range_flags.__flags = READ_ONCE(range->base.flags.__flags); + if (ctx.devmem_only && !range_flags.migrate_devmem) return -EACCES; if (xe_svm_range_is_valid(range, tile, ctx.devmem_only, dpagemap)) { @@ -1505,7 +1535,11 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range, { int err = 0; - err = drm_gpusvm_range_get_pages(&vm->svm.gpusvm, &range->base, ctx); + err = drm_gpusvm_get_pages(&vm->svm.gpusvm, &range->pages, + vm->svm.gpusvm.mm, + &range->base.notifier->notifier, + drm_gpusvm_range_start(&range->base), + drm_gpusvm_range_end(&range->base), ctx); if (err == -EOPNOTSUPP) { range_debug(range, "PAGE FAULT - EVICT PAGES"); drm_gpusvm_range_evict(&vm->svm.gpusvm, &range->base); @@ -1620,8 +1654,12 @@ int xe_svm_alloc_vram(struct xe_svm_range *range, const struct drm_gpusvm_ctx *c struct xe_device *xe = vm->xe; int err, retries = 1; bool write_locked = false; + struct drm_gpusvm_range_flags flags = { + /* READ_ONCE pairs with WRITE_ONCE in drm_gpusvm_range_set_unmapped() */ + .__flags = READ_ONCE(range->base.flags.__flags), + }; - xe_assert(range_to_vm(&range->base)->xe, range->base.pages.flags.migrate_devmem); + xe_assert(range_to_vm(&range->base)->xe, flags.migrate_devmem); range_debug(range, "ALLOCATE VRAM"); migration_state = drm_gpusvm_scan_mm(&range->base, diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h index 3ca46a6f98c7..a921556d3466 100644 --- a/drivers/gpu/drm/xe/xe_svm.h +++ b/drivers/gpu/drm/xe/xe_svm.h @@ -31,6 +31,8 @@ struct xe_vram_region; struct xe_svm_range { /** @base: base drm_gpusvm_range */ struct drm_gpusvm_range base; + /** @pages: Page/DMA mapping state for this range (single drm_device). */ + struct drm_gpusvm_pages pages; /** * @garbage_collector_link: Link into VM's garbage collect SVM range * list. Protected by VM's garbage collect lock. @@ -74,7 +76,7 @@ struct xe_pagemap { */ static inline bool xe_svm_range_pages_valid(struct xe_svm_range *range) { - return drm_gpusvm_range_pages_valid(range->base.gpusvm, &range->base); + return drm_gpusvm_pages_valid(range->base.gpusvm, &range->pages); } int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr); @@ -132,7 +134,7 @@ void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem); static inline bool xe_svm_range_has_dma_mapping(struct xe_svm_range *range) { lockdep_assert_held(&range->base.gpusvm->notifier_lock); - return range->base.pages.flags.has_dma_mapping; + return range->pages.flags.has_dma_mapping; } /** @@ -210,10 +212,10 @@ struct xe_vram_region; struct xe_svm_range { struct { struct interval_tree_node itree; - struct { - const struct drm_pagemap_addr *dma_addr; - } pages; } base; + struct { + const struct drm_pagemap_addr *dma_addr; + } pages; u32 tile_present; u32 tile_invalidated; }; @@ -233,7 +235,7 @@ static inline int xe_svm_init(struct xe_vm *vm) { #if IS_ENABLED(CONFIG_DRM_GPUSVM) - return drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM (simple)", &vm->xe->drm, + return drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM (simple)", NULL, 0, 0, 0, NULL, NULL, 0); #else return 0; diff --git a/drivers/gpu/drm/xe/xe_trace.h b/drivers/gpu/drm/xe/xe_trace.h index 750fa32c13b2..2fe8f89a1e34 100644 --- a/drivers/gpu/drm/xe/xe_trace.h +++ b/drivers/gpu/drm/xe/xe_trace.h @@ -213,6 +213,11 @@ DEFINE_EVENT(xe_exec_queue, xe_exec_queue_memory_cat_error, TP_ARGS(q) ); +DEFINE_EVENT(xe_exec_queue, xe_guc_uncorrectable_error, + TP_PROTO(struct xe_exec_queue *q), + TP_ARGS(q) +); + DEFINE_EVENT(xe_exec_queue, xe_exec_queue_cgp_context_error, TP_PROTO(struct xe_exec_queue *q), TP_ARGS(q) diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c index 3f08a3b54062..a8e6f18cc9b4 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -115,6 +115,7 @@ 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, major_ver(xe, guc, nvl, 70, 71, 0)) \ 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/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c index 1d2ab678faf5..8b2d461ea0b2 100644 --- a/drivers/gpu/drm/xe/xe_userptr.c +++ b/drivers/gpu/drm/xe/xe_userptr.c @@ -390,19 +390,20 @@ int xe_userptr_setup(struct xe_userptr_vma *uvma, unsigned long start, unsigned long range) { struct xe_userptr *userptr = &uvma->userptr; + struct xe_vm *vm = xe_vma_vm(&uvma->vma); int err; INIT_LIST_HEAD(&userptr->invalidate_link); INIT_LIST_HEAD(&userptr->repin_link); + drm_gpusvm_init_pages(&userptr->pages, &vm->xe->drm); + err = mmu_interval_notifier_insert(&userptr->notifier, current->mm, start, range, &vma_userptr_notifier_ops); if (err) return err; - userptr->pages.notifier_seq = LONG_MAX; - return 0; } diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index 9e343f9aa44d..0474768a38aa 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -657,7 +657,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil xe_device_is_l2_flush_optimized(xe) && (pat_index != 19 && coh_mode != XE_COH_2WAY))) { err = -EINVAL; - goto madv_fini; + goto free_vmas; } } diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules index 9027365f0043..5d6574ec9dee 100644 --- a/drivers/gpu/drm/xe/xe_wa_oob.rules +++ b/drivers/gpu/drm/xe/xe_wa_oob.rules @@ -66,3 +66,8 @@ 14025883347 MEDIA_VERSION_RANGE(1301, 3503) GRAPHICS_VERSION_RANGE(2004, 3005) 16029380221 MEDIA_VERSION(3500) +22022079272 MEDIA_VERSION(3503) + GRAPHICS_VERSION(3510) + GRAPHICS_VERSION(3511) +16029897822 MEDIA_VERSION(3500) + GRAPHICS_VERSION(3510) diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index f0c595175c9c..08f4acef09f6 100644 --- a/drivers/gpu/nova-core/fsp.rs +++ b/drivers/gpu/nova-core/fsp.rs @@ -85,16 +85,16 @@ impl FspMessageHeader { } } -/// Complete FSP response structure with MCTP and NVDM headers. +/// Common FSP response header with MCTP, NVDM and command response payloads. #[repr(C, packed)] #[derive(Clone, Copy)] -struct FspResponse { +struct FspResponseHeader { header: FspMessageHeader, response: NvdmPayloadCommandResponse, } -// SAFETY: FspResponse is a packed C struct with only integral fields. -unsafe impl FromBytes for FspResponse {} +// SAFETY: FspResponseHeader is a packed C struct with only integral fields. +unsafe impl FromBytes for FspResponseHeader {} /// Trait implemented by types representing a message to send to FSP. /// @@ -273,10 +273,11 @@ impl<'a> Fsp<'a> { dev_err!(dev, "FSP response error: {:?}\n", e); })?; - let (response, _) = FspResponse::from_bytes_prefix(&response_buf[..]).ok_or_else(|| { - dev_err!(dev, "FSP response too small: {}\n", response_buf.len()); - EIO - })?; + let (response, _) = + FspResponseHeader::from_bytes_prefix(&response_buf[..]).ok_or_else(|| { + dev_err!(dev, "FSP response too small: {}\n", response_buf.len()); + EIO + })?; let mctp_header = response.header.mctp_header; let nvdm_header = response.header.nvdm_header; diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index ab0491b57944..152093aafae6 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -135,7 +135,7 @@ impl super::Gsp { self.cmdq .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?; self.cmdq - .send_command_no_wait(bar, commands::SetRegistry::new())?; + .send_command_no_wait(bar, commands::SetRegistry::new()?)?; hal.post_boot(&self, &ctx, &gsp_fw)?; diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 86a3747cd31c..08380de39048 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -67,37 +67,44 @@ struct RegistryEntry { /// The `SetRegistry` command. pub(crate) struct SetRegistry { - entries: [RegistryEntry; Self::NUM_ENTRIES], + entries: KVec<RegistryEntry>, } impl SetRegistry { - // For now we hard-code the registry entries. Future work will allow others to - // be added as module parameters. - const NUM_ENTRIES: usize = 3; - /// Creates a new `SetRegistry` command, using a set of hardcoded entries. - pub(crate) fn new() -> Self { - Self { - entries: [ - // RMSecBusResetEnable - enables PCI secondary bus reset - RegistryEntry { - key: "RMSecBusResetEnable", - value: 1, - }, - // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on - // any PCI reset. - RegistryEntry { - key: "RMForcePcieConfigSave", - value: 1, - }, - // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found - // in the internal product name database. - RegistryEntry { - key: "RMDevidCheckIgnore", - value: 1, - }, - ], - } + pub(crate) fn new() -> Result<Self> { + let mut entries = KVec::new(); + + // RMSecBusResetEnable - enables PCI secondary bus reset + entries.push( + RegistryEntry { + key: "RMSecBusResetEnable", + value: 1, + }, + GFP_KERNEL, + )?; + + // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on + // any PCI reset. + entries.push( + RegistryEntry { + key: "RMForcePcieConfigSave", + value: 1, + }, + GFP_KERNEL, + )?; + + // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found + // in the internal product name database. + entries.push( + RegistryEntry { + key: "RMDevidCheckIgnore", + value: 1, + }, + GFP_KERNEL, + )?; + + Ok(Self { entries }) } } @@ -108,15 +115,18 @@ impl CommandToGsp for SetRegistry { type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { - Self::Command::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32) + Self::Command::init( + self.entries.len() as u32, + self.variable_payload_len() as u32, + ) } fn variable_payload_len(&self) -> usize { let mut key_size = 0; - for i in 0..Self::NUM_ENTRIES { - key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator + for entry in self.entries.iter() { + key_size += entry.key.len() + 1; // +1 for NULL terminator } - Self::NUM_ENTRIES * size_of::<fw::commands::PackedRegistryEntry>() + key_size + self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>() + key_size } fn init_variable_payload( @@ -124,12 +134,12 @@ impl CommandToGsp for SetRegistry { dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>, ) -> Result { let string_data_start_offset = size_of::<Self::Command>() - + Self::NUM_ENTRIES * size_of::<fw::commands::PackedRegistryEntry>(); + + self.entries.len() * size_of::<fw::commands::PackedRegistryEntry>(); // Array for string data. let mut string_data = KVec::new(); - for entry in self.entries.iter().take(Self::NUM_ENTRIES) { + for entry in self.entries.iter() { dst.write_all( fw::commands::PackedRegistryEntry::new( (string_data_start_offset + string_data.len()) as u32, diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index fc17ad93a376..6502e20517a8 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -221,3 +221,4 @@ module_platform_driver(iio_hwmon_driver); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("IIO to hwmon driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 1ac0288fbbd8..4fa7f522f245 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -748,3 +748,4 @@ MODULE_DESCRIPTION("NTC Thermistor Driver"); MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ntc-thermistor"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 0f478f142400..eb521125f4eb 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -121,9 +121,8 @@ static const struct iio_chan_spec gravity_channels[] = { /* Channel read_raw handler */ static int accel_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int report_id = -1; @@ -150,7 +149,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, else { *val = 0; hid_sensor_power_state(&accel_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&accel_state->common_attributes, false); @@ -183,10 +182,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int accel_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct accel_3d_state *accel_state = iio_priv(indio_dev); int ret = 0; @@ -222,8 +219,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data, /* Callback handler to send event after all samples are received and captured */ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -246,9 +242,9 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -280,10 +276,10 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int accel_3d_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct accel_3d_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct accel_3d_state *st) { int ret; @@ -302,10 +298,10 @@ static int accel_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n", - st->accel[0].index, - st->accel[0].report_id, - st->accel[1].index, st->accel[1].report_id, - st->accel[2].index, st->accel[2].report_id); + st->accel[0].index, + st->accel[0].report_id, + st->accel[1].index, st->accel[1].report_id, + st->accel[2].index, st->accel[2].report_id); st->scale_precision = hid_sensor_format_scale( hsdev->usage, @@ -328,7 +324,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct accel_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -365,8 +361,8 @@ static int hid_accel_3d_probe(struct platform_device *pdev) return -ENOMEM; } ret = accel_3d_parse_report(pdev, hsdev, - (struct iio_chan_spec *)indio_dev->channels, - hsdev->usage, accel_state); + (struct iio_chan_spec *)indio_dev->channels, + hsdev->usage, accel_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); return ret; @@ -379,32 +375,32 @@ static int hid_accel_3d_probe(struct platform_device *pdev) atomic_set(&accel_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &accel_state->common_attributes); + &accel_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - accel_state->callbacks.send_event = accel_3d_proc_event; accel_state->callbacks.capture_sample = accel_3d_capture_sample; accel_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &accel_state->callbacks); + &accel_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); return ret; @@ -417,8 +413,8 @@ static void hid_accel_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct accel_3d_state *accel_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); } diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index bc21034078b5..d1d6ca27cce1 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1077,6 +1077,7 @@ config MAX1363 config MAX14001 tristate "Analog Devices MAX14001/MAX14002 ADC driver" depends on SPI + select REGMAP help Say yes here to build support for Analog Devices MAX14001/MAX14002 Configurable, Isolated 10-bit ADCs for Multi-Range Binary Inputs. @@ -1087,6 +1088,7 @@ config MAX14001 config MAX34408 tristate "Maxim max34408/max344089 ADC driver" depends on I2C + select REGMAP_I2C help Say yes here to build support for Maxim max34408/max34409 current sense monitor with 8-bits ADC interface with overcurrent delay/threshold and @@ -1166,6 +1168,17 @@ config MCP3911 This driver can also be built as a module. If so, the module will be called mcp3911. +config MEDIATEK_MT6323_AUXADC + tristate "MediaTek MT6323 PMIC AUXADC driver" + depends on MFD_MT6397 + help + Say yes here to enable support for MediaTek MT6323 PMIC Auxiliary ADC. + This driver provides multiple channels for system monitoring, + such as battery voltage, PMIC temperature, and others. + + This driver can also be built as a module. If so, the module will be + called mt6323-auxadc. + config MEDIATEK_MT6359_AUXADC tristate "MediaTek MT6359 PMIC AUXADC driver" depends on MFD_MT6397 @@ -1965,6 +1978,39 @@ config TWL6030_GPADC This driver can also be built as a module. If so, the module will be called twl6030-gpadc. +config VERSAL_SYSMON_CORE + tristate + select REGMAP + +config VERSAL_SYSMON + tristate "AMD Versal SysMon driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on HAS_IOMEM + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon). This driver provides voltage and temperature + monitoring through the IIO subsystem. + + The SysMon measures up to 160 supply voltages and reads up to + 64 temperature satellites distributed across the SoC. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon. + +config VERSAL_SYSMON_I2C + tristate "AMD Versal SysMon I2C driver" + depends on I2C + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon) via I2C interface. This driver enables voltage + and temperature monitoring when the Versal chip has SysMon + configured with I2C access. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon-i2c. + config VF610_ADC tristate "Freescale vf610 ADC driver" depends on HAS_IOMEM diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 707dd708912f..0f90b75577ff 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MCP3564) += mcp3564.o obj-$(CONFIG_MCP3911) += mcp3911.o +obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o @@ -168,6 +169,9 @@ obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o +obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o +obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o +obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c index 003e23d6e242..79fb38d0f400 100644 --- a/drivers/iio/adc/ad7779.c +++ b/drivers/iio/adc/ad7779.c @@ -142,7 +142,6 @@ struct ad7779_state { const struct ad7779_chip_info *chip_info; struct clk *mclk; struct iio_trigger *trig; - struct completion completion; unsigned int sampling_freq; enum ad7779_filter filter_enabled; struct iio_backend *back; @@ -842,8 +841,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name, st->trig); if (ret) - return dev_err_probe(dev, ret, "request IRQ %d failed\n", - st->spi->irq); + return ret; ret = devm_iio_trigger_register(dev, st->trig); if (ret) @@ -851,8 +849,6 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev indio_dev->trig = iio_trigger_get(st->trig); - init_completion(&st->completion); - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, &iio_pollfunc_store_time, &ad7779_trigger_handler, diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index b80cdd8ad982..c6c3ea953fea 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * ADE9000 driver * * Copyright 2025 Analog Devices Inc. diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index 30672e584c10..aace7e103770 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -405,3 +405,4 @@ module_platform_driver(envelope_detector_driver); MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index fb7f53316e3d..b3cecbf9a89e 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -175,6 +175,7 @@ static const struct spi_device_id max1241_id[] = { { .name = "max1241" }, { } }; +MODULE_DEVICE_TABLE(spi, max1241_id); static const struct of_device_id max1241_dt_ids[] = { { .compatible = "maxim,max1241" }, diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c new file mode 100644 index 000000000000..c450fb6f09cb --- /dev/null +++ b/drivers/iio/adc/mt6323-auxadc.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 Roman Vivchar <rva333@protonmail.com> + * + * Based on drivers/iio/adc/mt6359-auxadc.c + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/stringify.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <linux/mfd/mt6323/registers.h> + +#include <dt-bindings/iio/adc/mediatek,mt6323-auxadc.h> + +#define AUXADC_STRUP_CON10_RSTB_SEL BIT(7) +#define AUXADC_STRUP_CON10_RSTB_SW BIT(5) + +#define AUXADC_TOP_CKPDN2_CTL_CK BIT(5) + +#define AUXADC_TRIM_CH2_MASK GENMASK(11, 10) +#define AUXADC_TRIM_CH4_MASK GENMASK(9, 8) +#define AUXADC_TRIM_CH5_MASK GENMASK(5, 4) +#define AUXADC_TRIM_CH6_MASK GENMASK(3, 2) + +#define AUXADC_CON27_VREF18_ENB_MD BIT(15) +#define AUXADC_CON27_MD_STATUS BIT(0) + +#define AUXADC_CON19_GPS_STATUS BIT(1) + +#define AUXADC_CON26_VREF18_SELB BIT(1) +#define AUXADC_CON26_DECI_GDLY_SEL BIT(0) + +#define AUXADC_CON11_VBUF_EN BIT(4) + +#define AUXADC_CON19_DECI_GDLY_MASK GENMASK(15, 14) +#define AUXADC_ADC19_BUSY_MASK GENMASK(15, 1) +#define AUXADC_READY_MASK BIT(15) +#define AUXADC_DATA_MASK GENMASK(14, 0) + +#define AUXADC_CON9_OSR_MASK GENMASK(12, 10) +#define AUXADC_DEFAULT_OSR 3 + +#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _addr, \ + .datasheet_name = __stringify(_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + +/* + * AUXADC reports everything in mV, including temperature and + * current channels. Channel macros are mapped such that their + * ID matches their respective hardware bit position in CON22. + */ +static const struct iio_chan_spec mt6323_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(baton2, MT6323_AUXADC_BATON2, MT6323_AUXADC_ADC6), + MTK_PMIC_IIO_CHAN(ch6, MT6323_AUXADC_CH6, MT6323_AUXADC_ADC11), + MTK_PMIC_IIO_CHAN(bat_temp, MT6323_AUXADC_BAT_TEMP, MT6323_AUXADC_ADC5), + MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4), + MTK_PMIC_IIO_CHAN(vcdt, MT6323_AUXADC_VCDT, MT6323_AUXADC_ADC2), + MTK_PMIC_IIO_CHAN(baton1, MT6323_AUXADC_BATON1, MT6323_AUXADC_ADC3), + MTK_PMIC_IIO_CHAN(isense, MT6323_AUXADC_ISENSE, MT6323_AUXADC_ADC1), + MTK_PMIC_IIO_CHAN(batsns, MT6323_AUXADC_BATSNS, MT6323_AUXADC_ADC0), + MTK_PMIC_IIO_CHAN(accdet, MT6323_AUXADC_ACCDET, MT6323_AUXADC_ADC7), +}; + +/* + * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy: + * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM) + * + * Therefore, PWRAP regmap should be obtained using dev->parent->parent. + */ +struct mt6323_auxadc { + struct regmap *regmap; + /* AUXADC doesn't support reading multiple channels simultaneously. */ + struct mutex lock; +}; + +static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read(map, MT6323_AUXADC_CON19, &val); + if (ret) + return ret; + + /* The ADC is idle. */ + if (!(val & AUXADC_CON19_DECI_GDLY_MASK)) + return 0; + + ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19, + val, !(val & AUXADC_ADC19_BUSY_MASK), + 10, 500); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON19, + AUXADC_CON19_DECI_GDLY_MASK); +} + +static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); + if (ret) + return ret; + + return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel)); +} + +static int mt6323_auxadc_release(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_clear_bits(map, MT6323_AUXADC_CON22, BIT(channel)); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); +} + +static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc, + const struct iio_chan_spec *chan, int *out) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read_poll_timeout(map, chan->address, + val, (val & AUXADC_READY_MASK), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + if (ret) + return ret; + + *out = FIELD_GET(AUXADC_DATA_MASK, val); + + return 0; +} + +static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6323_auxadc *auxadc = iio_priv(indio_dev); + int ret, mult; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->channel == MT6323_AUXADC_ISENSE || + chan->channel == MT6323_AUXADC_BATSNS) + mult = 4; + else + mult = 1; + + /* 1800mV full range with 15-bit resolution. */ + *val = mult * 1800; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&auxadc->lock); + + ret = mt6323_auxadc_prepare_channel(auxadc); + if (ret) + return ret; + + ret = mt6323_auxadc_request(auxadc, chan->channel); + if (ret) + return ret; + + /* Hardware limitation: the AUXADC needs a delay to become ready. */ + fsleep(300); + + ret = mt6323_auxadc_read(auxadc, chan, val); + + if (mt6323_auxadc_release(auxadc, chan->channel)) + dev_err(&indio_dev->dev, + "failed to release channel %d\n", chan->channel); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int mt6323_auxadc_init(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_STRUP_CON10, + AUXADC_STRUP_CON10_RSTB_SW | + AUXADC_STRUP_CON10_RSTB_SEL); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_TOP_CKPDN2, AUXADC_TOP_CKPDN2_CTL_CK); + if (ret) + return ret; + + ret = regmap_update_bits(map, MT6323_AUXADC_CON10, + AUXADC_TRIM_CH2_MASK | AUXADC_TRIM_CH4_MASK | + AUXADC_TRIM_CH5_MASK | AUXADC_TRIM_CH6_MASK, + FIELD_PREP(AUXADC_TRIM_CH2_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH4_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH5_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH6_MASK, 1)); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON27, + AUXADC_CON27_VREF18_ENB_MD | + AUXADC_CON27_MD_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON19, AUXADC_CON19_GPS_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON26, + AUXADC_CON26_VREF18_SELB | + AUXADC_CON26_DECI_GDLY_SEL); + if (ret) + return ret; + + return regmap_update_bits(map, MT6323_AUXADC_CON9, AUXADC_CON9_OSR_MASK, + FIELD_PREP(AUXADC_CON9_OSR_MASK, AUXADC_DEFAULT_OSR)); +} + +static const struct iio_info mt6323_auxadc_iio_info = { + .read_raw = mt6323_auxadc_read_raw, +}; + +static int mt6323_auxadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mt6323_auxadc *auxadc; + struct regmap *regmap; + struct iio_dev *iio; + int ret; + + regmap = dev_get_regmap(dev->parent->parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "failed to get regmap\n"); + + iio = devm_iio_device_alloc(dev, sizeof(*auxadc)); + if (!iio) + return -ENOMEM; + + auxadc = iio_priv(iio); + auxadc->regmap = regmap; + + ret = devm_mutex_init(dev, &auxadc->lock); + if (ret) + return ret; + + ret = mt6323_auxadc_init(auxadc); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize auxadc\n"); + + iio->name = "mt6323-auxadc"; + iio->info = &mt6323_auxadc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + iio->channels = mt6323_auxadc_channels; + iio->num_channels = ARRAY_SIZE(mt6323_auxadc_channels); + + return devm_iio_device_register(dev, iio); +} + +static const struct of_device_id mt6323_auxadc_of_match[] = { + { .compatible = "mediatek,mt6323-auxadc" }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6323_auxadc_of_match); + +static struct platform_driver mt6323_auxadc_driver = { + .driver = { + .name = "mt6323-auxadc", + .of_match_table = mt6323_auxadc_of_match, + }, + .probe = mt6323_auxadc_probe, +}; +module_platform_driver(mt6323_auxadc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek MT6323 PMIC AUXADC Driver"); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index e33a2f4bf66c..2df8f1c21c2a 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -189,10 +189,8 @@ static int adc081c_probe(struct i2c_client *client) err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL, adc081c_trigger_handler, NULL); - if (err < 0) { - dev_err(&client->dev, "iio triggered buffer setup failed\n"); - return err; - } + if (err < 0) + return dev_err_probe(&client->dev, err, "iio triggered buffer setup failed\n"); return devm_iio_device_register(&client->dev, iio); } diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index b08a5de9b6b2..cf2f6ee59411 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -228,10 +228,8 @@ static int adc084s021_probe(struct spi_device *spi) ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, adc084s021_buffer_trigger_handler, &adc084s021_buffer_setup_ops); - if (ret) { - dev_err(&spi->dev, "Failed to setup triggered buffer\n"); - return ret; - } + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n"); return devm_iio_device_register(&spi->dev, indio_dev); } diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 8a272af69f7d..0fbfa4e499aa 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -930,6 +930,17 @@ static int ads1015_set_conv_mode(struct ads1015_data *data, int mode) mode << ADS1015_CFG_MOD_SHIFT); } +static void ads1015_power_down(void *p) +{ + struct ads1015_data *data = p; + int ret; + + ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); + if (ret) + dev_warn(regmap_get_device(data->regmap), + "Failed to power down (%pe)\n", ERR_PTR(ret)); +} + static int ads1015_probe(struct i2c_client *client) { const struct ads1015_chip_data *chip; @@ -1030,6 +1041,10 @@ static int ads1015_probe(struct i2c_client *client) if (ret) return ret; + ret = devm_add_action_or_reset(&client->dev, ads1015_power_down, data); + if (ret) + return ret; + data->conv_invalid = true; ret = pm_runtime_set_active(&client->dev); @@ -1037,33 +1052,11 @@ static int ads1015_probe(struct i2c_client *client) return ret; pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - pm_runtime_enable(&client->dev); - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "Failed to register IIO device\n"); + ret = devm_pm_runtime_enable(&client->dev); + if (ret) return ret; - } - - return 0; -} -static void ads1015_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ads1015_data *data = iio_priv(indio_dev); - int ret; - - iio_device_unregister(indio_dev); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - /* power down single shot mode */ - ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); - if (ret) - dev_warn(&client->dev, "Failed to power down (%pe)\n", - ERR_PTR(ret)); + return devm_iio_device_register(&client->dev, indio_dev); } #ifdef CONFIG_PM @@ -1150,7 +1143,6 @@ static struct i2c_driver ads1015_driver = { .pm = &ads1015_pm_ops, }, .probe = ads1015_probe, - .remove = ads1015_remove, .id_table = ads1015_id, }; diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c new file mode 100644 index 000000000000..1b55d343982e --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-core.c @@ -0,0 +1,1052 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon core driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/limits.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/units.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> + +#include "versal-sysmon.h" + +/* + * Oversampling ratio values exposed to userspace via IIO. + * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x. + */ +static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 }; + +/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */ +#define SYSMON_TEMP_HYST_MASK BIT(1) + +/* Compute alarm register offset from a channel address */ +#define SYSMON_ALARM_OFFSET(addr) \ + (SYSMON_ALARM_REG + ((addr) / SYSMON_ALARM_BITS_PER_REG) * SYSMON_REG_STRIDE) + +#define SYSMON_CHAN_TEMP(_chan, _address, _name) \ +{ \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .address = _address, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = _name, \ +} + +enum sysmon_alarm_bit { + SYSMON_BIT_ALARM0 = 0, + SYSMON_BIT_ALARM1 = 1, + SYSMON_BIT_ALARM2 = 2, + SYSMON_BIT_ALARM3 = 3, + SYSMON_BIT_ALARM4 = 4, + SYSMON_BIT_TEMP = 9, +}; + +/* Temperature event specification: rising threshold + hysteresis only */ +static const struct iio_event_spec sysmon_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +/* Supply event specifications */ +static const struct iio_event_spec sysmon_supply_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +/* + * Static temperature channels (always present). + * + * These are hardware-computed aggregate registers across all active + * temperature satellites: + * temp: current max temperature across all active satellites + * min: current min temperature across all active satellites + * max_max: highest peak recorded since last hardware reset + * min_min: lowest trough recorded since last hardware reset + */ +static const struct iio_chan_spec temp_channels[] = { + SYSMON_CHAN_TEMP(0, SYSMON_TEMP_MAX, "temp"), + SYSMON_CHAN_TEMP(1, SYSMON_TEMP_MIN, "min"), + SYSMON_CHAN_TEMP(2, SYSMON_TEMP_MAX_MAX, "max_max"), + SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"), +}; + +static void sysmon_q8p7_to_millicelsius(s16 raw_data, int *val) +{ + *val = (raw_data * MILLIDEGREE_PER_DEGREE) >> SYSMON_FRACTIONAL_SHIFT; +} + +static void sysmon_millicelsius_to_q8p7(u32 *raw_data, int val) +{ + *raw_data = (val << SYSMON_FRACTIONAL_SHIFT) / MILLIDEGREE_PER_DEGREE; +} + +static void sysmon_supply_rawtoprocessed(int raw_data, int *val) +{ + int mantissa, format, exponent; + + mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data); + exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data); + format = FIELD_GET(SYSMON_FMT_MASK, raw_data); + /* + * When format bit is set the mantissa is two's complement + * (per hardware spec); sign-extend to int for correct arithmetic. + */ + if (format) + mantissa = sign_extend32(mantissa, 15); + + *val = (mantissa * (int)MILLI) >> exponent; +} + +static void sysmon_supply_processedtoraw(int val, u32 reg_val, u32 *raw_data) +{ + int exponent = FIELD_GET(SYSMON_MODE_MASK, reg_val); + int format = FIELD_GET(SYSMON_FMT_MASK, reg_val); + int scale, tmp; + + scale = BIT(SYSMON_SUPPLY_MANTISSA_BITS - exponent); + tmp = (val * scale) / (int)MILLI; + + if (format) + tmp = clamp(tmp, S16_MIN, S16_MAX); + else + tmp = clamp(tmp, 0, U16_MAX); + + *raw_data = (u16)tmp; +} + +static int sysmon_supply_thresh_offset(unsigned long address, enum iio_event_direction dir) +{ + if (dir == IIO_EV_DIR_RISING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_UP; + if (dir == IIO_EV_DIR_FALLING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_LOW; + + return -EINVAL; +} + +static int sysmon_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int regval; + int ret; + + guard(mutex)(&sysmon->lock); + + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) { + *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling : + sysmon->supply_oversampling; + return IIO_VAL_INT; + } + + switch (chan->type) { + case IIO_TEMP: + if (mask == IIO_CHAN_INFO_SCALE) { + /* Q8.7 to millicelsius: raw * 1000 / 128 */ + *val = MILLIDEGREE_PER_DEGREE; + *val2 = BIT(SYSMON_FRACTIONAL_SHIFT); + return IIO_VAL_FRACTIONAL; + } + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, chan->address, ®val); + if (ret) + return ret; + + *val = sign_extend32(regval, 15); + return IIO_VAL_INT; + + case IIO_VOLTAGE: + if (mask != IIO_CHAN_INFO_PROCESSED) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, + chan->address * SYSMON_REG_STRIDE + + SYSMON_SUPPLY_BASE, ®val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(regval, val); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static u32 sysmon_get_event_mask(const struct iio_chan_spec *chan) +{ + if (chan->type == IIO_TEMP) + return BIT(SYSMON_BIT_TEMP); + + return BIT(chan->address / SYSMON_ALARM_BITS_PER_REG); +} + +static int sysmon_read_alarm_config(struct sysmon *sysmon, + unsigned long address) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_test_bits(sysmon->regmap, offset, BIT(shift)); +} + +static int sysmon_write_alarm_config(struct sysmon *sysmon, + unsigned long address, bool enable) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_assign_bits(sysmon->regmap, offset, BIT(shift), enable); +} + +static int sysmon_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int imr; + int config_value; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + + /* IMR bits are 1=masked, invert to get 1=enabled */ + imr = ~imr; + + switch (chan->type) { + case IIO_VOLTAGE: + config_value = sysmon_read_alarm_config(sysmon, chan->address); + if (config_value < 0) + return config_value; + return config_value && (imr & mask); + + case IIO_TEMP: + /* + * Return the administrative state, not the hardware IMR. + * The IRQ handler temporarily masks the interrupt during + * the polling window; reading IMR would show it as disabled. + * temp_mask bit is set when administratively disabled. + */ + return !(sysmon->temp_mask & mask); + + default: + return -EINVAL; + } +} + +static int sysmon_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + u32 offset = SYSMON_ALARM_OFFSET(chan->address); + struct sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int alarm_config; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_VOLTAGE: + ret = sysmon_write_alarm_config(sysmon, chan->address, state); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, offset, &alarm_config); + if (ret) + return ret; + + if (alarm_config) + return regmap_write(sysmon->regmap, SYSMON_IER, mask); + + return regmap_write(sysmon->regmap, SYSMON_IDR, mask); + + case IIO_TEMP: + if (state) { + ret = regmap_write(sysmon->regmap, SYSMON_IER, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask &= ~mask; + } else { + ret = regmap_write(sysmon->regmap, SYSMON_IDR, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask |= mask; + } + return 0; + + default: + return -EINVAL; + } +} + +/* + * Recompute the lower threshold register from upper threshold and + * cached hysteresis. Called when either upper threshold or hysteresis + * is written. + */ +static int sysmon_update_temp_lower(struct sysmon *sysmon) +{ + unsigned int upper_reg; + int upper_mc, lower_mc; + u32 raw_val; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + lower_mc = clamp(upper_mc - sysmon->temp_hysteresis, -256000, 255992); + sysmon_millicelsius_to_q8p7(&raw_val, lower_mc); + + return regmap_write(sysmon->regmap, SYSMON_TEMP_TH_LOW, raw_val); +} + +static int sysmon_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, ®_val); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(reg_val, val); + + return IIO_VAL_INT; + + case IIO_EV_INFO_HYSTERESIS: + *val = sysmon->temp_hysteresis; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(reg_val, val); + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int sysmon_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + u32 raw_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + /* Q8.7 signed range: -256000 to +255992 mC */ + if (val < -256000 || val > 255992) + return -EINVAL; + + sysmon_millicelsius_to_q8p7(&raw_val, val); + + ret = regmap_write(sysmon->regmap, SYSMON_TEMP_TH_UP, raw_val); + if (ret) + return ret; + + /* Recompute lower = upper - hysteresis */ + return sysmon_update_temp_lower(sysmon); + + case IIO_EV_INFO_HYSTERESIS: + if (val < 0) + return -EINVAL; + + sysmon->temp_hysteresis = val; + + return sysmon_update_temp_lower(sysmon); + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + /* Clamp to prevent overflow in processedtoraw conversion */ + if (val < -32768 || val > 32767) + return -EINVAL; + + sysmon_supply_processedtoraw(val, reg_val, &raw_val); + + /* + * The hardware threshold register returns FMT and MODE + * bits in the upper 16 bits on read, but only the lower + * 16-bit mantissa is used on write. + */ + return regmap_write(sysmon->regmap, offset, raw_val); + + default: + return -EINVAL; + } +} + +static int sysmon_set_avg_enable(struct sysmon *sysmon, + u32 base, u32 count, u32 val) +{ + struct regmap *map = sysmon->regmap; + int ret; + + for (unsigned int i = 0; i < count; i++) { + ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val); + if (ret) + return ret; + } + + return 0; +} + +static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val) +{ + /* + * HW register encoding is sample_count / 2: + * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based). + */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_TEMP_SAT_OSR, + FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val)); + if (ret) + return ret; + + /* + * Readback fence: the SysMon CONFIG register resides in the + * PMC domain behind the NoC. A posted write may not reach the + * hardware before the next MMIO access. Reading the register + * back forces the interconnect to complete the write, preventing + * a bus hang on the subsequent access. + */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE, + SYSMON_TEMP_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val) +{ + /* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_SUPPLY_OSR, + FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val)); + if (ret) + return ret; + + /* Readback fence -- see sysmon_osr_write_temp for details */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE, + SYSMON_SUPPLY_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int i; + int ret; + + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) { + if (val == sysmon_oversampling_avail[i]) + break; + } + if (i == ARRAY_SIZE(sysmon_oversampling_avail)) + return -EINVAL; + + guard(mutex)(&sysmon->lock); + + if (chan->type == IIO_TEMP) { + ret = sysmon_osr_write_temp(sysmon, val); + if (ret) + return ret; + sysmon->temp_oversampling = val; + } else { + ret = sysmon_osr_write_supply(sysmon, val); + if (ret) + return ret; + sysmon->supply_oversampling = val; + } + + return 0; +} + +static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return IIO_VAL_INT; + + return -EINVAL; +} + +static int sysmon_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + *vals = sysmon_oversampling_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(sysmon_oversampling_avail); + + return IIO_AVAIL_LIST; +} + +static int sysmon_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->datasheet_name) + return sysfs_emit(label, "%s\n", chan->datasheet_name); + + return -EINVAL; +} + +static const struct iio_info sysmon_iio_info = { + .read_raw = sysmon_read_raw, + .write_raw = sysmon_write_raw, + .write_raw_get_fmt = sysmon_write_raw_get_fmt, + .read_avail = sysmon_read_avail, + .read_label = sysmon_read_label, + .read_event_config = sysmon_read_event_config, + .write_event_config = sysmon_write_event_config, + .read_event_value = sysmon_read_event_value, + .write_event_value = sysmon_write_event_value, +}; + +static void sysmon_push_event(struct iio_dev *indio_dev, u32 address) +{ + const struct iio_chan_spec *chan; + enum iio_event_direction dir; + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].address != address) + continue; + + chan = &indio_dev->channels[i]; + /* Temp uses hysteresis mode (rising only), voltage uses window */ + dir = (chan->type == IIO_TEMP) ? IIO_EV_DIR_RISING : + IIO_EV_DIR_EITHER; + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, + chan->channel, + IIO_EV_TYPE_THRESH, + dir), + iio_get_time_ns(indio_dev)); + } +} + +static int sysmon_handle_event(struct iio_dev *indio_dev, u32 event) +{ + u32 alarm_flag_offset = SYSMON_ALARM_FLAG + event * SYSMON_REG_STRIDE; + u32 alarm_reg_offset = SYSMON_ALARM_REG + event * SYSMON_REG_STRIDE; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned long alarm_flag_reg; + unsigned int reg_val; + u32 address, bit; + int ret; + + switch (event) { + case SYSMON_BIT_TEMP: + sysmon_push_event(indio_dev, SYSMON_TEMP_MAX); + + ret = regmap_write(sysmon->regmap, SYSMON_IDR, BIT(SYSMON_BIT_TEMP)); + if (ret) + return ret; + + sysmon->masked_temp |= BIT(SYSMON_BIT_TEMP); + return 0; + + case SYSMON_BIT_ALARM0: + case SYSMON_BIT_ALARM1: + case SYSMON_BIT_ALARM2: + case SYSMON_BIT_ALARM3: + case SYSMON_BIT_ALARM4: + ret = regmap_read(sysmon->regmap, alarm_flag_offset, ®_val); + if (ret) + return ret; + + alarm_flag_reg = reg_val; + + for_each_set_bit(bit, &alarm_flag_reg, SYSMON_ALARM_BITS_PER_REG) { + address = bit + SYSMON_ALARM_BITS_PER_REG * event; + sysmon_push_event(indio_dev, address); + ret = regmap_clear_bits(sysmon->regmap, alarm_reg_offset, BIT(bit)); + if (ret) + return ret; + } + + return regmap_write(sysmon->regmap, alarm_flag_offset, alarm_flag_reg); + + default: + return -EINVAL; + } +} + +static void sysmon_handle_events(struct iio_dev *indio_dev, + unsigned long events) +{ + unsigned int bit; + + for_each_set_bit(bit, &events, SYSMON_NO_OF_EVENTS) + sysmon_handle_event(indio_dev, bit); +} + +static void sysmon_unmask_temp(struct sysmon *sysmon, unsigned int isr) +{ + unsigned int status; + u32 ier; + + status = isr & SYSMON_TEMP_INTR_MASK; + + ier = ~status & sysmon->masked_temp; + sysmon->masked_temp &= status; + + /* Only unmask if not administratively disabled by userspace */ + ier &= ~sysmon->temp_mask; + + regmap_write(sysmon->regmap, SYSMON_IER, ier); +} + +/* + * Versal threshold interrupts are level-sensitive. Active threshold + * interrupts are masked in the handler and polled via delayed work + * until the condition clears, then unmasked. + */ +static void sysmon_unmask_worker(struct work_struct *work) +{ + struct sysmon *sysmon = + container_of(work, struct sysmon, sysmon_unmask_work.work); + unsigned int isr; + + /* + * If the ISR read fails, skip processing to avoid acting + * on undefined data. + */ + scoped_guard(spinlock_irq, &sysmon->irq_lock) { + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr)) + break; + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + sysmon_unmask_temp(sysmon, isr); + } + + if (sysmon->masked_temp) + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + else + regmap_write(sysmon->regmap, SYSMON_STATUS_RESET, 1); +} + +static irqreturn_t sysmon_iio_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int isr, imr; + + guard(spinlock)(&sysmon->irq_lock); + + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr) || + regmap_read(sysmon->regmap, SYSMON_IMR, &imr)) + return IRQ_NONE; + + isr &= ~imr; + if (!isr) + return IRQ_NONE; + + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + + sysmon_handle_events(indio_dev, isr); + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + + return IRQ_HANDLED; +} + +static void sysmon_disable_interrupts(void *data) +{ + struct sysmon *sysmon = data; + + regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->masked_temp = 0; + + cancel_delayed_work_sync(&sysmon->sysmon_unmask_work); +} + +static int sysmon_init_interrupt(struct sysmon *sysmon, + struct device *dev, + struct iio_dev *indio_dev, + int irq) +{ + unsigned int imr; + int ret; + + /* Events not supported without IRQ (e.g. I2C path) */ + if (!irq) + return 0; + + INIT_DELAYED_WORK(&sysmon->sysmon_unmask_work, sysmon_unmask_worker); + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + sysmon->temp_mask = imr & SYSMON_TEMP_INTR_MASK; + + ret = devm_request_irq(dev, irq, sysmon_iio_irq, 0, "sysmon-irq", indio_dev); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, sysmon_disable_interrupts, sysmon); +} + +/* + * Initialize the cached hysteresis for a temperature channel from the + * current hardware threshold registers: hysteresis = upper - lower. + */ +static int sysmon_init_hysteresis(struct sysmon *sysmon, int *hysteresis) +{ + unsigned int upper_reg, lower_reg; + int upper_mc, lower_mc; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_LOW, &lower_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + sysmon_q8p7_to_millicelsius(lower_reg, &lower_mc); + *hysteresis = upper_mc - lower_mc; + + return 0; +} + +/** + * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels. + * @indio_dev: IIO device instance + * @dev: Parent device + * @irq: IRQ number (positive enables event channels, 0 disables) + * + * Reads voltage-channels and temperature-channels container nodes from + * firmware and builds the IIO channel array. Static temperature channels + * and event channels are prepended, followed by supply and satellite + * channels from DT. + * + * Event channels and per-channel event specs are only added when the + * device has an IRQ. I2C devices have no interrupt line, and the I2C + * regmap cannot be called from atomic context, so events are not + * supported on that path. + * + * Return: 0 on success, negative errno on failure. + */ +static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq) +{ + unsigned int num_chan, num_static, num_supply, num_temp; + unsigned int idx, temp_chan_idx, volt_chan_idx; + struct iio_chan_spec *sysmon_channels; + const char *label; + u32 reg; + int ret; + + struct fwnode_handle *supply_node __free(fwnode_handle) = + device_get_named_child_node(dev, "voltage-channels"); + num_supply = fwnode_get_child_node_count(supply_node); + + struct fwnode_handle *temp_node __free(fwnode_handle) = + device_get_named_child_node(dev, "temperature-channels"); + num_temp = fwnode_get_child_node_count(temp_node); + + num_static = ARRAY_SIZE(temp_channels); + num_chan = size_add(num_temp, size_add(num_static, num_supply)); + sysmon_channels = devm_kcalloc(dev, num_chan, sizeof(*sysmon_channels), GFP_KERNEL); + if (!sysmon_channels) + return -ENOMEM; + + memcpy(sysmon_channels, temp_channels, sizeof(temp_channels)); + + /* Attach event spec to channel 0 when IRQ is available */ + if (irq > 0) { + sysmon_channels[0].event_spec = sysmon_temp_events; + sysmon_channels[0].num_event_specs = ARRAY_SIZE(sysmon_temp_events); + } + + idx = num_static; + + /* Supply channels from DT */ + fwnode_for_each_child_node_scoped(supply_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for supply channel\n"); + + if (reg > SYSMON_SUPPLY_IDX_MAX) + return dev_err_probe(dev, -EINVAL, + "supply reg %u exceeds max %u\n", + reg, SYSMON_SUPPLY_IDX_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for supply channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_VOLTAGE, + .indexed = 1, + .address = reg, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .event_spec = irq > 0 ? + sysmon_supply_events : NULL, + .num_event_specs = irq > 0 ? + ARRAY_SIZE(sysmon_supply_events) : 0, + .datasheet_name = label, + }; + } + + /* Temperature satellite channels from DT */ + fwnode_for_each_child_node_scoped(temp_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for temp channel\n"); + + if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX) + return dev_err_probe(dev, -EINVAL, + "temp reg %u out of range [1..%u]\n", + reg, SYSMON_TEMP_SAT_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for temp channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_TEMP, + .indexed = 1, + .address = SYSMON_TEMP_SAT_BASE + + (reg - 1) * SYSMON_REG_STRIDE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .datasheet_name = label, + }; + } + + indio_dev->num_channels = idx; + indio_dev->info = &sysmon_iio_info; + + /* + * Assign per-type sequential channel numbers. + * IIO sysfs uses type prefix (in_tempN, in_voltageN) + * so numbers only need to be unique within each type. + */ + temp_chan_idx = 0; + volt_chan_idx = 0; + for (unsigned int idx = 0; idx < indio_dev->num_channels; idx++) { + if (sysmon_channels[idx].type == IIO_TEMP) + sysmon_channels[idx].channel = temp_chan_idx++; + else + sysmon_channels[idx].channel = volt_chan_idx++; + } + + indio_dev->channels = sysmon_channels; + + return 0; +} + +/** + * devm_versal_sysmon_core_probe() - Initialize Versal SysMon core + * @dev: Parent device + * @regmap: Register map for hardware access + * + * Return: 0 on success, negative errno on failure. + */ +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) +{ + struct iio_dev *indio_dev; + struct sysmon *sysmon; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon)); + if (!indio_dev) + return -ENOMEM; + + sysmon = iio_priv(indio_dev); + sysmon->regmap = regmap; + sysmon->temp_oversampling = 1; + sysmon->supply_oversampling = 1; + + ret = devm_mutex_init(dev, &sysmon->lock); + if (ret) + return ret; + spin_lock_init(&sysmon->irq_lock); + + /* Disable all interrupts and clear pending status */ + ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + ret = regmap_write(sysmon->regmap, SYSMON_ISR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + + irq = fwnode_irq_get(dev_fwnode(dev), 0); + if (irq == -EPROBE_DEFER) + return dev_err_probe(dev, irq, "failed to get IRQ\n"); + + indio_dev->name = "versal-sysmon"; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = sysmon_parse_fw(indio_dev, dev, irq); + if (ret) + return ret; + + if (irq > 0) { + /* Set hysteresis mode for temperature threshold */ + ret = regmap_set_bits(sysmon->regmap, SYSMON_TEMP_EV_CFG, + SYSMON_TEMP_HYST_MASK); + if (ret) + return ret; + + /* Initialize cached hysteresis from hardware registers */ + ret = sysmon_init_hysteresis(sysmon, &sysmon->temp_hysteresis); + if (ret) + return ret; + + ret = sysmon_init_interrupt(sysmon, dev, indio_dev, irq); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Core Driver"); +MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>"); diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c new file mode 100644 index 000000000000..e9a7629159ab --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-i2c.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon I2C driver + * + * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc. + */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/types.h> +#include <linux/unaligned.h> + +#include "versal-sysmon.h" + +#define SYSMON_I2C_INSTR_READ BIT(2) +#define SYSMON_I2C_INSTR_WRITE BIT(3) + +/* + * I2C command frame layout (8 bytes): + * [0..3] data payload (little-endian u32) + * [4..5] register offset >> 2 (little-endian u16) + * [6] instruction (read/write) + * [7] reserved + */ +#define SYSMON_I2C_DATA_OFS 0 +#define SYSMON_I2C_REG_OFS 4 +#define SYSMON_I2C_INSTR_OFS 6 + +static int sysmon_i2c_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + u8 read_buf[4]; + int ret; + + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + ret = i2c_master_recv(client, read_buf, sizeof(read_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(read_buf)) + return -EIO; + + *val = get_unaligned_le32(read_buf); + + return 0; +} + +static int sysmon_i2c_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + int ret; + + put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]); + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + return 0; +} + +/* + * Almost all registers are volatile (live ADC readings, interrupt + * status). The rest are not accessed often enough to benefit from + * caching. + */ +static const struct regmap_config sysmon_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_i2c_reg_read, + .reg_write = sysmon_i2c_reg_write, +}; + +static int sysmon_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + + regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* I2C has no IRQ connection; events are not supported */ + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_i2c_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table); + +static const struct i2c_device_id sysmon_i2c_id_table[] = { + { .name = "versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table); + +static struct i2c_driver sysmon_i2c_driver = { + .probe = sysmon_i2c_probe, + .driver = { + .name = "versal-sysmon-i2c", + .of_match_table = sysmon_i2c_of_match_table, + }, + .id_table = sysmon_i2c_id_table, +}; +module_i2c_driver(sysmon_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Conall O'Griofa <conall.ogriofa@amd.com>"); +MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>"); diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c new file mode 100644 index 000000000000..529d0486c9f9 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon MMIO platform driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include "versal-sysmon.h" + +struct sysmon_mmio { + void __iomem *base; +}; + +static int sysmon_mmio_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct sysmon_mmio *mmio = context; + + *val = readl(mmio->base + reg); + return 0; +} + +static int sysmon_mmio_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct sysmon_mmio *mmio = context; + + /* NPI must be unlocked before any register write except to NPI_LOCK */ + if (reg != SYSMON_NPI_LOCK) + writel(SYSMON_NPI_UNLOCK_CODE, mmio->base + SYSMON_NPI_LOCK); + writel(val, mmio->base + reg); + + return 0; +} + +static const struct regmap_config sysmon_mmio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_mmio_reg_read, + .reg_write = sysmon_mmio_reg_write, + .fast_io = true, +}; + +static int sysmon_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sysmon_mmio *mmio; + struct regmap *regmap; + + mmio = devm_kzalloc(dev, sizeof(*mmio), GFP_KERNEL); + if (!mmio) + return -ENOMEM; + + mmio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmio->base)) + return PTR_ERR(mmio->base); + + regmap = devm_regmap_init(dev, NULL, mmio, &sysmon_mmio_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_of_match_table); + +static struct platform_driver sysmon_platform_driver = { + .probe = sysmon_platform_probe, + .driver = { + .name = "versal-sysmon", + .of_match_table = sysmon_of_match_table, + }, +}; +module_platform_driver(sysmon_platform_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Platform Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>"); diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h new file mode 100644 index 000000000000..bb9a75bf71c0 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AMD Versal SysMon driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#ifndef _VERSAL_SYSMON_H_ +#define _VERSAL_SYSMON_H_ + +#include <linux/bits.h> +#include <linux/mutex.h> +#include <linux/spinlock_types.h> +#include <linux/workqueue.h> + +struct device; +struct regmap; + +/* Register offsets (sorted by address) */ +#define SYSMON_NPI_LOCK 0x000C +#define SYSMON_ISR 0x0044 +#define SYSMON_IMR 0x0048 +#define SYSMON_IER 0x004C +#define SYSMON_IDR 0x0050 +#define SYSMON_CONFIG 0x0100 +#define SYSMON_TEMP_MAX 0x1030 +#define SYSMON_TEMP_MIN 0x1034 +#define SYSMON_SUPPLY_BASE 0x1040 +#define SYSMON_ALARM_FLAG 0x1018 +#define SYSMON_ALARM_REG 0x1940 +#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958 +#define SYSMON_TEMP_TH_LOW 0x1970 +#define SYSMON_TEMP_TH_UP 0x1974 +#define SYSMON_SUPPLY_TH_LOW 0x1980 +#define SYSMON_SUPPLY_TH_UP 0x1C80 +#define SYSMON_TEMP_EV_CFG 0x1F84 +#define SYSMON_TEMP_MIN_MIN 0x1F8C +#define SYSMON_TEMP_MAX_MAX 0x1F90 +#define SYSMON_STATUS_RESET 0x1F94 +#define SYSMON_TEMP_SAT_BASE 0x1FAC +#define SYSMON_TEMP_EN_AVG_BASE 0x24B4 +#define SYSMON_MAX_REG 0x24C0 + +/* NPI unlock value written to SYSMON_NPI_LOCK */ +#define SYSMON_NPI_UNLOCK_CODE 0xF9E8D7C6 + +/* Register stride: 4 bytes per 32-bit register */ +#define SYSMON_REG_STRIDE 4 + +#define SYSMON_SUPPLY_IDX_MAX 159 +#define SYSMON_TEMP_SAT_MAX 64 +#define SYSMON_NO_OF_EVENTS 32 +#define SYSMON_INTR_ALL_MASK GENMASK(31, 0) + +/* ISR/IMR temperature alarm mask (bit 9) */ +#define SYSMON_TEMP_INTR_MASK BIT(9) + +/* SYSMON_CONFIG: supply oversampling ratio */ +#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14) + +/* SYSMON_CONFIG: temperature satellite oversampling ratio */ +#define SYSMON_CONFIG_TEMP_SAT_OSR GENMASK(27, 24) + +/* Per-channel averaging enable register counts */ +#define SYSMON_SUPPLY_EN_AVG_COUNT 5 +#define SYSMON_TEMP_EN_AVG_COUNT 2 + +/* Supply voltage conversion register fields */ +#define SYSMON_MANTISSA_MASK GENMASK(15, 0) +#define SYSMON_FMT_MASK BIT(16) +#define SYSMON_MODE_MASK GENMASK(18, 17) + +/* Q8.7 fractional shift */ +#define SYSMON_FRACTIONAL_SHIFT 7U +#define SYSMON_SUPPLY_MANTISSA_BITS 16 + +/* Bits per alarm register */ +#define SYSMON_ALARM_BITS_PER_REG 32 + +#define SYSMON_UNMASK_WORK_DELAY_MS 500 + +/** + * struct sysmon - Driver data for Versal SysMon + * @regmap: register map for hardware access + * @lock: protects read-modify-write sequences on threshold registers + * and cached state that spans multiple regmap calls + * @irq_lock: protects interrupt mask register updates (MMIO path only) + * @masked_temp: currently masked temperature alarm bits + * @temp_mask: temperature interrupt configuration mask + * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius + * @sysmon_unmask_work: re-enables events after alarm condition clears + * @temp_oversampling: current temp oversampling ratio + * @supply_oversampling: current supply oversampling ratio + */ +struct sysmon { + struct regmap *regmap; + /* + * Protects read-modify-write sequences on threshold registers + * and cached state (oversampling ratios, hysteresis values) + * that spans multiple regmap calls. + */ + struct mutex lock; + /* + * Protects interrupt mask register updates. Only used on the + * MMIO path (fast_io regmap); I2C has no IRQ and never reaches + * the event code that takes this lock. + */ + spinlock_t irq_lock; + unsigned int masked_temp; + unsigned int temp_mask; + int temp_hysteresis; + struct delayed_work sysmon_unmask_work; + unsigned int temp_oversampling; + unsigned int supply_oversampling; +}; + +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap); + +#endif /* _VERSAL_SYSMON_H_ */ diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index 654a3a50eb4f..3a6a09ffa526 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -608,3 +608,4 @@ module_platform_driver(rescale_driver); MODULE_DESCRIPTION("IIO rescale driver"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 0c266c216525..b58263bcf3cb 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -154,3 +154,4 @@ EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev); MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("Industrial I/O callback buffer"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 10e912bbf0c5..d035aa33b357 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -216,3 +216,4 @@ EXPORT_SYMBOL_GPL(iio_hw_consumer_disable); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Hardware consumer buffer the IIO framework"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 23895744c42d..f85cdd8bd84f 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor core driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> */ #include <linux/bitfield.h> @@ -774,6 +774,6 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, } EXPORT_SYMBOL_NS(scd30_probe, "IIO_SCD30"); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c index abceccdddc71..bcef1e473e34 100644 --- a/drivers/iio/chemical/scd30_i2c.c +++ b/drivers/iio/chemical/scd30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> * * I2C slave address: 0x61 */ @@ -136,7 +136,7 @@ static struct i2c_driver scd30_i2c_driver = { }; module_i2c_driver(scd30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index 7fefdadbfa0a..31b0fc957b08 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SCD30 carbon dioxide sensor serial driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> */ #include <linux/crc16.h> #include <linux/device.h> @@ -257,7 +257,7 @@ static struct serdev_device_driver scd30_serdev_driver = { }; module_serdev_device_driver(scd30_serdev_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SCD30"); diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c index 90f1adb8c89f..2d3a876cc06e 100644 --- a/drivers/iio/chemical/sps30_i2c.c +++ b/drivers/iio/chemical/sps30_i2c.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor i2c driver * - * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2020 Tomasz Duszynski <tduszyns@gmail.com> * * I2C slave address: 0x69 */ @@ -252,7 +252,7 @@ static struct i2c_driver sps30_i2c_driver = { }; module_i2c_driver(sps30_i2c_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c index 80eab9b2e4bf..0cdba8703e93 100644 --- a/drivers/iio/chemical/sps30_serial.c +++ b/drivers/iio/chemical/sps30_serial.c @@ -2,7 +2,7 @@ /* * Sensirion SPS30 particulate matter sensor serial driver * - * Copyright (c) 2021 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * Copyright (c) 2021 Tomasz Duszynski <tduszyns@gmail.com> */ #include <linux/completion.h> #include <linux/device.h> @@ -425,7 +425,7 @@ static struct serdev_device_driver sps30_serial_driver = { }; module_serdev_device_driver(sps30_serial_driver); -MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_SPS30"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index c115a72832b2..9e7c26e37880 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -72,8 +72,7 @@ static const struct { {HID_USAGE_SENSOR_HUMAN_ATTENTION, 0, 1, 0}, }; -static void simple_div(int dividend, int divisor, int *whole, - int *micro_frac) +static void simple_div(int dividend, int divisor, int *whole, int *micro_frac) { int rem; int exp = 0; @@ -111,7 +110,7 @@ for 10^-2. Negative numbers are 2's complement */ static void convert_from_vtf_format(u32 value, int size, int exp, - int *val1, int *val2) + int *val1, int *val2) { int sign = 1; @@ -176,7 +175,7 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, "IIO_HID_ATTRIBUTES"); int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; @@ -203,7 +202,7 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, "IIO_HID"); int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -238,15 +237,15 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, "IIO_HID"); int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, - int *val1, int *val2) + int *val1, int *val2) { s32 value; int ret; ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -268,8 +267,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -282,9 +281,8 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, } EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, "IIO_HID"); - int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, - int val1, int val2) + int val1, int val2) { s32 value; int ret; @@ -293,8 +291,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity.size, - st->sensitivity.unit_expo, - val1, val2); + st->sensitivity.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id, st->sensitivity.index, sizeof(value), &value); @@ -303,8 +301,8 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity.report_id, - st->sensitivity.index, sizeof(value), - &value); + st->sensitivity.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -324,8 +322,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, return -EINVAL; value = convert_to_vtf_format(st->sensitivity_rel.size, - st->sensitivity_rel.unit_expo, - val1, val2); + st->sensitivity_rel.unit_expo, + val1, val2); ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id, st->sensitivity_rel.index, sizeof(value), &value); @@ -334,8 +332,8 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, ret = sensor_hub_get_feature(st->hsdev, st->sensitivity_rel.report_id, - st->sensitivity_rel.index, sizeof(value), - &value); + st->sensitivity_rel.index, + sizeof(value), &value); if (ret < 0 || value < 0) return -EINVAL; @@ -356,8 +354,8 @@ EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, "IIO_HID"); * 1.001745329 ->exp:4-> val0[10017]val1[453290000] * 9.806650000 ->exp:-2-> val0[0]val1[98066500] */ -static void adjust_exponent_nano(int *val0, int *val1, int scale0, - int scale1, int exp) +static void adjust_exponent_nano(int *val0, int *val1, + int scale0, int scale1, int exp) { int divisor; int i; @@ -404,8 +402,8 @@ static void adjust_exponent_nano(int *val0, int *val1, int scale0, } int hid_sensor_format_scale(u32 usage_id, - struct hid_sensor_hub_attribute_info *attr_info, - int *val0, int *val1) + struct hid_sensor_hub_attribute_info *attr_info, + int *val0, int *val1) { int i; int exp; @@ -415,12 +413,11 @@ int hid_sensor_format_scale(u32 usage_id, for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) { if (unit_conversion[i].usage_id == usage_id && - unit_conversion[i].unit == attr_info->units) { - exp = hid_sensor_convert_exponent( - attr_info->unit_expo); + unit_conversion[i].unit == attr_info->units) { + exp = hid_sensor_convert_exponent(attr_info->unit_expo); adjust_exponent_nano(val0, val1, - unit_conversion[i].scale_val0, - unit_conversion[i].scale_val1, exp); + unit_conversion[i].scale_val0, + unit_conversion[i].scale_val1, exp); break; } } @@ -438,8 +435,8 @@ EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, "IIO_HID"); static int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st) + u32 usage_id, + struct hid_sensor_common *st) { sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -473,9 +470,10 @@ int hid_sensor_get_report_latency(struct hid_sensor_common *st) int ret; int value; - ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id, - st->report_latency.index, sizeof(value), - &value); + ret = sensor_hub_get_feature(st->hsdev, + st->report_latency.report_id, + st->report_latency.index, + sizeof(value), &value); if (ret < 0) return ret; @@ -498,10 +496,10 @@ bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st) EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, "IIO_HID_ATTRIBUTES"); int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct hid_sensor_common *st, - const u32 *sensitivity_addresses, - u32 sensitivity_addresses_len) + u32 usage_id, + struct hid_sensor_common *st, + const u32 *sensitivity_addresses, + u32 sensitivity_addresses_len) { struct hid_sensor_hub_attribute_info timestamp; @@ -527,7 +525,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, - &st->sensitivity); + &st->sensitivity); sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -578,8 +576,9 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, timestamp.index, timestamp.report_id); ret = sensor_hub_get_feature(hsdev, - st->power_state.report_id, - st->power_state.index, sizeof(value), &value); + st->power_state.report_id, + st->power_state.index, + sizeof(value), &value); if (ret < 0) return ret; if (value < 0) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 417c4ab8c1b2..60808e2430ca 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -233,7 +233,7 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, "IIO_HID"); int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb) + struct hid_sensor_common *attrb) { const struct iio_dev_attr **fifo_attrs; int ret; @@ -266,7 +266,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, trig = iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", name, iio_device_id(indio_dev)); - if (trig == NULL) { + if (!trig) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); return -ENOMEM; } @@ -314,7 +314,9 @@ static int __maybe_unused hid_sensor_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + schedule_work(&attrb->work); + return 0; } @@ -322,6 +324,7 @@ static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + return _hid_sensor_power_state(attrb, true); } diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index f94fca4f1edf..589de858e369 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -15,7 +15,7 @@ struct iio_dev; extern const struct dev_pm_ops hid_sensor_pm_ops; int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, - struct hid_sensor_common *attrb); + struct hid_sensor_common *attrb); void hid_sensor_remove_trigger(struct iio_dev *indio_dev, struct hid_sensor_common *attrb); int hid_sensor_power_state(struct hid_sensor_common *st, bool state); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 657c68e75542..17529509da9d 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -11,8 +11,11 @@ config AD3530R depends on SPI select REGMAP_SPI help - Say yes here to build support for Analog Devices AD3530R, AD3531R - Digital to Analog Converter. + Say yes here to build support for the following Analog Devices + Digital to Analog Converters: + - AD3530/AD3530R (8-channel) + - AD3531/AD3531R (4-channel) + - AD3532/AD3532R (16-channel) To compile this driver as a module, choose M here: the module will be called ad3530r. @@ -527,6 +530,17 @@ config MAX5821 Say yes here to build support for Maxim MAX5821 10 bits DAC. +config MCF54415_DAC + tristate "NXP MCF54415 DAC driver" + depends on M5441x || COMPILE_TEST + select REGMAP_MMIO + help + Say yes here if you want to build support for NXP ColdFire + MCF54415/6/7/8 12-bit DAC module. + + To compile this driver as a module, choose M here: the module + will be called mcf54415_dac. + config MCP4725 tristate "MCP4725/6 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 003431798498..5d20d37e44ce 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX22007) += max22007.o obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o +obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 6ff7ee208541..06b7f280f61b 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -2,6 +2,7 @@ /* * AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver * AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver + * AD3532R/AD3532 16-channel, 16-bit Voltage Output DAC Driver * * Copyright 2025 Analog Devices Inc. */ @@ -38,6 +39,25 @@ #define AD3531R_SW_LDAC_TRIG_A 0xDD #define AD3531R_INPUT_CH 0xE3 +/* AD3532R/AD3532 bank 0 registers (channels 0-7) */ +#define AD3532R_INTERFACE_CONFIG_A_0 0x1000 +#define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020 +#define AD3532R_OUTPUT_OPERATING_MODE_1 0x1021 +#define AD3532R_OUTPUT_CONTROL_0 0x102A +#define AD3532R_REFERENCE_CONTROL_0 0x103C +#define AD3532R_SW_LDAC_TRIG_0 0x10E5 +#define AD3532R_INPUT_CH_0 0x10EB + +/* AD3532R/AD3532 bank 1 registers (channels 8-15) */ +#define AD3532R_INTERFACE_CONFIG_A_1 0x3000 +#define AD3532R_OUTPUT_OPERATING_MODE_2 0x3020 +#define AD3532R_OUTPUT_OPERATING_MODE_3 0x3021 +#define AD3532R_OUTPUT_CONTROL_1 0x302A +#define AD3532R_REFERENCE_CONTROL_1 0x303C +#define AD3532R_SW_LDAC_TRIG_1 0x30E5 +#define AD3532R_INPUT_CH_1 0x30EB +#define AD3532R_MAX_REG_ADDR 0x30F9 + #define AD3530R_SLD_TRIG_A BIT(7) #define AD3530R_OUTPUT_CONTROL_RANGE BIT(2) #define AD3530R_REFERENCE_CONTROL_SEL BIT(0) @@ -49,8 +69,10 @@ #define AD3530R_LDAC_PULSE_US 100 #define AD3530R_DAC_MAX_VAL GENMASK(15, 0) -#define AD3530R_MAX_CHANNELS 8 +#define AD3530R_CH_PER_REG 4 +#define AD3530R_CH_PER_BANK 8 #define AD3531R_MAX_CHANNELS 4 +#define AD3532R_MAX_CHANNELS 16 enum ad3530r_mode { AD3530R_NORMAL_OP, @@ -67,9 +89,16 @@ struct ad3530r_chan { struct ad3530r_chip_info { const char *name; const struct iio_chan_spec *channels; + const struct regmap_config *regmap_config; int (*input_ch_reg)(unsigned int channel); + int (*sw_ldac_trig_reg)(unsigned int channel); + const unsigned int *interface_config_a; + const unsigned int *output_control; + const unsigned int *reference_control; + const unsigned int *op_mode; unsigned int num_channels; - unsigned int sw_ldac_trig_reg; + unsigned int num_banks; + unsigned int num_op_mode_regs; bool internal_ref_support; }; @@ -77,7 +106,7 @@ struct ad3530r_state { struct regmap *regmap; /* lock to protect against multiple access to the device and shared data */ struct mutex lock; - struct ad3530r_chan chan[AD3530R_MAX_CHANNELS]; + struct ad3530r_chan chan[AD3532R_MAX_CHANNELS]; const struct ad3530r_chip_info *chip_info; struct gpio_desc *ldac_gpio; int vref_mV; @@ -98,6 +127,14 @@ static int ad3531r_input_ch_reg(unsigned int channel) return 2 * channel + AD3531R_INPUT_CH; } +static int ad3532r_input_ch_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + unsigned int local_ch = channel % AD3530R_CH_PER_BANK; + + return 2 * local_ch + (bank ? AD3532R_INPUT_CH_1 : AD3532R_INPUT_CH_0); +} + static const char * const ad3530r_powerdown_modes[] = { "1kohm_to_gnd", "7.7kohm_to_gnd", @@ -110,6 +147,12 @@ static const char * const ad3531r_powerdown_modes[] = { "16kohm_to_gnd", }; +static const char * const ad3532r_powerdown_modes[] = { + "1kohm_to_gnd", + "10kohm_to_gnd", + "three_state", +}; + static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -145,6 +188,13 @@ static const struct iio_enum ad3531r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static const struct iio_enum ad3532r_powerdown_mode_enum = { + .items = ad3532r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3532r_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, @@ -189,6 +239,62 @@ static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev, return len; } +static ssize_t ad3532r_set_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + unsigned int bank, local_ch, reg_in_bank, ch_in_reg; + unsigned int reg, mask, val; + bool powerdown; + int ret; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + bank = chan->channel / AD3530R_CH_PER_BANK; + local_ch = chan->channel % AD3530R_CH_PER_BANK; + reg_in_bank = local_ch / AD3530R_CH_PER_REG; + ch_in_reg = local_ch % AD3530R_CH_PER_REG; + + reg = reg_in_bank + (bank ? AD3532R_OUTPUT_OPERATING_MODE_2 : + AD3532R_OUTPUT_OPERATING_MODE_0); + mask = AD3530R_OP_MODE_CHAN_MSK(ch_in_reg); + + guard(mutex)(&st->lock); + if (powerdown) { + val = field_prep(mask, st->chan[chan->channel].powerdown_mode); + ret = regmap_update_bits(st->regmap, reg, mask, val); + } else { + ret = regmap_clear_bits(st->regmap, reg, mask); + } + if (ret) + return ret; + + st->chan[chan->channel].powerdown = powerdown; + + return len; +} + +static int ad3530r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3530R_SW_LDAC_TRIG_A; +} + +static int ad3531r_trigger_sw_ldac_reg(unsigned int channel) +{ + return AD3531R_SW_LDAC_TRIG_A; +} + +static int ad3532r_trigger_sw_ldac_reg(unsigned int channel) +{ + unsigned int bank = channel / AD3530R_CH_PER_BANK; + + return bank ? AD3532R_SW_LDAC_TRIG_1 : AD3532R_SW_LDAC_TRIG_0; +} + static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) { gpiod_set_value_cansleep(ldac_gpio, 1); @@ -214,7 +320,7 @@ static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan, if (st->ldac_gpio) return ad3530r_trigger_hw_ldac(st->ldac_gpio); - return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg, + return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg(chan), AD3530R_SLD_TRIG_A); } @@ -301,6 +407,19 @@ static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = { { } }; +static const struct iio_chan_spec_ext_info ad3532r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3532r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3532r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3532r_powerdown_mode_enum), + { } +}; + #define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ @@ -330,48 +449,215 @@ static const struct iio_chan_spec ad3531r_channels[] = { AD3530R_CHAN(3, ad3531r_ext_info), }; +static const struct iio_chan_spec ad3532r_channels[] = { + AD3530R_CHAN(0, ad3532r_ext_info), + AD3530R_CHAN(1, ad3532r_ext_info), + AD3530R_CHAN(2, ad3532r_ext_info), + AD3530R_CHAN(3, ad3532r_ext_info), + AD3530R_CHAN(4, ad3532r_ext_info), + AD3530R_CHAN(5, ad3532r_ext_info), + AD3530R_CHAN(6, ad3532r_ext_info), + AD3530R_CHAN(7, ad3532r_ext_info), + AD3530R_CHAN(8, ad3532r_ext_info), + AD3530R_CHAN(9, ad3532r_ext_info), + AD3530R_CHAN(10, ad3532r_ext_info), + AD3530R_CHAN(11, ad3532r_ext_info), + AD3530R_CHAN(12, ad3532r_ext_info), + AD3530R_CHAN(13, ad3532r_ext_info), + AD3530R_CHAN(14, ad3532r_ext_info), + AD3530R_CHAN(15, ad3532r_ext_info), +}; + +static const unsigned int ad3530r_if_config[] = { + AD3530R_INTERFACE_CONFIG_A, +}; + +static const unsigned int ad3530r_out_ctrl[] = { + AD3530R_OUTPUT_CONTROL_0, +}; + +static const unsigned int ad3530r_ref_ctrl[] = { + AD3530R_REFERENCE_CONTROL_0, +}; + +static const unsigned int ad3530r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, + AD3530R_OUTPUT_OPERATING_MODE_1, +}; + +static const unsigned int ad3531r_op_mode[] = { + AD3530R_OUTPUT_OPERATING_MODE_0, +}; + +static const unsigned int ad3532r_if_config[] = { + AD3532R_INTERFACE_CONFIG_A_0, + AD3532R_INTERFACE_CONFIG_A_1, +}; + +static const unsigned int ad3532r_out_ctrl[] = { + AD3532R_OUTPUT_CONTROL_0, + AD3532R_OUTPUT_CONTROL_1, +}; + +static const unsigned int ad3532r_ref_ctrl[] = { + AD3532R_REFERENCE_CONTROL_0, + AD3532R_REFERENCE_CONTROL_1, +}; + +static const unsigned int ad3532r_op_mode[] = { + AD3532R_OUTPUT_OPERATING_MODE_0, + AD3532R_OUTPUT_OPERATING_MODE_1, + AD3532R_OUTPUT_OPERATING_MODE_2, + AD3532R_OUTPUT_OPERATING_MODE_3, +}; + +static const struct regmap_config ad3530r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3530R_MAX_REG_ADDR, +}; + +static const struct regmap_config ad3532r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3532R_MAX_REG_ADDR, +}; + static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3530r_chip = { .name = "ad3530r", .channels = ad3530r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3530r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3530r_op_mode), .internal_ref_support = true, }; static const struct ad3530r_chip_info ad3531_chip = { .name = "ad3531", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3531r_chip = { .name = "ad3531r", .channels = ad3531r_channels, + .regmap_config = &ad3530r_regmap_config, .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .sw_ldac_trig_reg = ad3531r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .interface_config_a = ad3530r_if_config, + .output_control = ad3530r_out_ctrl, + .reference_control = ad3530r_ref_ctrl, + .op_mode = ad3531r_op_mode, + .num_banks = ARRAY_SIZE(ad3530r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3531r_op_mode), + .internal_ref_support = true, +}; + +static const struct ad3530r_chip_info ad3532_chip = { + .name = "ad3532", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), + .internal_ref_support = false, +}; + +static const struct ad3530r_chip_info ad3532r_chip = { + .name = "ad3532r", + .channels = ad3532r_channels, + .regmap_config = &ad3532r_regmap_config, + .num_channels = ARRAY_SIZE(ad3532r_channels), + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .interface_config_a = ad3532r_if_config, + .output_control = ad3532r_out_ctrl, + .reference_control = ad3532r_ref_ctrl, + .op_mode = ad3532r_op_mode, + .num_banks = ARRAY_SIZE(ad3532r_if_config), + .num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode), .internal_ref_support = true, }; +static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int mask) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_set_bits(st->regmap, regs[i], mask); + if (ret) + return ret; + } + + return 0; +} + +static int ad3530r_write_reg_banks(const struct ad3530r_state *st, + const unsigned int *regs, + unsigned int num_regs, + unsigned int val) +{ + int ret; + + for (unsigned int i = 0; i < num_regs; i++) { + ret = regmap_write(st->regmap, regs[i], val); + if (ret) + return ret; + } + + return 0; +} + static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) { + const struct ad3530r_chip_info *chip_info = st->chip_info; struct device *dev = regmap_get_device(st->regmap); struct gpio_desc *reset_gpio; - int i, ret; u8 range_multiplier, val; + int ret; reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) @@ -384,8 +670,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) gpiod_set_value_cansleep(reset_gpio, 0); } else { /* Perform software reset */ - ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A, - AD3530R_SW_RESET, AD3530R_SW_RESET); + ret = ad3530r_set_reg_bank_bits(st, chip_info->interface_config_a, + chip_info->num_banks, + AD3530R_SW_RESET); if (ret) return ret; } @@ -394,8 +681,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) range_multiplier = 1; if (device_property_read_bool(dev, "adi,range-double")) { - ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0, - AD3530R_OUTPUT_CONTROL_RANGE); + ret = ad3530r_set_reg_bank_bits(st, chip_info->output_control, + chip_info->num_banks, + AD3530R_OUTPUT_CONTROL_RANGE); if (ret) return ret; @@ -405,8 +693,9 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) if (external_vref_uV) { st->vref_mV = range_multiplier * external_vref_uV / MILLI; } else { - ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0, - AD3530R_REFERENCE_CONTROL_SEL); + ret = ad3530r_set_reg_bank_bits(st, chip_info->reference_control, + chip_info->num_banks, + AD3530R_REFERENCE_CONTROL_SEL); if (ret) return ret; @@ -419,18 +708,12 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val); + ret = ad3530r_write_reg_banks(st, st->chip_info->op_mode, + st->chip_info->num_op_mode_regs, val); if (ret) return ret; - if (st->chip_info->num_channels > 4) { - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1, - val); - if (ret) - return ret; - } - - for (i = 0; i < st->chip_info->num_channels; i++) + for (unsigned int i = 0; i < st->chip_info->num_channels; i++) st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); @@ -441,12 +724,6 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) return 0; } -static const struct regmap_config ad3530r_regmap_config = { - .reg_bits = 16, - .val_bits = 8, - .max_register = AD3530R_MAX_REG_ADDR, -}; - static const struct iio_info ad3530r_info = { .read_raw = ad3530r_read_raw, .write_raw = ad3530r_write_raw, @@ -467,7 +744,11 @@ static int ad3530r_probe(struct spi_device *spi) st = iio_priv(indio_dev); - st->regmap = devm_regmap_init_spi(spi, &ad3530r_regmap_config); + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + st->regmap = devm_regmap_init_spi(spi, st->chip_info->regmap_config); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to init regmap"); @@ -476,10 +757,6 @@ static int ad3530r_probe(struct spi_device *spi) if (ret) return ret; - st->chip_info = spi_get_device_match_data(spi); - if (!st->chip_info) - return -ENODEV; - ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), regulators); if (ret) @@ -513,6 +790,8 @@ static const struct spi_device_id ad3530r_id[] = { { .name = "ad3530r", .driver_data = (kernel_ulong_t)&ad3530r_chip }, { .name = "ad3531", .driver_data = (kernel_ulong_t)&ad3531_chip }, { .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip }, + { .name = "ad3532", .driver_data = (kernel_ulong_t)&ad3532_chip }, + { .name = "ad3532r", .driver_data = (kernel_ulong_t)&ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(spi, ad3530r_id); @@ -522,6 +801,8 @@ static const struct of_device_id ad3530r_of_match[] = { { .compatible = "adi,ad3530r", .data = &ad3530r_chip }, { .compatible = "adi,ad3531", .data = &ad3531_chip }, { .compatible = "adi,ad3531r", .data = &ad3531r_chip }, + { .compatible = "adi,ad3532", .data = &ad3532_chip }, + { .compatible = "adi,ad3532r", .data = &ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(of, ad3530r_of_match); diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c index ddec62b6e57b..a2292810e4cf 100644 --- a/drivers/iio/dac/ad8460.c +++ b/drivers/iio/dac/ad8460.c @@ -954,3 +954,4 @@ MODULE_AUTHOR("Mariel Tinaco <mariel.tinaco@analog.com"); MODULE_DESCRIPTION("AD8460 DAC driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index cf6d94e7af84..de36e2e27330 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -253,3 +253,4 @@ module_platform_driver(dpot_dac_driver); MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/dac/mcf54415_dac.c b/drivers/iio/dac/mcf54415_dac.c new file mode 100644 index 000000000000..e2c12241a534 --- /dev/null +++ b/drivers/iio/dac/mcf54415_dac.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP mcf54415 DAC driver + * + * Copyright 2026 BayLibre - adureghello@baylibre.com + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> + +#define MCF54415_DAC_CR 0x00 +#define MCF54415_DAC_CR_PDN BIT(0) +#define MCF54415_DAC_CR_HSLS BIT(6) +#define MCF54415_DAC_CR_WMLVL GENMASK(9, 8) +#define MCF54415_DAC_CR_FILT BIT(12) + +#define MCF54415_DAC_DATA 0x02 + +struct mcf54415_dac { + struct regmap *map; + struct clk *clk; +}; + +static const struct regmap_config mcf54415_dac_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x0c, /* DACX_FILTCNT, R.M. Table 30-2 */ + .val_format_endian = REGMAP_ENDIAN_BIG, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int mcf54415_dac_init(struct mcf54415_dac *info) +{ + u16 val = MCF54415_DAC_CR_FILT | FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1); + int ret; + + /* Fixed defaults and enable DAC (bit 0 set to 0) */ + ret = regmap_write(info->map, MCF54415_DAC_CR, val); + if (ret) + return ret; + + /* DAC is ready after 12us, from RM table 40-3 */ + fsleep(12); + + return 0; +} + +static void mcf54415_dac_exit(void *data) +{ + struct mcf54415_dac *info = data; + + regmap_set_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN); +} + +static const struct iio_chan_spec mcf54415_dac_iio_channel = { + .type = IIO_VOLTAGE, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int mcf54415_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + unsigned int reg; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(info->map, MCF54415_DAC_DATA, ®); + if (ret) + return ret; + *val = reg & GENMASK(11, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Reference voltage as per ColdFire datasheet is 3.3V */ + *val = 3300 /* mV */; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int mcf54415_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcf54415_dac *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* Check based on RM 30.3.2 (DACn_DATA) reg. resolution */ + if (val < 0 || val > 4095) + return -EINVAL; + return regmap_write(info->map, MCF54415_DAC_DATA, val); + default: + return -EINVAL; + } +} + +static const struct iio_info mcf54415_dac_iio_info = { + .read_raw = &mcf54415_read_raw, + .write_raw = &mcf54415_write_raw, +}; + +static int mcf54415_dac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct mcf54415_dac *info; + void __iomem *regs; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), "failed to get io regs\n"); + + info->map = devm_regmap_init_mmio(dev, regs, &mcf54415_dac_regmap_config); + if (IS_ERR(info->map)) + return PTR_ERR(info->map); + + info->clk = devm_clk_get_enabled(dev, "dac"); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n"); + + indio_dev->name = "mcf54415"; + indio_dev->info = &mcf54415_dac_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &mcf54415_dac_iio_channel; + indio_dev->num_channels = 1; + + ret = mcf54415_dac_init(info); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id mcf54415_dac_ids[] = { + { .name = "mcfdac" }, + { } +}; +MODULE_DEVICE_TABLE(platform, mcf54415_dac_ids); + +static struct platform_driver mcf54415_dac_driver = { + .driver = { + .name = "mcf54415_dac", + }, + .probe = mcf54415_dac_probe, + .id_table = mcf54415_dac_ids, +}; +module_platform_driver(mcf54415_dac_driver); + +MODULE_AUTHOR("Angelo Dureghello <angelo@kernel-space.org>"); +MODULE_DESCRIPTION("NXP MCF54415 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 4bc3c387cb17..58250a972567 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -84,9 +84,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = { /* Channel read_raw handler */ static int gyro_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int report_id = -1; @@ -111,8 +110,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { *val = 0; - hid_sensor_power_state(&gyro_state->common_attributes, - false); + hid_sensor_power_state(&gyro_state->common_attributes, false); return -EINVAL; } hid_sensor_power_state(&gyro_state->common_attributes, false); @@ -145,10 +143,8 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int gyro_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct gyro_3d_state *gyro_state = iio_priv(indio_dev); int ret = 0; @@ -176,8 +172,7 @@ static const struct iio_info gyro_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -198,9 +193,9 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -253,10 +248,10 @@ static int gyro_3d_parse_report(struct platform_device *pdev, }; } dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n", - st->gyro[0].index, - st->gyro[0].report_id, - st->gyro[1].index, st->gyro[1].report_id, - st->gyro[2].index, st->gyro[2].report_id); + st->gyro[0].index, + st->gyro[0].report_id, + st->gyro[1].index, st->gyro[1].report_id, + st->gyro[2].index, st->gyro[2].report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_GYRO_3D, @@ -317,32 +312,32 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) atomic_set(&gyro_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &gyro_state->common_attributes); + &gyro_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - gyro_state->callbacks.send_event = gyro_3d_proc_event; gyro_state->callbacks.capture_sample = gyro_3d_capture_sample; gyro_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D, - &gyro_state->callbacks); + &gyro_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); return ret; @@ -355,8 +350,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); } diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index 7751abb58bb9..7cec81ff5685 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec humidity_channels[] = { /* Adjust channel real bits based on report descriptor */ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -54,8 +54,8 @@ static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, } static int humidity_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -100,8 +100,8 @@ static int humidity_read_raw(struct iio_dev *indio_dev, } static int humidity_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -126,7 +126,7 @@ static const struct iio_info humidity_info = { /* Callback handler to send event after all samples are received and captured */ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -140,8 +140,9 @@ static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_humidity_state *humid_st = iio_priv(indio_dev); @@ -158,10 +159,10 @@ static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id */ static int humidity_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct hid_humidity_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct hid_humidity_state *st) { int ret; @@ -214,13 +215,13 @@ static int hid_humidity_probe(struct platform_device *pdev) if (ret) return ret; - humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels, - sizeof(humidity_channels), GFP_KERNEL); + humid_chans = devm_kmemdup(&pdev->dev, humidity_channels, + sizeof(humidity_channels), GFP_KERNEL); if (!humid_chans) return -ENOMEM; ret = humidity_parse_report(pdev, hsdev, humid_chans, - HID_USAGE_SENSOR_HUMIDITY, humid_st); + HID_USAGE_SENSOR_HUMIDITY, humid_st); if (ret) return ret; @@ -233,7 +234,7 @@ static int hid_humidity_probe(struct platform_device *pdev) atomic_set(&humid_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &humid_st->common_attributes); + &humid_st->common_attributes); if (ret) return ret; @@ -241,7 +242,7 @@ static int hid_humidity_probe(struct platform_device *pdev) humidity_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY, - &humidity_callbacks); + &humidity_callbacks); if (ret) goto error_remove_trigger; diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 8603103cede0..5187566c1876 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -110,7 +110,7 @@ #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) -/* Each filter coefficent bank spans two pages */ +/* Each filter coefficient bank spans two pages */ #define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \ ADIS16480_REG((page) + 1, (x) - 60 + 8)) #define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x)) @@ -1467,7 +1467,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p) * We need to perform the padding to have the buffer * elements naturally aligned in case there are any * 32-bit storage size channels enabled which are added - * in the buffer after the temprature data. In case + * in the buffer after the temperature data. In case * there is no data being added after the temperature * data, the padding is harmless. */ diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 1e435d60cd6d..096a612865c4 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -88,25 +88,7 @@ struct adis16550_sync { u16 max_rate; }; -struct adis16550_chip_info { - const struct iio_chan_spec *channels; - const struct adis16550_sync *sync_mode; - char *name; - u32 num_channels; - u32 gyro_max_val; - u32 gyro_max_scale; - u32 accel_max_val; - u32 accel_max_scale; - u32 temp_scale; - u32 deltang_max_val; - u32 deltvel_max_val; - u32 int_clk; - u16 max_dec; - u16 num_sync; -}; - struct adis16550 { - const struct adis16550_chip_info *info; struct adis adis; unsigned long clk_freq_hz; u32 sync_mode; @@ -449,8 +431,8 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) * The optimal sample rate for the supported IMUs is between * int_clk - 1000 and int_clk + 500. */ - u32 max_sample_rate = st->info->int_clk * 1000 + 500000; - u32 min_sample_rate = st->info->int_clk * 1000 - 1000000; + u32 max_sample_rate = 4000 * 1000 + 500000; + u32 min_sample_rate = 4000 * 1000 - 1000000; if (!freq_hz) return -EINVAL; @@ -483,7 +465,7 @@ static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) if (dec) dec--; - dec = min(dec, st->info->max_dec); + dec = min(dec, 4095); return __adis_write_reg_16(&st->adis, ADIS16550_REG_DEC_RATE, dec); } @@ -591,30 +573,30 @@ static int adis16550_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: - *val = st->info->gyro_max_val; - *val2 = st->info->gyro_max_scale; + *val = 1; + *val2 = IIO_RAD_TO_DEGREE(80 << 16); return IIO_VAL_FRACTIONAL; case IIO_ACCEL: - *val = st->info->accel_max_val; - *val2 = st->info->accel_max_scale; + *val = 1; + *val2 = IIO_M_S_2_TO_G(102400000); return IIO_VAL_FRACTIONAL; case IIO_TEMP: - *val = st->info->temp_scale; + *val = 4; return IIO_VAL_INT; case IIO_DELTA_ANGL: - *val = st->info->deltang_max_val; + *val = IIO_DEGREE_TO_RAD(720); *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; case IIO_DELTA_VELOCITY: - *val = st->info->deltvel_max_val; + *val = 125; *val2 = 31; return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - /* temperature centered at 25°C */ - *val = DIV_ROUND_CLOSEST(25000, st->info->temp_scale); + /* temperature centered at 25°C divided by temp scale */ + *val = 25000 / 4; return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBBIAS: ret = adis_read_reg_32(&st->adis, @@ -792,23 +774,6 @@ static const struct adis16550_sync adis16550_sync_modes[] = { { ADIS16550_SYNC_MODE_SCALED, 1, 128 }, }; -static const struct adis16550_chip_info adis16550_chip_info = { - .num_channels = ARRAY_SIZE(adis16550_channels), - .channels = adis16550_channels, - .name = "adis16550", - .gyro_max_val = 1, - .gyro_max_scale = IIO_RAD_TO_DEGREE(80 << 16), - .accel_max_val = 1, - .accel_max_scale = IIO_M_S_2_TO_G(102400000), - .temp_scale = 4, - .deltang_max_val = IIO_DEGREE_TO_RAD(720), - .deltvel_max_val = 125, - .int_clk = 4000, - .max_dec = 4095, - .sync_mode = adis16550_sync_modes, - .num_sync = ARRAY_SIZE(adis16550_sync_modes), -}; - static u32 adis16550_validate_crc(__be32 *buffer, const u8 n_elem) { int i; @@ -917,21 +882,21 @@ static int adis16550_config_sync(struct adis16550 *st) if (IS_ERR(clk)) return PTR_ERR(clk); if (!clk) { - st->clk_freq_hz = st->info->int_clk * 1000; + st->clk_freq_hz = 4000000; return 0; } st->clk_freq_hz = clk_get_rate(clk); - for (i = 0; i < st->info->num_sync; i++) { - if (st->clk_freq_hz >= st->info->sync_mode[i].min_rate && - st->clk_freq_hz <= st->info->sync_mode[i].max_rate) { - sync_mode_data = &st->info->sync_mode[i]; + for (i = 0; i < ARRAY_SIZE(adis16550_sync_modes); i++) { + if (st->clk_freq_hz >= adis16550_sync_modes[i].min_rate && + st->clk_freq_hz <= adis16550_sync_modes[i].max_rate) { + sync_mode_data = &adis16550_sync_modes[i]; break; } } - if (i == st->info->num_sync) + if (i == ARRAY_SIZE(adis16550_sync_modes)) return dev_err_probe(dev, -EINVAL, "Clk rate: %lu not in a valid range", st->clk_freq_hz); @@ -942,7 +907,7 @@ static int adis16550_config_sync(struct adis16550 *st) * of [3000 4500]. */ - sync_scale = DIV_ROUND_CLOSEST(st->info->int_clk, st->clk_freq_hz); + sync_scale = DIV_ROUND_CLOSEST(4000, st->clk_freq_hz); if (3000 > sync_scale || 4500 < sync_scale) return dev_err_probe(dev, -EINVAL, @@ -954,7 +919,7 @@ static int adis16550_config_sync(struct adis16550 *st) if (ret) return ret; - st->clk_freq_hz = st->info->int_clk; + st->clk_freq_hz = 4000; } st->clk_freq_hz *= 1000; @@ -1063,13 +1028,11 @@ static int adis16550_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - st->info = spi_get_device_match_data(spi); - if (!st->info) - return -EINVAL; + adis = &st->adis; - indio_dev->name = st->info->name; - indio_dev->channels = st->info->channels; - indio_dev->num_channels = st->info->num_channels; + indio_dev->name = "adis16550"; + indio_dev->channels = adis16550_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16550_channels); indio_dev->available_scan_masks = adis16550_channel_masks; indio_dev->info = &adis16550_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1116,13 +1079,13 @@ static int adis16550_probe(struct spi_device *spi) } static const struct spi_device_id adis16550_id[] = { - { "adis16550", (kernel_ulong_t)&adis16550_chip_info}, + { .name = "adis16550" }, { } }; MODULE_DEVICE_TABLE(spi, adis16550_id); static const struct of_device_id adis16550_of_match[] = { - { .compatible = "adi,adis16550", .data = &adis16550_chip_info }, + { .compatible = "adi,adis16550" }, { } }; MODULE_DEVICE_TABLE(of, adis16550_of_match); diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 86f6ecfd64aa..1e73a7f1f017 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -5,7 +5,7 @@ * Copyright (c) 2016, Intel Corporation. * Copyright (c) 2019, Martin Kelly. * - * IIO core driver for BMI160, with support for I2C/SPI busses + * IIO core driver for BMI160, with support for I2C/SPI buses * * TODO: magnetometer, hardware FIFO */ diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c index b6cfc5f3844e..759b56b59e54 100644 --- a/drivers/iio/imu/bmi270/bmi270_spi.c +++ b/drivers/iio/imu/bmi270/bmi270_spi.c @@ -75,6 +75,7 @@ static const struct of_device_id bmi270_of_match[] = { { .compatible = "bosch,bmi270", .data = &bmi270_chip_info }, { } }; +MODULE_DEVICE_TABLE(of, bmi270_of_match); static struct spi_driver bmi270_spi_driver = { .driver = { diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c index 281ebfd9c15a..9d2eb6fbab4a 100644 --- a/drivers/iio/imu/fxos8700_core.c +++ b/drivers/iio/imu/fxos8700_core.c @@ -2,7 +2,7 @@ /* * FXOS8700 - NXP IMU (accelerometer plus magnetometer) * - * IIO core driver for FXOS8700, with support for I2C/SPI busses + * IIO core driver for FXOS8700, with support for I2C/SPI buses * * TODO: Buffer, trigger, and IRQ support */ diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index c8b48a5c5ed0..b55d993f0264 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -6,13 +6,15 @@ #ifndef INV_ICM42600_H_ #define INV_ICM42600_H_ -#include <linux/bits.h> #include <linux/bitfield.h> -#include <linux/regmap.h> +#include <linux/bits.h> #include <linux/mutex.h> -#include <linux/regulator/consumer.h> #include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + #include <linux/iio/iio.h> + #include <linux/iio/common/inv_sensors_timestamp.h> #include "inv_icm42600_buffer.h" @@ -354,6 +356,8 @@ struct inv_icm42600_sensor_state { cpu_to_le16((_wm) & GENMASK(11, 0)) /* FIFO is 2048 bytes, let 12 samples for reading latency */ #define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16) +/* INV_ICM42600_FIFO_WATERMARK_MAX / 8 = 232 */ +#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 232 #define INV_ICM42600_REG_INT_CONFIG1 0x0064 #define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 532d5fdffaf8..4b0e3cd8a506 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -3,25 +3,26 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/minmax.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/delay.h> -#include <linux/math64.h> -#include <linux/minmax.h> #include <linux/units.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> +#include <linux/iio/common/inv_sensors_timestamp.h> + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \ { \ @@ -1170,10 +1171,10 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) accel_st->filter = INV_ICM42600_FILTER_AVG_16X; /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&accel_st->ts, &ts_chip); @@ -1186,8 +1187,9 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels); indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 5c3840acf085..998d312f7bde 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -3,17 +3,20 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/kernel.h> #include <linux/minmax.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/delay.h> +#include <linux/stringify.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <linux/iio/common/inv_sensors_timestamp.h> #include "inv_icm42600.h" #include "inv_icm42600_buffer.h" @@ -186,7 +189,7 @@ static unsigned int inv_icm42600_wm_truncate(unsigned int watermark, * smallest latency but this is not as simple as choosing the smallest watermark * value. Latency depends on watermark and ODR. It requires several steps: * 1) compute gyro and accel latencies and choose the smallest value. - * 2) adapt the choosen latency so that it is a multiple of both gyro and accel + * 2) adapt the chosen latency so that it is a multiple of both gyro and accel * ones. Otherwise it is possible that you don't meet a requirement. (for * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the * value of 4 will not meet accel latency requirement because 6 is not a @@ -438,6 +441,40 @@ const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = { .postdisable = inv_icm42600_buffer_postdisable, }; +static ssize_t hwfifo_watermark_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + unsigned int wm; + + guard(mutex)(&st->lock); + + if (indio_dev == st->indio_accel) + wm = st->fifo.watermark.eff_accel; + else if (indio_dev == st->indio_gyro) + wm = st->fifo.watermark.eff_gyro; + else + return -EINVAL; + + return sysfs_emit(buf, "%u\n", wm); +} + +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "1"); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max, + __stringify(INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES)); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_enabled, "1"); + +const struct iio_dev_attr *inv_icm42600_buffer_attrs[] = { + &iio_dev_attr_hwfifo_watermark_min, + &iio_dev_attr_hwfifo_watermark_max, + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL +}; + int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, unsigned int max) { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index 88b8b9f780af..e37ee3416337 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -6,8 +6,8 @@ #ifndef INV_ICM42600_BUFFER_H_ #define INV_ICM42600_BUFFER_H_ -#include <linux/kernel.h> #include <linux/bits.h> +#include <linux/kernel.h> struct inv_icm42600_state; @@ -80,6 +80,7 @@ ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, const void **timestamp, unsigned int *odr); extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops; +extern const struct iio_dev_attr *inv_icm42600_buffer_attrs[]; int inv_icm42600_buffer_init(struct inv_icm42600_state *st); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 76eb22488e5f..dc97d8a274e3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -3,18 +3,18 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/module.h> -#include <linux/slab.h> #include <linux/delay.h> -#include <linux/mutex.h> +#include <linux/device.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/regulator/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/property.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 11339ddf1da3..253bf571439d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -3,22 +3,23 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/kernel.h> +#include <linux/math64.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/delay.h> -#include <linux/math64.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> +#include <linux/iio/common/inv_sensors_timestamp.h> + #include "inv_icm42600.h" -#include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" +#include "inv_icm42600_temp.h" #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \ { \ @@ -755,10 +756,10 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) } /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip); @@ -772,8 +773,9 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks; indio_dev->setup_ops = &inv_icm42600_buffer_ops; - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - &inv_icm42600_buffer_ops); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &inv_icm42600_buffer_ops, + inv_icm42600_buffer_attrs); if (ret) return ERR_PTR(ret); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 1013aff4f0ab..28552d2db91d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -3,12 +3,12 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include <linux/kernel.h> #include <linux/device.h> -#include <linux/module.h> #include <linux/i2c.h> -#include <linux/regmap.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/property.h> +#include <linux/regmap.h> #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 11077f4df11b..faf743bc6444 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -3,12 +3,12 @@ * Copyright (C) 2020 InvenSense, Inc. */ -#include <linux/kernel.h> #include <linux/device.h> +#include <linux/kernel.h> #include <linux/module.h> -#include <linux/spi/spi.h> -#include <linux/regmap.h> #include <linux/property.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c index 727b03d541a5..7f80bda471c3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c @@ -3,11 +3,12 @@ * Copyright (C) 2020 Invensense, Inc. */ -#include <linux/kernel.h> #include <linux/device.h> +#include <linux/kernel.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> + #include <linux/iio/iio.h> #include "inv_icm42600.h" diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h index 1c796d4b2a40..95fa934a42b4 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -190,7 +190,7 @@ struct inv_icm45600_sensor_state { #define INV_ICM45600_REG_IREG_ADDR 0x7C #define INV_ICM45600_REG_IREG_DATA 0x7E -/* Direct acces registers */ +/* Direct access registers */ #define INV_ICM45600_REG_MISC2 0x007F #define INV_ICM45600_MISC2_SOFT_RESET BIT(1) diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c index 2b9ea317385c..42111c543d3c 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -422,8 +422,11 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, if (max > 0 && fifo_nb > max) fifo_nb = max; - /* Try to read all FIFO data in internal buffer. */ - st->fifo.count = fifo_nb * packet_size; + /* + * Read all FIFO data into the internal buffer, clamping the + * device-reported count to the buffer capacity. + */ + st->fifo.count = min(fifo_nb * packet_size, INV_ICM45600_FIFO_SIZE_MAX); ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA, st->fifo.data, st->fifo.count); if (ret == -ENOTSUPP || ret == -EFBIG) { diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c index d49053161a65..c1d7aa7e950d 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -716,7 +716,7 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi dev_set_drvdata(dev, st); - st->fifo.data = devm_kzalloc(dev, 8192, GFP_KERNEL); + st->fifo.data = devm_kzalloc(dev, INV_ICM45600_FIFO_SIZE_MAX, GFP_KERNEL); if (!st->fifo.data) return -ENOMEM; diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c index 81ba1b60f04b..69acf9bac2db 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c @@ -22,7 +22,7 @@ static int inv_icm45600_probe(struct i2c_client *client) if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -ENODEV; - chip_info = device_get_match_data(&client->dev); + chip_info = i2c_get_match_data(client); if (!chip_info) return -ENODEV; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 47394594d17a..6b858fdfd1c6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -106,9 +106,9 @@ static int inv_magn_init(struct inv_mpu6050_state *st) return ret; /* - * Sensor sentivity + * Sensor sensitivity * 1 uT = 0.01 G and value is in micron (1e6) - * sensitvity = x uT * 0.01 * 1e6 + * sensitivity = x uT * 0.01 * 1e6 */ switch (st->chip_type) { case INV_MPU9150: diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 4763e224ebc6..f7a4be8ec320 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -649,7 +649,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, "IIO_BACKEND"); * @len: Buffer length * * This helper is intended to be used by backends that extend an IIO channel - * (trough iio_backend_extend_chan_spec()) with extended info. In that case, + * (through iio_backend_extend_chan_spec()) with extended info. In that case, * backends are not supposed to give their own callbacks (as they would not have * a way to get the backend from indio_dev). This is the setter. * @@ -851,7 +851,7 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_filter_type_set, "IIO_BACKEND"); * @back: Backend device * @timeout_us: Timeout value in us. * - * When activated, it initates a proccess that aligns the sample's most + * When activated, it initiates a process that aligns the sample's most * significant bit (MSB) based solely on the captured data, without * considering any other external signals. * @@ -1017,7 +1017,7 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con * @dev: Consumer device for the backend * @name: Backend name * - * Get's the backend associated with @dev. + * Gets the backend associated with @dev. * * RETURNS: * A backend pointer, negative error pointer otherwise. @@ -1050,7 +1050,7 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get_by_index, "IIO_BACKEND"); * @name: Backend name * @fwnode: Firmware node of the backend consumer * - * Get's the backend associated with a firmware node. + * Gets the backend associated with a firmware node. * * RETURNS: * A backend pointer, negative error pointer otherwise. diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index a0d6fcf2a9c9..e6730f52262a 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -207,6 +207,8 @@ static int iio_event_getfd(struct iio_dev *indio_dev) goto unlock; } + kfifo_reset_out(&ev_int->det_events); + iio_device_get(indio_dev); fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, @@ -214,10 +216,7 @@ static int iio_event_getfd(struct iio_dev *indio_dev) if (fd < 0) { clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); iio_device_put(indio_dev); - } else { - kfifo_reset_out(&ev_int->det_events); } - unlock: mutex_unlock(&iio_dev_opaque->mlock); return fd; diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9ce20cb05a9b..fb08e4f02520 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -281,7 +281,7 @@ struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, return ERR_PTR(-ENODEV); } -EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) { @@ -386,7 +386,7 @@ struct iio_channel *iio_channel_get(struct device *dev, return iio_channel_get_sys(name, channel_name); } -EXPORT_SYMBOL_GPL(iio_channel_get); +EXPORT_SYMBOL_NS_GPL(iio_channel_get, "IIO_CONSUMER"); void iio_channel_release(struct iio_channel *channel) { @@ -395,7 +395,7 @@ void iio_channel_release(struct iio_channel *channel) iio_device_put(channel->indio_dev); kfree(channel); } -EXPORT_SYMBOL_GPL(iio_channel_release); +EXPORT_SYMBOL_NS_GPL(iio_channel_release, "IIO_CONSUMER"); static void devm_iio_channel_free(void *iio_channel) { @@ -418,7 +418,7 @@ struct iio_channel *devm_iio_channel_get(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get, "IIO_CONSUMER"); struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, struct fwnode_handle *fwnode, @@ -437,7 +437,7 @@ struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name); +EXPORT_SYMBOL_NS_GPL(devm_fwnode_iio_channel_get_by_name, "IIO_CONSUMER"); struct iio_channel *iio_channel_get_all(struct device *dev) { @@ -506,7 +506,7 @@ error_free_chans: iio_device_put(chans[i].indio_dev); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_get_all, "IIO_CONSUMER"); void iio_channel_release_all(struct iio_channel *channels) { @@ -518,7 +518,7 @@ void iio_channel_release_all(struct iio_channel *channels) } kfree(channels); } -EXPORT_SYMBOL_GPL(iio_channel_release_all); +EXPORT_SYMBOL_NS_GPL(iio_channel_release_all, "IIO_CONSUMER"); static void devm_iio_channel_free_all(void *iio_channels) { @@ -541,7 +541,7 @@ struct iio_channel *devm_iio_channel_get_all(struct device *dev) return channels; } -EXPORT_SYMBOL_GPL(devm_iio_channel_get_all); +EXPORT_SYMBOL_NS_GPL(devm_iio_channel_get_all, "IIO_CONSUMER"); static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info) @@ -585,7 +585,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_raw, "IIO_CONSUMER"); int iio_read_channel_average_raw(struct iio_channel *chan, int *val) { @@ -597,7 +597,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); } -EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_average_raw, "IIO_CONSUMER"); int iio_multiply_value(int *result, s64 multiplier, unsigned int type, int val, int val2) @@ -701,7 +701,7 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, return iio_convert_raw_to_processed_unlocked(chan, raw, processed, scale); } -EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); +EXPORT_SYMBOL_NS_GPL(iio_convert_raw_to_processed, "IIO_CONSUMER"); int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum attribute) @@ -714,13 +714,13 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, return iio_channel_read(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_read_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_attribute, "IIO_CONSUMER"); int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET); } -EXPORT_SYMBOL_GPL(iio_read_channel_offset); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_offset, "IIO_CONSUMER"); int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, unsigned int scale) @@ -752,20 +752,20 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, scale); } } -EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed_scale, "IIO_CONSUMER"); int iio_read_channel_processed(struct iio_channel *chan, int *val) { /* This is just a special case with scale factor 1 */ return iio_read_channel_processed_scale(chan, val, 1); } -EXPORT_SYMBOL_GPL(iio_read_channel_processed); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_processed, "IIO_CONSUMER"); int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE); } -EXPORT_SYMBOL_GPL(iio_read_channel_scale); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_scale, "IIO_CONSUMER"); static int iio_channel_read_avail(struct iio_channel *chan, const int **vals, int *type, int *length, @@ -794,7 +794,7 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, return iio_channel_read_avail(chan, vals, type, length, attribute); } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_attribute, "IIO_CONSUMER"); int iio_read_avail_channel_raw(struct iio_channel *chan, const int **vals, int *length) @@ -811,7 +811,7 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, return ret; } -EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_avail_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_max(struct iio_channel *chan, int *val, int *val2, int *type, @@ -867,7 +867,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_max_channel_raw, "IIO_CONSUMER"); static int iio_channel_read_min(struct iio_channel *chan, int *val, int *val2, int *type, @@ -923,7 +923,7 @@ int iio_read_min_channel_raw(struct iio_channel *chan, int *val) return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_read_min_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_read_min_channel_raw, "IIO_CONSUMER"); int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { @@ -937,7 +937,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) return 0; } -EXPORT_SYMBOL_GPL(iio_get_channel_type); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_type, "IIO_CONSUMER"); static int iio_channel_write(struct iio_channel *chan, int val, int val2, enum iio_chan_info_enum info) @@ -961,13 +961,13 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, return iio_channel_write(chan, val, val2, attribute); } -EXPORT_SYMBOL_GPL(iio_write_channel_attribute); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_attribute, "IIO_CONSUMER"); int iio_write_channel_raw(struct iio_channel *chan, int val) { return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW); } -EXPORT_SYMBOL_GPL(iio_write_channel_raw); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_raw, "IIO_CONSUMER"); unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) { @@ -982,7 +982,7 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) return i; } -EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); +EXPORT_SYMBOL_NS_GPL(iio_get_channel_ext_info_count, "IIO_CONSUMER"); static const struct iio_chan_spec_ext_info * iio_lookup_ext_info(const struct iio_channel *chan, const char *attr) @@ -1017,7 +1017,7 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan, return ext_info->read(chan->indio_dev, ext_info->private, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, const char *buf, size_t len) @@ -1031,7 +1031,7 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, return ext_info->write(chan->indio_dev, ext_info->private, chan->channel, buf, len); } -EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); +EXPORT_SYMBOL_NS_GPL(iio_write_channel_ext_info, "IIO_CONSUMER"); ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) { @@ -1042,4 +1042,4 @@ ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } -EXPORT_SYMBOL_GPL(iio_read_channel_label); +EXPORT_SYMBOL_NS_GPL(iio_read_channel_label, "IIO_CONSUMER"); diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 62a77acfd075..49f6d8336dbe 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -41,7 +41,7 @@ enum al3xxxx_range { }; static const int al3010_scales[][2] = { - {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600} + { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 }, }; static const struct regmap_config al3010_regmap_config = { diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 98c84f33db60..9f799041edf9 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -324,3 +324,4 @@ module_platform_driver(cm3605_driver); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index a8db514cca5e..8dd3704e6299 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -717,3 +717,4 @@ module_i2c_driver(gp2ap002_driver); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index bc1df59241fb..6fb6ace26834 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -119,9 +119,8 @@ static const struct iio_chan_spec als_channels[] = { /* Channel read_raw handler */ static int als_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct als_state *als_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev = als_state->common_attributes.hsdev; @@ -162,12 +161,12 @@ static int als_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&als_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( hsdev, hsdev->usage, address, report_id, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&als_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -205,10 +204,8 @@ static int als_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int als_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct als_state *als_state = iio_priv(indio_dev); int ret = 0; @@ -240,8 +237,7 @@ static const struct iio_info als_info = { /* Callback handler to send event after all samples are received and captured */ static int als_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -262,9 +258,9 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); @@ -303,9 +299,9 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int als_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - u32 usage_id, - struct als_state *st) + struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct als_state *st) { struct iio_chan_spec *channels; int ret, index = 0; @@ -399,31 +395,31 @@ static int hid_als_probe(struct platform_device *pdev) atomic_set(&als_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &als_state->common_attributes); + &als_state->common_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - als_state->callbacks.send_event = als_proc_event; als_state->callbacks.capture_sample = als_capture_sample; als_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); return ret; @@ -436,8 +432,8 @@ static void hid_als_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct als_state *als_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); } diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index fb8aef3744ae..63fd3eff171c 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -69,9 +69,8 @@ static const struct iio_chan_spec prox_channels[] = { /* Channel read_raw handler */ static int prox_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct prox_state *prox_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -134,10 +133,8 @@ static int prox_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int prox_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct prox_state *prox_state = iio_priv(indio_dev); int ret = 0; @@ -165,8 +162,7 @@ static const struct iio_info prox_info = { /* Callback handler to send event after all samples are received and captured */ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -182,9 +178,9 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); @@ -217,8 +213,8 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int prox_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct prox_state *st) + struct hid_sensor_hub_device *hsdev, + struct prox_state *st) { struct iio_chan_spec *channels = st->channels; int index = 0; @@ -270,8 +266,7 @@ static int hid_prox_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct prox_state *prox_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct prox_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct prox_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -305,18 +300,12 @@ static int hid_prox_probe(struct platform_device *pdev) atomic_set(&prox_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &prox_state->common_attributes); + &prox_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - prox_state->callbacks.send_event = prox_proc_event; prox_state->callbacks.capture_sample = prox_capture_sample; prox_state->callbacks.pdev = pdev; @@ -324,13 +313,19 @@ static int hid_prox_probe(struct platform_device *pdev) &prox_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); return ret; @@ -343,8 +338,8 @@ static void hid_prox_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct prox_state *prox_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); } diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index d8b8bcc865c3..ad18f233ee16 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -134,9 +134,8 @@ static const struct iio_chan_spec magn_3d_channels[] = { /* Channel read_raw handler */ static int magn_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int report_id = -1; @@ -166,8 +165,7 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, false); return -EINVAL; } - hid_sensor_power_state(&magn_state->magn_flux_attributes, - false); + hid_sensor_power_state(&magn_state->magn_flux_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: @@ -228,10 +226,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int magn_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct magn_3d_state *magn_state = iio_priv(indio_dev); int ret = 0; @@ -269,8 +265,7 @@ static const struct iio_info magn_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -291,9 +286,9 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -326,7 +321,7 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, iio_val = magn_state->magn_val_addr[offset]; - if (iio_val != NULL) + if (iio_val) *iio_val = *((u32 *)raw_data); else ret = -EINVAL; @@ -367,18 +362,17 @@ static int magn_3d_parse_report(struct platform_device *pdev, return -EINVAL; } - dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", - attr_count); + dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n", attr_count); dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n", - st->magn[0].index, - st->magn[0].report_id, - st->magn[1].index, st->magn[1].report_id, - st->magn[2].index, st->magn[2].report_id); + st->magn[0].index, + st->magn[0].report_id, + st->magn[1].index, st->magn[1].report_id, + st->magn[2].index, st->magn[2].report_id); /* Setup IIO channel array */ _channels = devm_kcalloc(&pdev->dev, attr_count, - sizeof(struct iio_chan_spec), - GFP_KERNEL); + sizeof(struct iio_chan_spec), + GFP_KERNEL); if (!_channels) { dev_err(&pdev->dev, "failed to allocate space for iio channels\n"); @@ -425,8 +419,7 @@ static int magn_3d_parse_report(struct platform_device *pdev, *channels = _channels; - dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", - *chan_count); + dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n", *chan_count); st->magn_flux_attr.scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_COMPASS_3D, @@ -467,7 +460,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct magn_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -490,8 +483,8 @@ static int hid_magn_3d_probe(struct platform_device *pdev) magn_state->rot_attributes.sensitivity.index = -1; ret = magn_3d_parse_report(pdev, hsdev, - &channels, &chan_count, - HID_USAGE_SENSOR_COMPASS_3D, magn_state); + &channels, &chan_count, + HID_USAGE_SENSOR_COMPASS_3D, magn_state); if (ret) { dev_err(&pdev->dev, "failed to parse report\n"); return ret; @@ -506,32 +499,32 @@ static int hid_magn_3d_probe(struct platform_device *pdev) atomic_set(&magn_state->magn_flux_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &magn_state->magn_flux_attributes); + &magn_state->magn_flux_attributes); if (ret < 0) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - magn_state->callbacks.send_event = magn_3d_proc_event; magn_state->callbacks.capture_sample = magn_3d_capture_sample; magn_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D, - &magn_state->callbacks); + &magn_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); return ret; @@ -544,8 +537,8 @@ static void hid_magn_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct magn_3d_state *magn_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); } diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index 4421dafcf94e..562457a67382 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -463,3 +463,4 @@ module_platform_driver(mux_driver); MODULE_DESCRIPTION("IIO multiplexer driver"); MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index d96fa432048b..aea28321db95 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -85,8 +85,7 @@ static const struct iio_chan_spec incl_3d_channels[] = { }; /* Adjust channel real bits based on report descriptor */ -static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, - int size) +static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, int size) { chan->scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -97,9 +96,8 @@ static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan, /* Channel read_raw handler */ static int incl_3d_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int report_id = -1; @@ -124,7 +122,7 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, min < 0); else { hid_sensor_power_state(&incl_state->common_attributes, - false); + false); return -EINVAL; } hid_sensor_power_state(&incl_state->common_attributes, false); @@ -157,10 +155,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int incl_3d_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct incl_3d_state *incl_state = iio_priv(indio_dev); int ret; @@ -188,8 +184,7 @@ static const struct iio_info incl_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -211,9 +206,9 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -306,7 +301,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct incl_3d_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -349,33 +344,33 @@ static int hid_incl_3d_probe(struct platform_device *pdev) atomic_set(&incl_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &incl_state->common_attributes); + &incl_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - incl_state->callbacks.send_event = incl_3d_proc_event; incl_state->callbacks.capture_sample = incl_3d_capture_sample; incl_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, - HID_USAGE_SENSOR_INCLINOMETER_3D, - &incl_state->callbacks); + HID_USAGE_SENSOR_INCLINOMETER_3D, + &incl_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); return ret; @@ -388,8 +383,8 @@ static void hid_incl_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct incl_3d_state *incl_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); } diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 9e5c14357a00..f8c98f866cb0 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -80,9 +80,8 @@ static const struct iio_chan_spec dev_rot_channels[] = { /* Channel read_raw handler */ static int dev_rot_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int size, int *vals, int *val_len, - long mask) + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev; @@ -164,10 +163,8 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int dev_rot_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); int ret; @@ -211,8 +208,7 @@ static const struct iio_info dev_rot_info = { /* Callback handler to send event after all samples are received and captured */ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -244,9 +240,9 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - size_t raw_len, char *raw_data, - void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -313,7 +309,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct dev_rot_state)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -361,32 +357,32 @@ static int hid_dev_rot_probe(struct platform_device *pdev) atomic_set(&rot_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &rot_state->common_attributes); + &rot_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - rot_state->callbacks.send_event = dev_rot_proc_event; rot_state->callbacks.capture_sample = dev_rot_capture_sample; rot_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, hsdev->usage, - &rot_state->callbacks); + &rot_state->callbacks); if (ret) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return 0; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); return ret; @@ -399,8 +395,8 @@ static void hid_dev_rot_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct dev_rot_state *rot_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); } diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index a0b984357bf0..d275bc1413fe 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -106,8 +106,8 @@ static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels, /* Channel read_raw handler */ static int hinge_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct hinge_state *st = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; @@ -153,8 +153,8 @@ static int hinge_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int hinge_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct hinge_state *st = iio_priv(indio_dev); @@ -208,8 +208,9 @@ static int hinge_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, size_t raw_len, - char *raw_data, void *priv) + u32 usage_id, + size_t raw_len, char *raw_data, + void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct hinge_state *st = iio_priv(indio_dev); @@ -291,7 +292,7 @@ static int hid_hinge_probe(struct platform_device *pdev) } indio_dev->num_channels = ARRAY_SIZE(hinge_channels); - indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels, + indio_dev->channels = devm_kmemdup(&pdev->dev, hinge_channels, sizeof(hinge_channels), GFP_KERNEL); if (!indio_dev->channels) return -ENOMEM; diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index 1984d990438c..00cb3fea97ca 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -422,3 +422,4 @@ module_i2c_driver(lmp91000_driver); MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); MODULE_DESCRIPTION("LMP91000 digital potentiostat"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 989d6d0fa94c..e688b0776547 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -55,9 +55,8 @@ static const struct iio_chan_spec press_channels[] = { /* Channel read_raw handler */ static int press_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct press_state *press_state = iio_priv(indio_dev); int report_id = -1; @@ -81,7 +80,7 @@ static int press_read_raw(struct iio_dev *indio_dev, } if (report_id >= 0) { hid_sensor_power_state(&press_state->common_attributes, - true); + true); *val = sensor_hub_input_attr_get_raw_value( press_state->common_attributes.hsdev, HID_USAGE_SENSOR_PRESSURE, address, @@ -89,7 +88,7 @@ static int press_read_raw(struct iio_dev *indio_dev, SENSOR_HUB_SYNC, min < 0); hid_sensor_power_state(&press_state->common_attributes, - false); + false); } else { *val = 0; return -EINVAL; @@ -123,10 +122,8 @@ static int press_read_raw(struct iio_dev *indio_dev, /* Channel write_raw handler */ static int press_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct press_state *press_state = iio_priv(indio_dev); int ret = 0; @@ -154,8 +151,7 @@ static const struct iio_info press_info = { /* Callback handler to send event after all samples are received and captured */ static int press_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - void *priv) + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct press_state *press_state = iio_priv(indio_dev); @@ -201,10 +197,10 @@ static int press_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int press_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct press_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct press_state *st) { int ret; @@ -221,7 +217,7 @@ static int press_parse_report(struct platform_device *pdev, }; dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index, - st->press_attr.report_id); + st->press_attr.report_id); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_PRESSURE, @@ -240,8 +236,7 @@ static int hid_press_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct press_state *press_state; - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct press_state)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct press_state)); if (!indio_dev) return -ENOMEM; platform_set_drvdata(pdev, indio_dev); @@ -284,32 +279,32 @@ static int hid_press_probe(struct platform_device *pdev) atomic_set(&press_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &press_state->common_attributes); + &press_state->common_attributes); if (ret) { dev_err(&pdev->dev, "trigger setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "device register failed\n"); - goto error_remove_trigger; - } - press_state->callbacks.send_event = press_proc_event; press_state->callbacks.capture_sample = press_capture_sample; press_state->callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE, - &press_state->callbacks); + &press_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - goto error_iio_unreg; + goto error_remove_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_callback; } return ret; -error_iio_unreg: - iio_device_unregister(indio_dev); +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); error_remove_trigger: hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); return ret; @@ -322,8 +317,8 @@ static void hid_press_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct press_state *press_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); } diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index b3be9de03678..9d72ae64126c 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -621,17 +621,15 @@ static const struct iio_buffer_setup_ops bm1390_buffer_ops = { .predisable = bm1390_buffer_predisable, }; -static irqreturn_t bm1390_trigger_handler(int irq, void *p) +static bool bm1390_handle_trigger(struct iio_dev *idev) { - struct iio_poll_func *pf = p; - struct iio_dev *idev = pf->indio_dev; struct bm1390_data *data = iio_priv(idev); int ret, status; /* DRDY is acked by reading status reg */ ret = regmap_read(data->regmap, BM1390_REG_STATUS, &status); if (ret || !status) - return IRQ_NONE; + return false; dev_dbg(data->dev, "DRDY trig status 0x%x\n", status); @@ -639,7 +637,7 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) ret = bm1390_pressure_read(data, &data->buf.pressure); if (ret) { dev_warn(data->dev, "sample read failed %d\n", ret); - return IRQ_NONE; + return false; } } @@ -648,15 +646,26 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) &data->buf.temp, sizeof(data->buf.temp)); if (ret) { dev_warn(data->dev, "temp read failed %d\n", ret); - return IRQ_HANDLED; + return true; } } iio_push_to_buffers_with_ts(idev, &data->buf, sizeof(data->buf), data->timestamp); + + return true; +} + +static irqreturn_t bm1390_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + bool result; + + result = bm1390_handle_trigger(idev); iio_trigger_notify_done(idev->trig); - return IRQ_HANDLED; + return IRQ_RETVAL(result); } /* Get timestamps and wake the thread if we need to read data */ diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index db8e34e2ba73..3b5ec21e3446 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1276,10 +1276,8 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, return regmap_write(st->regmap, reg, writeval); } -static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +static void ad2s1210_scan_to_buffers(struct iio_dev *indio_dev, s64 timestamp) { - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; struct ad2s1210_state *st = iio_priv(indio_dev); size_t chan = 0; int ret; @@ -1295,15 +1293,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_POSITION_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_POS); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1315,15 +1313,15 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) AD2S1210_REG_VELOCITY_MSB, &st->sample.raw, 2); if (ret < 0) - goto error_ret; + return; } else { ret = ad2s1210_set_mode(st, MOD_VEL); if (ret < 0) - goto error_ret; + return; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return; } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); @@ -1334,16 +1332,22 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - goto error_ret; + return; st->sample.fault = reg_val; } - ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); + ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), - pf->timestamp); + timestamp); +} + +static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; -error_ret: + ad2s1210_scan_to_buffers(indio_dev, pf->timestamp); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 586b5bc002c7..76723176d6b6 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -44,7 +44,7 @@ static const struct iio_chan_spec temperature_channels[] = { /* Adjust channel real bits based on report descriptor */ static void temperature_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) + int channel, int size) { channels[channel].scan_type.sign = 's'; /* Real storage bits will change based on the report desc. */ @@ -100,8 +100,8 @@ static int temperature_read_raw(struct iio_dev *indio_dev, } static int temperature_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct temperature_state *temp_st = iio_priv(indio_dev); @@ -124,7 +124,7 @@ static const struct iio_info temperature_info = { /* Callback handler to send event after all samples are received and captured */ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, - u32 usage_id, void *pdev) + u32 usage_id, void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -139,8 +139,9 @@ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, - u32 usage_id, size_t raw_len, - char *raw_data, void *pdev) + u32 usage_id, + size_t raw_len, char *raw_data, + void *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); @@ -156,10 +157,10 @@ static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int temperature_parse_report(struct platform_device *pdev, - struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - u32 usage_id, - struct temperature_state *st) + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + u32 usage_id, + struct temperature_state *st) { int ret; @@ -170,8 +171,7 @@ static int temperature_parse_report(struct platform_device *pdev, if (ret < 0) return ret; - temperature_adjust_channel_bit_mask(channels, 0, - st->temperature_attr.size); + temperature_adjust_channel_bit_mask(channels, 0, st->temperature_attr.size); st->scale_precision = hid_sensor_format_scale( HID_USAGE_SENSOR_TEMPERATURE, @@ -212,13 +212,13 @@ static int hid_temperature_probe(struct platform_device *pdev) if (ret) return ret; - temp_chans = devm_kmemdup(&indio_dev->dev, temperature_channels, - sizeof(temperature_channels), GFP_KERNEL); + temp_chans = devm_kmemdup(&pdev->dev, temperature_channels, + sizeof(temperature_channels), GFP_KERNEL); if (!temp_chans) return -ENOMEM; ret = temperature_parse_report(pdev, hsdev, temp_chans, - HID_USAGE_SENSOR_TEMPERATURE, temp_st); + HID_USAGE_SENSOR_TEMPERATURE, temp_st); if (ret) return ret; @@ -231,7 +231,7 @@ static int hid_temperature_probe(struct platform_device *pdev) atomic_set(&temp_st->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, - &temp_st->common_attributes); + &temp_st->common_attributes); if (ret) return ret; @@ -239,7 +239,7 @@ static int hid_temperature_probe(struct platform_device *pdev) temperature_callbacks.pdev = pdev; ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE, - &temperature_callbacks); + &temperature_callbacks); if (ret) goto error_remove_trigger; diff --git a/drivers/infiniband/hw/bng_re/bng_fw.c b/drivers/infiniband/hw/bng_re/bng_fw.c index 50156c300b33..ab6a2d2e95b5 100644 --- a/drivers/infiniband/hw/bng_re/bng_fw.c +++ b/drivers/infiniband/hw/bng_re/bng_fw.c @@ -401,14 +401,15 @@ static int __wait_for_resp(struct bng_re_rcfw *rcfw, u16 cookie) { struct bng_re_cmdq_ctx *cmdq; struct bng_re_crsqe *crsqe; + unsigned long time_left; cmdq = &rcfw->cmdq; crsqe = &rcfw->crsqe_tbl[cookie]; do { - wait_event_timeout(cmdq->waitq, - !crsqe->is_in_used, - secs_to_jiffies(rcfw->max_timeout)); + time_left = wait_event_timeout(cmdq->waitq, + !crsqe->is_in_used, + secs_to_jiffies(rcfw->max_timeout)); if (!crsqe->is_in_used) return 0; @@ -417,6 +418,9 @@ static int __wait_for_resp(struct bng_re_rcfw *rcfw, u16 cookie) if (!crsqe->is_in_used) return 0; + + if (!time_left) + return -ENODEV; } while (true); }; diff --git a/drivers/infiniband/hw/hfi1/device.c b/drivers/infiniband/hw/hfi1/device.c index a98a4175e53b..adcfb80d52d4 100644 --- a/drivers/infiniband/hw/hfi1/device.c +++ b/drivers/infiniband/hw/hfi1/device.c @@ -10,18 +10,6 @@ #include "hfi.h" #include "device.h" -static char *hfi1_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0600; - return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); -} - -static const struct class class = { - .name = "hfi1", - .devnode = hfi1_devnode, -}; - static char *hfi1_user_devnode(const struct device *dev, umode_t *mode) { if (mode) @@ -38,7 +26,6 @@ static dev_t hfi1_dev; int hfi1_cdev_init(int minor, const char *name, const struct file_operations *fops, struct cdev *cdev, struct device **devp, - bool user_accessible, struct kobject *parent) { const dev_t dev = MKDEV(MAJOR(hfi1_dev), minor); @@ -57,10 +44,7 @@ int hfi1_cdev_init(int minor, const char *name, goto done; } - if (user_accessible) - device = device_create(&user_class, NULL, dev, NULL, "%s", name); - else - device = device_create(&class, NULL, dev, NULL, "%s", name); + device = device_create(&user_class, NULL, dev, NULL, "%s", name); if (IS_ERR(device)) { ret = PTR_ERR(device); @@ -100,33 +84,21 @@ int __init dev_init(void) ret = alloc_chrdev_region(&hfi1_dev, 0, HFI1_NMINORS, DRIVER_NAME); if (ret < 0) { pr_err("Could not allocate chrdev region (err %d)\n", -ret); - goto done; - } - - ret = class_register(&class); - if (ret) { - pr_err("Could not create device class (err %d)\n", -ret); - unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); - goto done; + return ret; } ret = class_register(&user_class); if (ret) { pr_err("Could not create device class for user accessible files (err %d)\n", -ret); - class_unregister(&class); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); - goto done; } -done: return ret; } void dev_cleanup(void) { - class_unregister(&class); class_unregister(&user_class); - unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); } diff --git a/drivers/infiniband/hw/hfi1/device.h b/drivers/infiniband/hw/hfi1/device.h index a91bea426ba5..3e2d21770e6c 100644 --- a/drivers/infiniband/hw/hfi1/device.h +++ b/drivers/infiniband/hw/hfi1/device.h @@ -9,7 +9,6 @@ int hfi1_cdev_init(int minor, const char *name, const struct file_operations *fops, struct cdev *cdev, struct device **devp, - bool user_accessible, struct kobject *parent); void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp); const char *class_name(void); diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index 56031becb273..dc548e6802e2 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -1689,7 +1689,7 @@ static int user_add(struct hfi1_devdata *dd) snprintf(name, sizeof(name), "%s_%d", class_name(), dd->unit); ret = hfi1_cdev_init(dd->unit, name, &hfi1_file_ops, &dd->user_cdev, &dd->user_device, - true, &dd->verbs_dev.rdi.ibdev.dev.kobj); + &dd->verbs_dev.rdi.ibdev.dev.kobj); if (ret) user_remove(dd); diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h index 63828240d659..53dab2d54844 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.h +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h @@ -33,7 +33,6 @@ #define IONIC_MAX_QPID 0xffffff #define IONIC_SPEC_HIGH 8 #define IONIC_MAX_PD 1024 -#define IONIC_SPEC_HIGH 8 #define IONIC_SQCMB_ORDER 5 #define IONIC_RQCMB_ORDER 0 diff --git a/drivers/infiniband/hw/irdma/i40iw_hw.c b/drivers/infiniband/hw/irdma/i40iw_hw.c index 60c1f2b1811d..8301938b4543 100644 --- a/drivers/infiniband/hw/irdma/i40iw_hw.c +++ b/drivers/infiniband/hw/irdma/i40iw_hw.c @@ -29,7 +29,9 @@ static u32 i40iw_regs[IRDMA_MAX_REGS] = { I40E_PFHMC_PDINV, I40E_GLHMC_VFPDINV(0), I40E_GLPE_CRITERR, - 0xffffffff /* PFINT_RATEN not used in FPK */ + 0xffffffff, /* PFINT_RATEN not used in FPK */ + 0xffffffff, /* PFHMC_ERRORINFO not used in FPK */ + 0xffffffff /* PFHMC_ERRORDATA not used in FPK */ }; static u32 i40iw_stat_offsets[] = { diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.c b/drivers/infiniband/hw/irdma/icrdma_hw.c index 32f26284a788..b1f1b5485762 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.c +++ b/drivers/infiniband/hw/irdma/icrdma_hw.c @@ -29,6 +29,8 @@ static u32 icrdma_regs[IRDMA_MAX_REGS] = { GLHMC_VFPDINV(0), GLPE_CRITERR, GLINT_RATE(0), + PFHMC_ERRORINFO, + PFHMC_ERRORDATA, }; static u64 icrdma_masks[IRDMA_MAX_MASKS] = { diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.h b/drivers/infiniband/hw/irdma/icrdma_hw.h index d97944ab45da..0acdeda1236d 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.h +++ b/drivers/infiniband/hw/irdma/icrdma_hw.h @@ -40,6 +40,8 @@ #define GLHMC_VFPDINV(_i) (0x00528300 + ((_i) * 4)) /* _i=0...31 */ #define GLPE_CRITERR 0x00534000 #define GLINT_RATE(_INT) (0x0015A000 + ((_INT) * 4)) /* _i=0...2047 */ /* Reset Source: CORER */ +#define PFHMC_ERRORINFO 0x00520400 +#define PFHMC_ERRORDATA 0x00520500 #define ICRDMA_DB_ADDR_OFFSET (8 * 1024 * 1024 - 64 * 1024) diff --git a/drivers/infiniband/hw/irdma/icrdma_if.c b/drivers/infiniband/hw/irdma/icrdma_if.c index 2172a2092e3f..4b451d8482a4 100644 --- a/drivers/infiniband/hw/irdma/icrdma_if.c +++ b/drivers/infiniband/hw/irdma/icrdma_if.c @@ -91,8 +91,12 @@ static void icrdma_iidc_event_handler(struct iidc_rdma_core_dev_info *cdev_info, } } if (event->reg & IRDMAPFINT_OICR_HMC_ERR_M) { - ibdev_err(&iwdev->ibdev, "HMC Error\n"); - iwdev->rf->reset = true; + u32 hmc_errinfo = readl(iwdev->rf->sc_dev.hw_regs[IRDMA_PFHMC_ERRORINFO]); + u32 hmc_errdata = readl(iwdev->rf->sc_dev.hw_regs[IRDMA_PFHMC_ERRORDATA]); + + /* Log diagnostics; do not reset here. */ + ibdev_warn(&iwdev->ibdev, "HMC Error: errinfo=0x%08x errdata=0x%08x\n", + hmc_errinfo, hmc_errdata); } if (event->reg & IRDMAPFINT_OICR_PE_PUSH_M) { ibdev_err(&iwdev->ibdev, "PE Push Error\n"); diff --git a/drivers/infiniband/hw/irdma/irdma.h b/drivers/infiniband/hw/irdma/irdma.h index b5ce515f4ee8..d4530520e39f 100644 --- a/drivers/infiniband/hw/irdma/irdma.h +++ b/drivers/infiniband/hw/irdma/irdma.h @@ -66,6 +66,8 @@ enum irdma_registers { IRDMA_GLHMC_VFPDINV, IRDMA_GLPE_CRITERR, IRDMA_GLINT_RATE, + IRDMA_PFHMC_ERRORINFO, + IRDMA_PFHMC_ERRORDATA, IRDMA_MAX_REGS, /* Must be last entry */ }; diff --git a/drivers/infiniband/hw/irdma/utils.c b/drivers/infiniband/hw/irdma/utils.c index e4037d5ef899..290ad02ed657 100644 --- a/drivers/infiniband/hw/irdma/utils.c +++ b/drivers/infiniband/hw/irdma/utils.c @@ -1168,6 +1168,12 @@ void irdma_free_qp_rsrc(struct irdma_qp *iwqp) iwqp->kqp.dma_mem.va = NULL; kfree(iwqp->kqp.sq_wrid_mem); kfree(iwqp->kqp.rq_wrid_mem); + + if (iwqp->user_mode && iwqp->iwpbl) { + struct irdma_mr *iwmr = iwqp->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } } /** diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index be8c5cf12f7f..04d5af78686b 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -464,6 +464,9 @@ static struct irdma_pbl *irdma_get_pbl(unsigned long va, list_for_each_entry (iwpbl, pbl_list, list) { if (iwpbl->user_base == va) { + struct irdma_mr *iwmr = iwpbl->iwmr; + + refcount_inc(&iwmr->user_ring_refs); list_del(&iwpbl->list); iwpbl->on_list = false; return iwpbl; @@ -1880,6 +1883,11 @@ static void irdma_srq_free_rsrc(struct irdma_pci_f *rf, struct irdma_srq *iwsrq) dma_free_coherent(rf->sc_dev.hw->device, iwsrq->kmem.size, iwsrq->kmem.va, iwsrq->kmem.pa); iwsrq->kmem.va = NULL; + } else { + /* Not called in any failure path, so iwpbl is valid. */ + struct irdma_mr *iwmr = iwsrq->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); } irdma_free_rsrc(rf, rf->allocated_srqs, srq->srq_uk.srq_id); @@ -1902,6 +1910,21 @@ static void irdma_cq_free_rsrc(struct irdma_pci_f *rf, struct irdma_cq *iwcq) iwcq->kmem_shadow.size, iwcq->kmem_shadow.va, iwcq->kmem_shadow.pa); iwcq->kmem_shadow.va = NULL; + } else { + struct irdma_mr *iwmr; + + /* May be called in a failure path before iwpbl is valid. */ + if (iwcq->iwpbl) { + iwmr = iwcq->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } + + if (iwcq->iwpbl_shadow) { + iwmr = iwcq->iwpbl_shadow->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } } irdma_free_rsrc(rf, rf->allocated_cqs, cq->cq_uk.cq_id); @@ -2017,7 +2040,7 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, struct irdma_modify_cq_info info = {}; struct irdma_dma_mem kmem_buf; struct irdma_cq_mr *cqmr_buf; - struct irdma_pbl *iwpbl_buf; + struct irdma_pbl *iwpbl_buf = NULL; struct irdma_device *iwdev; struct irdma_pci_f *rf; struct irdma_cq_buf *cq_buf = NULL; @@ -2128,6 +2151,19 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, goto error; spin_lock_irqsave(&iwcq->lock, flags); + if (udata) { + struct irdma_pbl *old_iwpbl = iwcq->iwpbl; + + /* Only update if the resize was successful. Otherwise, HW is + * still pointing to the old PBL. + */ + iwcq->iwpbl = iwpbl_buf; + if (old_iwpbl) { + struct irdma_mr *old_iwmr = old_iwpbl->iwmr; + + refcount_dec(&old_iwmr->user_ring_refs); + } + } if (cq_buf) { cq_buf->kmem_buf = iwcq->kmem; cq_buf->hw = dev->hw; @@ -2143,6 +2179,11 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, return 0; error: + if (iwpbl_buf) { + struct irdma_mr *iwmr = iwpbl_buf->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } if (!udata) { dma_free_coherent(dev->hw->device, kmem_buf.size, kmem_buf.va, kmem_buf.pa); @@ -2419,6 +2460,11 @@ free_dmem: dma_free_coherent(rf->hw.device, iwsrq->kmem.size, iwsrq->kmem.va, iwsrq->kmem.pa); free_rsrc: + if (iwsrq->user_mode && iwsrq->iwpbl) { + struct irdma_mr *iwmr = iwsrq->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } irdma_free_rsrc(rf, rf->allocated_srqs, iwsrq->srq_num); return err_code; } @@ -2498,6 +2544,8 @@ static int irdma_create_cq(struct ib_cq *ibcq, INIT_LIST_HEAD(&iwcq->resize_list); INIT_LIST_HEAD(&iwcq->cmpl_generated); iwcq->cq_num = cq_num; + iwcq->iwpbl = NULL; + iwcq->iwpbl_shadow = NULL; info.dev = dev; ukinfo->cq_size = max(entries, 4); ukinfo->cq_id = cq_num; @@ -2517,8 +2565,6 @@ static int irdma_create_cq(struct ib_cq *ibcq, struct irdma_ucontext *ucontext; struct irdma_create_cq_req req = {}; struct irdma_cq_mr *cqmr; - struct irdma_pbl *iwpbl; - struct irdma_pbl *iwpbl_shadow; struct irdma_cq_mr *cqmr_shadow; iwcq->user_mode = true; @@ -2532,34 +2578,34 @@ static int irdma_create_cq(struct ib_cq *ibcq, } spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); - iwpbl = irdma_get_pbl((unsigned long)req.user_cq_buf, - &ucontext->cq_reg_mem_list); + iwcq->iwpbl = irdma_get_pbl((unsigned long)req.user_cq_buf, + &ucontext->cq_reg_mem_list); spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags); - if (!iwpbl) { + if (!iwcq->iwpbl) { err_code = -EPROTO; goto cq_free_rsrc; } - cqmr = &iwpbl->cq_mr; + cqmr = &iwcq->iwpbl->cq_mr; if (rf->sc_dev.hw_attrs.uk_attrs.feature_flags & IRDMA_FEATURE_CQ_RESIZE) { spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); - iwpbl_shadow = irdma_get_pbl( + iwcq->iwpbl_shadow = irdma_get_pbl( (unsigned long)req.user_shadow_area, &ucontext->cq_reg_mem_list); spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags); - if (!iwpbl_shadow) { + if (!iwcq->iwpbl_shadow) { err_code = -EPROTO; goto cq_free_rsrc; } - cqmr_shadow = &iwpbl_shadow->cq_mr; + cqmr_shadow = &iwcq->iwpbl_shadow->cq_mr; info.shadow_area_pa = cqmr_shadow->cq_pbl.addr; } else { info.shadow_area_pa = cqmr->shadow; } - if (iwpbl->pbl_allocated) { + if (iwcq->iwpbl->pbl_allocated) { info.virtual_map = true; info.pbl_chunk_size = 1; info.first_pm_pbl_idx = cqmr->cq_pbl.idx; @@ -2801,7 +2847,7 @@ static bool irdma_check_mem_contiguous(u64 *arr, u32 npages, u32 pg_size) u32 pg_idx; for (pg_idx = 0; pg_idx < npages; pg_idx++) { - if ((*arr + (pg_size * pg_idx)) != arr[pg_idx]) + if ((*arr + ((u64)pg_size * pg_idx)) != arr[pg_idx]) return false; } @@ -2834,7 +2880,7 @@ static bool irdma_check_mr_contiguous(struct irdma_pble_alloc *palloc, for (i = 0; i < lvl2->leaf_cnt; i++, leaf++) { arr = leaf->addr; - if ((*start_addr + (i * pg_size * PBLE_PER_PAGE)) != *arr) + if ((*start_addr + ((u64)i * pg_size * PBLE_PER_PAGE)) != *arr) return false; ret = irdma_check_mem_contiguous(arr, leaf->cnt, pg_size); if (!ret) @@ -3362,6 +3408,7 @@ static struct irdma_mr *irdma_alloc_iwmr(struct ib_umem *region, if (!iwmr) return ERR_PTR(-ENOMEM); + refcount_set(&iwmr->user_ring_refs, 1); iwpbl = &iwmr->iwpbl; iwpbl->iwmr = iwmr; iwmr->region = region; @@ -3929,41 +3976,41 @@ static struct ib_mr *irdma_get_dma_mr(struct ib_pd *pd, int acc) * irdma_del_memlist - Deleting pbl list entries for CQ/QP * @iwmr: iwmr for IB's user page addresses * @ucontext: ptr to user context + * + * Return: True if the MR is currently in-use by a QP/CQ/SRQ ring. */ -static void irdma_del_memlist(struct irdma_mr *iwmr, +static bool irdma_del_memlist(struct irdma_mr *iwmr, struct irdma_ucontext *ucontext) { struct irdma_pbl *iwpbl = &iwmr->iwpbl; unsigned long flags; + spinlock_t *lock; + bool in_use = false; switch (iwmr->type) { case IRDMA_MEMREG_TYPE_CQ: - spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); - if (iwpbl->on_list) { - iwpbl->on_list = false; - list_del(&iwpbl->list); - } - spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags); + lock = &ucontext->cq_reg_mem_list_lock; break; case IRDMA_MEMREG_TYPE_QP: - spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - if (iwpbl->on_list) { - iwpbl->on_list = false; - list_del(&iwpbl->list); - } - spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); + lock = &ucontext->qp_reg_mem_list_lock; break; case IRDMA_MEMREG_TYPE_SRQ: - spin_lock_irqsave(&ucontext->srq_reg_mem_list_lock, flags); - if (iwpbl->on_list) { - iwpbl->on_list = false; - list_del(&iwpbl->list); - } - spin_unlock_irqrestore(&ucontext->srq_reg_mem_list_lock, flags); + lock = &ucontext->srq_reg_mem_list_lock; break; default: - break; + return false; } + + spin_lock_irqsave(lock, flags); + if (!refcount_dec_if_one(&iwmr->user_ring_refs)) { + in_use = true; + } else if (iwpbl->on_list) { + iwpbl->on_list = false; + list_del(&iwpbl->list); + } + spin_unlock_irqrestore(lock, flags); + + return in_use; } /** @@ -3986,7 +4033,12 @@ static int irdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) ucontext = rdma_udata_to_drv_context(udata, struct irdma_ucontext, ibucontext); - irdma_del_memlist(iwmr, ucontext); + + /* Do not allow the MR to be unpinned if it is still + * backing a user ring. + */ + if (irdma_del_memlist(iwmr, ucontext)) + return -EBUSY; } goto done; } diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h index 289ebc9b23ca..a1651641eb71 100644 --- a/drivers/infiniband/hw/irdma/verbs.h +++ b/drivers/infiniband/hw/irdma/verbs.h @@ -120,6 +120,7 @@ struct irdma_mr { u64 len; u64 pgaddrmem[IRDMA_MAX_SAVED_PHY_PGADDR]; struct irdma_pbl iwpbl; + refcount_t user_ring_refs; }; struct irdma_srq { @@ -152,6 +153,8 @@ struct irdma_cq { struct list_head resize_list; struct irdma_cq_poll_info cur_cqe; struct list_head cmpl_generated; + struct irdma_pbl *iwpbl; + struct irdma_pbl *iwpbl_shadow; }; struct irdma_cmpl_gen { diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c index 379e65bfcd49..bddb7a257831 100644 --- a/drivers/infiniband/sw/rxe/rxe_mw.c +++ b/drivers/infiniband/sw/rxe/rxe_mw.c @@ -72,13 +72,6 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, return -EINVAL; } - /* C10-72 */ - if (unlikely(qp->pd != to_rpd(mw->ibmw.pd))) { - rxe_dbg_mw(mw, - "attempt to bind type 2 MW with qp with different PD\n"); - return -EINVAL; - } - /* o10-37.2.40 */ if (unlikely(!mr || wqe->wr.wr.mw.length == 0)) { rxe_dbg_mw(mw, @@ -87,10 +80,21 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } } - /* remaining checks only apply to a nonzero MR */ + /* C10-72 */ + if (unlikely(qp->pd != rxe_mw_pd(mw))) { + rxe_dbg_mw(mw, "attempt to bind MW with qp with different PD\n"); + return -EINVAL; + } + if (!mr) return 0; + /* remaining checks only apply to a nonzero MR */ + if (unlikely(qp->pd != mr_pd(mr))) { + rxe_dbg_mw(mw, "attempt to bind MW/QP to MR with different PD\n"); + return -EINVAL; + } + if (unlikely(mr->access & IB_ZERO_BASED)) { rxe_dbg_mw(mw, "attempt to bind MW to zero based MR\n"); return -EINVAL; diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index ff44f9978b71..4fa42f88bcfa 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -329,3 +329,4 @@ module_platform_driver(adc_joystick_driver); MODULE_DESCRIPTION("Input driver for joysticks connected over ADC"); MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c index f1753207429d..d687459a0c80 100644 --- a/drivers/input/keyboard/adc-keys.c +++ b/drivers/input/keyboard/adc-keys.c @@ -202,3 +202,4 @@ module_platform_driver(adc_keys_driver); MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c index 98d5b2ba63fb..89c4d7b2b89e 100644 --- a/drivers/input/touchscreen/colibri-vf50-ts.c +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -372,3 +372,4 @@ module_platform_driver(vf50_touch_driver); MODULE_AUTHOR("Sanchayan Maity"); MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/input/touchscreen/resistive-adc-touch.c b/drivers/input/touchscreen/resistive-adc-touch.c index 68f7e2e28f37..c89edc1280fe 100644 --- a/drivers/input/touchscreen/resistive-adc-touch.c +++ b/drivers/input/touchscreen/resistive-adc-touch.c @@ -300,3 +300,4 @@ module_platform_driver(grts_driver); MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>"); MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index e93bcb5eef70..c0748521dff8 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -3085,20 +3085,44 @@ static void __init free_iommu_resources(void) free_pci_segments(); } -/* SB IOAPIC is always on this device in AMD systems */ -#define IOAPIC_SB_DEVID ((0x00 << 8) | PCI_DEVFN(0x14, 0)) +static bool __init check_sb_ioapic(int devid) +{ + u8 bus = PCI_BUS_NUM(devid); + u8 devfn = devid & 0xff; + u16 val; + + val = read_pci_config_16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + PCI_CLASS_DEVICE); + + /* + * The SB IOAPIC is integrated into the FCH (Southbridge), which is + * exposed as an SMBus or ISA bridge in PCI config space. + */ + return val == PCI_CLASS_SERIAL_SMBUS || val == PCI_CLASS_BRIDGE_ISA; +} -/* SB IOAPIC for Hygon family 18h model 4h is on the device 0xb */ -#define IOAPIC_SB_DEVID_FAM18H_M4H ((0x00 << 8) | PCI_DEVFN(0xb, 0)) +/* + * The Southbridge IOAPIC is assigned a GSI Base of 0 (handling interrupts + * 0 through 23). + */ +static int __init get_sb_ioapic_id(void) +{ + int idx = mp_find_ioapic(0); + + if (idx < 0) + return -ENODEV; + + return mpc_ioapic_id(idx); +} static bool __init check_ioapic_information(void) { const char *fw_bug = FW_BUG; bool ret, has_sb_ioapic; - int idx; + int idx, sb_apicid; has_sb_ioapic = false; - ret = false; + ret = true; /* * If we have map overrides on the kernel command line the @@ -3108,6 +3132,16 @@ static bool __init check_ioapic_information(void) if (cmdline_maps) fw_bug = ""; + sb_apicid = get_sb_ioapic_id(); + if (sb_apicid < 0) { + /* + * Lack of SB IOAPIC registration is not a firmware bug, + * e.g. kernel booted with noapic or noacpi. + */ + fw_bug = ""; + goto out; + } + for (idx = 0; idx < nr_ioapics; idx++) { int devid, id = mpc_ioapic_id(idx); @@ -3116,17 +3150,11 @@ static bool __init check_ioapic_information(void) pr_err("%s: IOAPIC[%d] not in IVRS table\n", fw_bug, id); ret = false; - } else if (devid == IOAPIC_SB_DEVID || - (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && - boot_cpu_data.x86 == 0x18 && - boot_cpu_data.x86_model >= 0x4 && - boot_cpu_data.x86_model <= 0xf && - devid == IOAPIC_SB_DEVID_FAM18H_M4H)) { + } else if (id == sb_apicid && check_sb_ioapic(devid)) { has_sb_ioapic = true; - ret = true; } } - +out: if (!has_sb_ioapic) { /* * We expect the SB IOAPIC to be listed in the IVRS @@ -3137,6 +3165,7 @@ static bool __init check_ioapic_information(void) * device id for the IOAPIC in the system. */ pr_err("%s: No southbridge IOAPIC found\n", fw_bug); + ret = false; } if (!ret) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index a10affb483a4..57b750ebcd3d 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -2956,8 +2956,13 @@ static void arm_smmu_enable_ats(struct arm_smmu_master *master) * ATC invalidation of PASID 0 causes the entire ATC to be flushed. */ arm_smmu_atc_inv_master(master, IOMMU_NO_PASID); - if (pci_enable_ats(pdev, stu)) - dev_err(master->dev, "Failed to enable ATS (STU %zu)\n", stu); + + /* + * Since pci_prepare_ats() has already verified the HW capability + * and programmed the STE, pci_enable_ats() should not fail here. + */ + WARN(pci_enable_ats(pdev, stu), + "%s: Failed to enable ATS (STU %zu)\n", dev_name(master->dev), stu); } static int arm_smmu_enable_pasid(struct arm_smmu_master *master) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 849d06dfe1ae..f39451323553 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -876,8 +876,14 @@ static void iommu_enable_pci_ats(struct device_domain_info *info) if (!pci_ats_page_aligned(pdev)) return; - if (!pci_enable_ats(pdev, VTD_PAGE_SHIFT)) - info->ats_enabled = 1; + /* + * pci_enable_ats() should not fail here because earlier checks + * have already verified support and configuration. + */ + if (WARN_ON(pci_enable_ats(pdev, VTD_PAGE_SHIFT))) + return; + + info->ats_enabled = 1; } static void iommu_disable_pci_ats(struct device_domain_info *info) @@ -3292,7 +3298,10 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev) dev_iommu_priv_set(dev, info); if (pdev && pci_ats_supported(pdev)) { - pci_prepare_ats(pdev, VTD_PAGE_SHIFT); + ret = pci_prepare_ats(pdev, VTD_PAGE_SHIFT); + if (ret) + goto free; + ret = device_rbtree_insert(iommu, info); if (ret) goto free; diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index 0ad5ff431d5b..708baeb29c03 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -720,7 +720,7 @@ static int msm_iommu_probe(struct platform_device *pdev) iommu = devm_kzalloc(&pdev->dev, sizeof(*iommu), GFP_KERNEL); if (!iommu) - return -ENODEV; + return -ENOMEM; iommu->dev = &pdev->dev; INIT_LIST_HEAD(&iommu->ctx_list); diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index ac97dd2868d4..e907c9953142 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -88,7 +88,7 @@ struct dma_iommu_mapping { /* MTK generation one iommu HW only support 4K size mapping */ #define MT2701_IOMMU_PAGE_SHIFT 12 #define MT2701_IOMMU_PAGE_SIZE (1UL << MT2701_IOMMU_PAGE_SHIFT) -#define MT2701_LARB_NR_MAX 3 +#define MT2701_LARB_NR_MAX 4 /* * MTK m4u support 4GB iova address space, and only support 4K page diff --git a/drivers/iommu/riscv/iommu-pci.c b/drivers/iommu/riscv/iommu-pci.c index d82d2b00904c..8abf52dfb4c9 100644 --- a/drivers/iommu/riscv/iommu-pci.c +++ b/drivers/iommu/riscv/iommu-pci.c @@ -109,9 +109,9 @@ static void riscv_iommu_pci_shutdown(struct pci_dev *pdev) } static const struct pci_device_id riscv_iommu_pci_tbl[] = { - {PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_RISCV_IOMMU), 0}, - {PCI_VDEVICE(RIVOS, PCI_DEVICE_ID_RIVOS_RISCV_IOMMU_GA), 0}, - {0,} + { PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_RISCV_IOMMU) }, + { PCI_VDEVICE(RIVOS, PCI_DEVICE_ID_RIVOS_RISCV_IOMMU_GA) }, + { } }; static struct pci_driver riscv_iommu_pci_driver = { diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 99444a1b2ffa..2673954d4577 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2588,7 +2588,7 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) if (err) goto out_fwhandle_free; - acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id, NULL); if (static_branch_likely(&supports_deactivate_key)) gic_acpi_setup_kvm_info(); diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c index 9103feb70ce8..d3433dd3f2b5 100644 --- a/drivers/irqchip/irq-gic-v5-iwb.c +++ b/drivers/irqchip/irq-gic-v5-iwb.c @@ -143,7 +143,7 @@ static int gicv5_iwb_irq_domain_translate(struct irq_domain *d, struct irq_fwspe return -EINVAL; /* - * param[0] is be the wire + * param[0] is the wire * param[1] is the interrupt type */ *hwirq = fwspec->param[0]; @@ -269,6 +269,8 @@ static int gicv5_iwb_device_probe(struct platform_device *pdev) if (IS_ERR(iwb_node)) return PTR_ERR(iwb_node); + acpi_device_clear_deps(&pdev->dev); + return 0; } diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index e9d1795235a6..5dcf404ab4c8 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -1226,11 +1226,19 @@ static struct fwnode_handle *gsi_domain_handle; static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi) { if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE) - return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); + return iort_iwb_handle_fwnode(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); return gsi_domain_handle; } +static acpi_handle gic_v5_get_gsi_handle(u32 gsi) +{ + if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE) + return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); + + return NULL; +} + static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) { struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; @@ -1251,7 +1259,8 @@ static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsig if (ret) goto out_irs; - acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id, + gic_v5_get_gsi_handle); return 0; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index ec70c84e9f91..f6bc29f515fb 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1690,7 +1690,7 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header, return ret; } - acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id, NULL); if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) gicv2m_init(NULL, gic_data[0].domain); diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c index 950bc087e388..84ce24889488 100644 --- a/drivers/irqchip/irq-loongarch-cpu.c +++ b/drivers/irqchip/irq-loongarch-cpu.c @@ -168,7 +168,7 @@ static int __init cpuintc_acpi_init(union acpi_subtable_headers *header, panic("Failed to add irqdomain for LoongArch CPU"); set_handle_irq(&handle_cpu_irq); - acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id, NULL); acpi_set_gsi_to_irq_fallback(lpic_gsi_to_irq); ret = acpi_cascade_irqdomain_init(); diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 84418dbd5a27..0595144116e2 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -384,7 +384,8 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header, if (rc) irq_domain_free_fwnode(fn); else - acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id, + acpi_get_riscv_gsi_handle); return rc; } diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index f38ee74b0397..ce6d80c7f17a 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -426,7 +426,7 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type) } /** - * qcom_pdc_gic_set_type: Configure PDC for the interrupt + * qcom_pdc_gic_secondary_set_type: Configure PDC for the interrupt in secondary mode * * @d: the interrupt data * @type: the interrupt type diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index f3bf59495b68..2d37e00e459d 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -327,9 +327,9 @@ static int pca9532_gpio_set_value(struct gpio_chip *gc, unsigned int offset, struct pca9532_led *led = &data->leds[offset]; if (val) - led->state = PCA9532_ON; - else led->state = PCA9532_OFF; + else + led->state = PCA9532_ON; pca9532_setled(led); @@ -349,7 +349,7 @@ static int pca9532_gpio_get_value(struct gpio_chip *gc, unsigned offset) static int pca9532_gpio_direction_input(struct gpio_chip *gc, unsigned offset) { /* To use as input ensure pin is not driven */ - pca9532_gpio_set_value(gc, offset, 0); + pca9532_gpio_set_value(gc, offset, 1); return 0; } diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c index 7f68d956f694..168df5ecf27b 100644 --- a/drivers/leds/leds-st1202.c +++ b/drivers/leds/leds-st1202.c @@ -31,10 +31,11 @@ #define ST1202_ILED_REG0 0x09 #define ST1202_MAX_LEDS 12 #define ST1202_MAX_PATTERNS 8 -#define ST1202_MILLIS_PATTERN_DUR_MAX 5660 +#define ST1202_MILLIS_PATTERN_DUR_MAX (ST1202_MILLIS_PATTERN_DUR_MIN * U8_MAX) #define ST1202_MILLIS_PATTERN_DUR_MIN 22 #define ST1202_PATTERN_DUR 0x16 #define ST1202_PATTERN_PWM 0x1E +#define ST1202_PATTERN_PWM_FULL 0x0FFF #define ST1202_PATTERN_REP 0x15 struct st1202_led { @@ -85,7 +86,7 @@ static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val) static uint8_t st1202_prescalar_to_miliseconds(unsigned int value) { - return value / ST1202_MILLIS_PATTERN_DUR_MIN - 1; + return value / ST1202_MILLIS_PATTERN_DUR_MIN; } static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num, @@ -127,37 +128,11 @@ static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern, st1202_prescalar_to_miliseconds(value)); } -static void st1202_brightness_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct st1202_led *led = cdev_to_st1202_led(led_cdev); - struct st1202_chip *chip = led->chip; - - guard(mutex)(&chip->lock); - - st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value); -} - -static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev) -{ - struct st1202_led *led = cdev_to_st1202_led(led_cdev); - struct st1202_chip *chip = led->chip; - u8 value = 0; - - guard(mutex)(&chip->lock); - - st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value); - - return value; -} - -static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active) +static int __st1202_channel_set(struct st1202_chip *chip, int led_num, bool active) { u8 chan_low, chan_high; int ret; - guard(mutex)(&chip->lock); - if (led_num <= 7) { ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_LOW, &chan_low); if (ret < 0) @@ -185,6 +160,40 @@ static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active return 0; } +static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active) +{ + guard(mutex)(&chip->lock); + + return __st1202_channel_set(chip, led_num, active); +} + +static void st1202_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct st1202_led *led = cdev_to_st1202_led(led_cdev); + struct st1202_chip *chip = led->chip; + + guard(mutex)(&chip->lock); + + for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) + st1202_pwm_pattern_write(chip, led->led_num, patt, ST1202_PATTERN_PWM_FULL); + st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value); + __st1202_channel_set(chip, led->led_num, !!value); +} + +static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev) +{ + struct st1202_led *led = cdev_to_st1202_led(led_cdev); + struct st1202_chip *chip = led->chip; + u8 value = 0; + + guard(mutex)(&chip->lock); + + st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value); + + return value; +} + static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value) { struct st1202_led *led = cdev_to_st1202_led(ldev); @@ -200,12 +209,16 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev) guard(mutex)(&chip->lock); + ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT); + if (ret != 0) + return ret; + for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) { - ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF); + ret = st1202_pwm_pattern_write(chip, led->led_num, patt, ST1202_PATTERN_PWM_FULL); if (ret != 0) return ret; - ret = st1202_duration_pattern_write(chip, patt, ST1202_MILLIS_PATTERN_DUR_MIN); + ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + patt, 0); if (ret != 0) return ret; } @@ -224,13 +237,19 @@ static int st1202_led_pattern_set(struct led_classdev *ldev, if (len > ST1202_MAX_PATTERNS) return -EINVAL; - guard(mutex)(&chip->lock); - for (int patt = 0; patt < len; patt++) { if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN || pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX) return -EINVAL; + } + guard(mutex)(&chip->lock); + + ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT); + if (ret != 0) + return ret; + + for (int patt = 0; patt < len; patt++) { ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness); if (ret != 0) return ret; @@ -244,6 +263,10 @@ static int st1202_led_pattern_set(struct led_classdev *ldev, if (ret != 0) return ret; + ret = __st1202_channel_set(chip, led->led_num, true); + if (ret != 0) + return ret; + ret = st1202_write_reg(chip, ST1202_CONFIG_REG, (ST1202_CONFIG_REG_PATSR | ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_SHFT)); if (ret != 0) @@ -256,13 +279,19 @@ static int st1202_dt_init(struct st1202_chip *chip) { struct device *dev = &chip->client->dev; struct st1202_led *led; - int err, reg; + int err; + u32 reg; for_each_available_child_of_node_scoped(dev_of_node(dev), child) { err = of_property_read_u32(child, "reg", ®); if (err) return dev_err_probe(dev, err, "Invalid register\n"); + if (reg >= ST1202_MAX_LEDS) + return dev_err_probe(dev, -EINVAL, + "LED reg %u out of range [0, %d]\n", + reg, ST1202_MAX_LEDS - 1); + led = &chip->leds[reg]; led->is_active = true; led->fwnode = of_fwnode_handle(child); @@ -322,11 +351,6 @@ static int st1202_setup(struct st1202_chip *chip) if (ret < 0) return ret; - ret = st1202_write_reg(chip, ST1202_CONFIG_REG, - ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_PATSR); - if (ret < 0) - return ret; - return 0; } diff --git a/drivers/leds/rgb/leds-lp5860-core.c b/drivers/leds/rgb/leds-lp5860-core.c index fd0e2f6e6e0f..e21d5f2302be 100644 --- a/drivers/leds/rgb/leds-lp5860-core.c +++ b/drivers/leds/rgb/leds-lp5860-core.c @@ -204,9 +204,9 @@ int lp5860_device_init(struct device *dev) mutex_lock(&lp->lock); ret = regmap_update_bits(lp->regmap, LP5860_REG_DEV_INITIAL, LP5860_MODE_MASK, LP5860_MODE_1 << LP5860_MODE_SHIFT); + mutex_unlock(&lp->lock); if (ret) goto err_disable; - mutex_unlock(&lp->lock); ret = lp5860_init_dt(lp); if (ret) @@ -215,7 +215,6 @@ int lp5860_device_init(struct device *dev) return 0; err_disable: - mutex_unlock(&lp->lock); lp5860_chip_enable(lp, LP5860_CHIP_DISABLE); return ret; } diff --git a/drivers/leds/rgb/leds-lp5860-spi.c b/drivers/leds/rgb/leds-lp5860-spi.c index 5e0c44854a68..6bf6a625c28a 100644 --- a/drivers/leds/rgb/leds-lp5860-spi.c +++ b/drivers/leds/rgb/leds-lp5860-spi.c @@ -38,6 +38,7 @@ static int lp5860_probe(struct spi_device *spi) struct device *dev = &spi->dev; struct lp5860 *lp5860; unsigned int multi_leds; + int ret; multi_leds = device_get_child_node_count(dev); if (!multi_leds) { @@ -61,7 +62,10 @@ static int lp5860_probe(struct spi_device *spi) "Failed to initialise Regmap.\n"); lp5860->dev = dev; - mutex_init(&lp5860->lock); + + ret = devm_mutex_init(dev, &lp5860->lock); + if (ret) + return ret; spi_set_drvdata(spi, lp5860); @@ -70,10 +74,6 @@ static int lp5860_probe(struct spi_device *spi) static void lp5860_remove(struct spi_device *spi) { - struct lp5860 *lp5860 = spi_get_drvdata(spi); - - mutex_destroy(&lp5860->lock); - lp5860_device_remove(&spi->dev); } diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 829ee4861bf7..de0c4fcd8dfe 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -2219,9 +2219,13 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, * Unprocessed messages are aborted if userspace isn't doing * any processing either. */ + mutex_lock(&adap->lock); if (!is_broadcast && !is_reply && !adap->follower_cnt && - !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) + !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) { + mutex_unlock(&adap->lock); return cec_feature_abort(adap, msg); + } + mutex_unlock(&adap->lock); break; } @@ -2234,10 +2238,12 @@ skip_processing: * Send to the exclusive follower if there is one, otherwise send * to all followers. */ + mutex_lock(&adap->lock); if (adap->cec_follower) cec_queue_msg_fh(adap->cec_follower, msg); else cec_queue_msg_followers(adap, msg); + mutex_unlock(&adap->lock); return 0; } diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 90a98f322f5e..c51e769b4e34 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -5,16 +5,33 @@ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/bitops.h> +#include <linux/bug.h> +#include <linux/cdev.h> +#include <linux/container_of.h> #include <linux/debugfs.h> -#include <linux/errno.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> #include <linux/init.h> -#include <linux/kernel.h> -#include <linux/kmod.h> -#include <linux/mm.h> +#include <linux/kobject.h> +#include <linux/kthread.h> +#include <linux/list.h> +#include <linux/minmax.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/printk.h> +#include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/sprintf.h> #include <linux/string.h> +#include <linux/time.h> #include <linux/types.h> +#include <linux/wait.h> + +#include <asm/page.h> #include "cec-priv.h" @@ -93,7 +110,7 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, minor = find_first_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES); if (minor == CEC_NUM_DEVICES) { mutex_unlock(&cec_devnode_lock); - pr_err("could not get a free minor\n"); + pr_err("Could not get a free minor\n"); return -ENFILE; } @@ -104,19 +121,19 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, devnode->dev.bus = &cec_bus_type; devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor); devnode->dev.release = cec_devnode_release; - dev_set_name(&devnode->dev, "cec%d", devnode->minor); + dev_set_name(&devnode->dev, "%s%d", CEC_NAME, devnode->minor); device_initialize(&devnode->dev); /* Part 2: Initialize and register the character device */ cdev_init(&devnode->cdev, &cec_devnode_fops); devnode->cdev.owner = owner; - kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor); + kobject_set_name(&devnode->cdev.kobj, "%s%d", CEC_NAME, devnode->minor); devnode->registered = true; ret = cdev_device_add(&devnode->cdev, &devnode->dev); if (ret) { devnode->registered = false; - pr_err("%s: cdev_device_add failed\n", __func__); + pr_err("cdev_device_add() failed\n"); goto clr_bit; } @@ -205,19 +222,7 @@ static int cec_error_inj_show(struct seq_file *sf, void *unused) return call_op(adap, error_inj_show, sf); } - -static int cec_error_inj_open(struct inode *inode, struct file *file) -{ - return single_open(file, cec_error_inj_show, inode->i_private); -} - -static const struct file_operations cec_error_inj_fops = { - .open = cec_error_inj_open, - .write = cec_error_inj_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj); #endif struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, @@ -263,12 +268,11 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, mutex_init(&adap->devnode.lock_fhs); mutex_init(&adap->devnode.lock); - adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); + adap->kthread = kthread_run(cec_thread_func, adap, "%s-%s", CEC_NAME, name); if (IS_ERR(adap->kthread)) { - pr_err("cec-%s: kernel_thread() failed\n", name); + pr_err("%s: kthread_run() failed\n", name); res = PTR_ERR(adap->kthread); - kfree(adap); - return ERR_PTR(res); + goto err_free_adap; } #ifdef CONFIG_MEDIA_CEC_RC @@ -278,11 +282,10 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, /* Prepare the RC input device */ adap->rc = rc_allocate_device(RC_DRIVER_SCANCODE); if (!adap->rc) { - pr_err("cec-%s: failed to allocate memory for rc_dev\n", - name); + pr_err("%s: failed to allocate memory for rc_dev\n", name); kthread_stop(adap->kthread); - kfree(adap); - return ERR_PTR(-ENOMEM); + res = -ENOMEM; + goto err_free_adap; } snprintf(adap->input_phys, sizeof(adap->input_phys), @@ -298,9 +301,18 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->rc->allowed_protocols = RC_PROTO_BIT_CEC; adap->rc->priv = adap; adap->rc->map_name = RC_MAP_CEC; - adap->rc->timeout = MS_TO_US(550); + adap->rc->timeout = 550 * USEC_PER_MSEC; #endif return adap; + +err_free_adap: + mutex_destroy(&adap->devnode.lock); + mutex_destroy(&adap->devnode.lock_fhs); + + mutex_destroy(&adap->lock); + + kfree(adap); + return ERR_PTR(res); } EXPORT_SYMBOL_GPL(cec_allocate_adapter); @@ -326,8 +338,7 @@ int cec_register_adapter(struct cec_adapter *adap, res = rc_register_device(adap->rc); if (res) { - pr_err("cec-%s: failed to prepare input device\n", - adap->name); + pr_err("%s: failed to prepare input device\n", adap->name); rc_free_device(adap->rc); adap->rc = NULL; return res; @@ -385,14 +396,23 @@ void cec_delete_adapter(struct cec_adapter *adap) { if (IS_ERR_OR_NULL(adap)) return; + if (adap->kthread_config) kthread_stop(adap->kthread_config); kthread_stop(adap->kthread); + if (adap->ops->adap_free) adap->ops->adap_free(adap); + #ifdef CONFIG_MEDIA_CEC_RC rc_free_device(adap->rc); #endif + + mutex_destroy(&adap->devnode.lock); + mutex_destroy(&adap->devnode.lock_fhs); + + mutex_destroy(&adap->lock); + kfree(adap); } EXPORT_SYMBOL_GPL(cec_delete_adapter); @@ -405,14 +425,14 @@ static int __init cec_devnode_init(void) int ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, CEC_NAME); if (ret < 0) { - pr_warn("cec: unable to allocate major\n"); + pr_warn("Unable to allocate major\n"); return ret; } #ifdef CONFIG_DEBUG_FS - top_cec_dir = debugfs_create_dir("cec", NULL); + top_cec_dir = debugfs_create_dir(CEC_NAME, NULL); if (IS_ERR_OR_NULL(top_cec_dir)) { - pr_warn("cec: Failed to create debugfs cec dir\n"); + pr_warn("Failed to create debugfs " CEC_NAME " dir\n"); top_cec_dir = NULL; } #endif @@ -421,7 +441,7 @@ static int __init cec_devnode_init(void) if (ret < 0) { debugfs_remove_recursive(top_cec_dir); unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); - pr_warn("cec: bus_register failed\n"); + pr_warn("bus_register() failed\n"); return -EIO; } diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index 97ed9654c78a..654c70503dd5 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -7,14 +7,15 @@ * Copyright (C) 2018, Aidilab Srl. */ -#include <linux/module.h> #include <linux/acpi.h> #include <linux/delay.h> #include <linux/dmi.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> +#include <linux/module.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/time.h> /* CEC Framework */ #include <media/cec-notifier.h> @@ -356,7 +357,7 @@ static int secocec_ir_probe(void *priv) cec->ir->allowed_protocols = RC_PROTO_BIT_RC5; cec->ir->priv = cec; cec->ir->map_name = RC_MAP_HAUPPAUGE; - cec->ir->timeout = MS_TO_US(100); + cec->ir->timeout = 100 * USEC_PER_MSEC; /* Clear the status register */ status = smb_rd16(SECOCEC_STATUS_REG_1, &val); diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index 3ed50097262f..fe66336e734f 100644 --- a/drivers/media/cec/platform/tegra/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -458,6 +458,7 @@ static const struct of_device_id tegra_cec_of_match[] = { { .compatible = "nvidia,tegra210-cec", }, {}, }; +MODULE_DEVICE_TABLE(of, tegra_cec_of_match); static struct platform_driver tegra_cec_driver = { .driver = { diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 931e5dc453b9..e1d5c220f738 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -2346,9 +2346,11 @@ static void tpg_fill_params_extras(const struct tpg_data *tpg, (params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); } -static void tpg_fill_plane_extras(const struct tpg_data *tpg, - const struct tpg_draw_params *params, - unsigned p, unsigned h, u8 *vbuf) +/* noinline to work around clang KASAN issues */ +static noinline_for_stack void +tpg_fill_plane_extras(const struct tpg_data *tpg, + const struct tpg_draw_params *params, + unsigned p, unsigned h, u8 *vbuf) { unsigned twopixsize = params->twopixsize; unsigned img_width = params->img_width; @@ -2483,9 +2485,9 @@ static void tpg_fill_plane_extras(const struct tpg_data *tpg, } } -static void tpg_fill_plane_pattern(const struct tpg_data *tpg, - const struct tpg_draw_params *params, - unsigned p, unsigned h, u8 *vbuf) +static noinline_for_stack void +tpg_fill_plane_pattern(const struct tpg_data *tpg, const struct tpg_draw_params *params, + unsigned p, unsigned h, u8 *vbuf) { unsigned twopixsize = params->twopixsize; unsigned img_width = params->img_width; diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index c564485e3bbb..0330e7f0881a 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -906,9 +906,12 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; mutex_unlock(&dev->v4l2_lock); + return 0; err: + rtl2832_sdr_free_urbs(dev); + rtl2832_sdr_free_stream_bufs(dev); rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); @@ -1477,14 +1480,22 @@ static void rtl2832_sdr_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "\n"); - mutex_lock(&dev->vb_queue_lock); + /* + * vb2_video_unregister_device() releases the vb2 queue, which + * triggers rtl2832_sdr_stop_streaming() if streaming is active. + * stop_streaming() uses dev->udev to free URBs and coherent DMA + * stream buffers via usb_free_coherent(), so it must run before + * dev->udev is cleared. vb2_video_unregister_device() locks + * vb_queue_lock internally and stop_streaming() locks v4l2_lock, + * so neither may be held by the caller. + */ + v4l2_device_disconnect(&dev->v4l2_dev); + vb2_video_unregister_device(&dev->vdev); + mutex_lock(&dev->v4l2_lock); - /* No need to keep the urbs around after disconnection */ dev->udev = NULL; - v4l2_device_disconnect(&dev->v4l2_dev); - video_unregister_device(&dev->vdev); mutex_unlock(&dev->v4l2_lock); - mutex_unlock(&dev->vb_queue_lock); + v4l2_device_put(&dev->v4l2_dev); module_put(pdev->dev.parent->driver->owner); } diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 5d173e0ecf42..7ffccba897ae 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -287,6 +287,27 @@ config VIDEO_IMX415 To compile this driver as a module, choose M here: the module will be called imx415. +config VIDEO_IMX471 + tristate "Sony IMX471 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX471 camera. + + To compile this driver as a module, choose M here: the + module will be called imx471. + +config VIDEO_IMX678 + tristate "Sony IMX678 sensor support" + depends on GPIOLIB + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX678 camera. + + To compile this driver as a module, choose M here: the + module will be called imx678. + config VIDEO_MAX9271_LIB tristate diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index e45359efe0e4..d04bd5724552 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -61,6 +61,8 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_IMX412) += imx412.o obj-$(CONFIG_VIDEO_IMX415) += imx415.o +obj-$(CONFIG_VIDEO_IMX678) += imx678.o +obj-$(CONFIG_VIDEO_IMX471) += imx471.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o obj-$(CONFIG_VIDEO_KS0127) += ks0127.o diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index dd991c2ee700..f51f9b987759 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -1290,7 +1290,7 @@ static int alvium_set_ctrl_auto_exposure(struct alvium_dev *alvium, bool on) struct device *dev = &alvium->i2c_client->dev; int ret; - ret = alvium_write_hshake(alvium, REG_BCRM_WHITE_BALANCE_AUTO_RW, + ret = alvium_write_hshake(alvium, REG_BCRM_EXPOSURE_AUTO_RW, on ? 0x02 : 0x00); if (ret) { dev_err(dev, "Fail to set autoexposure reg\n"); diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c index fe9e59ac311c..d4a3b9c3bab1 100644 --- a/drivers/media/i2c/cvs/core.c +++ b/drivers/media/i2c/cvs/core.c @@ -31,6 +31,7 @@ #define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ #define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ +#define PCI_DEVICE_ID_INTEL_IPU8 0xd719 /* NVL */ /* * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. @@ -39,6 +40,7 @@ static const struct pci_device_id icvs_ipu7_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU8) }, { } }; @@ -962,6 +964,7 @@ static const struct acpi_device_id intel_cvs_acpi_match[] = { { "INTC10DE" }, /* LNL */ { "INTC10E0" }, /* ARL */ { "INTC10E1" }, /* PTL */ + { "INTC10FA" }, /* NVL */ { } }; MODULE_DEVICE_TABLE(acpi, intel_cvs_acpi_match); diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c new file mode 100644 index 000000000000..6d358b11e96d --- /dev/null +++ b/drivers/media/i2c/imx471.c @@ -0,0 +1,957 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx471.c - imx471 sensor driver + * + * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2026 Kate Hsuan <hpa@redhat.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-fwnode.h> + +#define IMX471_REG_MODE_SELECT CCI_REG8(0x0100) +#define IMX471_MODE_STANDBY 0x00 +#define IMX471_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX471_REG_CHIP_ID CCI_REG16(0x0016) +#define IMX471_CHIP_ID 0x0471 + +/* V_TIMING internal */ +#define IMX471_REG_FLL CCI_REG16(0x0340) +#define IMX471_FLL_MAX 0xffff + +/* Exposure control */ +#define IMX471_REG_EXPOSURE CCI_REG16(0x0202) +#define IMX471_EXPOSURE_MIN 1 +#define IMX471_EXPOSURE_STEP 1 +#define IMX471_EXPOSURE_DEFAULT 1270 + +/* Default exposure margin */ +#define IMX471_EXPOSURE_MARGIN 18 + +/* Analog gain control */ +#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204) +#define IMX471_ANA_GAIN_MIN 0 +#define IMX471_ANA_GAIN_MAX 800 +#define IMX471_ANA_GAIN_STEP 1 +#define IMX471_ANA_GAIN_DEFAULT 0 + +/* Digital gain control */ +#define IMX471_REG_DPGA_USE_GLOBAL_GAIN CCI_REG16(0x3ff9) +#define IMX471_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) +#define IMX471_DGTL_GAIN_MIN 256 +#define IMX471_DGTL_GAIN_MAX 4095 +#define IMX471_DGTL_GAIN_STEP 1 +#define IMX471_DGTL_GAIN_DEFAULT 256 + +/* HFLIP and VFLIP control */ +#define IMX471_REG_ORIENTATION CCI_REG8(0x0101) + +/* Test Pattern Control */ +#define IMX471_REG_TEST_PATTERN CCI_REG8(0x0600) + +/* default link frequency and external clock */ +#define IMX471_LINK_FREQ_DEFAULT 200000000LL +#define IMX471_EXT_CLK 19200000 + +/* PLL */ +#define IMX471_REG_VTPXCK_DIV CCI_REG8(0x0301) +#define IMX471_REG_VTSYCK_DIV CCI_REG8(0x0303) +#define IMX471_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) +#define IMX471_REG_PLL_VT_MPY CCI_REG16(0x0306) +#define IMX471_REG_OPPXCK_DIV CCI_REG8(0x0309) +#define IMX471_REG_OPSYCK_DIV CCI_REG8(0x030b) +#define IMX471_REG_PLL_MULT_DRIV CCI_REG8(0x0310) +#define IMX471_PLL_SINGLE 0 +#define IMX471_PLL_DUAL 1 + +/* IMX471 native and active pixel array size */ +#define IMX471_NATIVE_WIDTH 4672 +#define IMX471_NATIVE_HEIGHT 3512 +#define IMX471_PIXEL_ARRAY_LEFT 8 +#define IMX471_PIXEL_ARRAY_TOP 8 +#define IMX471_PIXEL_ARRAY_WIDTH 4656 +#define IMX471_PIXEL_ARRAY_HEIGHT 3496 + +#define IMX471_REG_EXCK_FREQ CCI_REG16(0x0136) + +#define IMX471_REG_CSI_DATA_FORMAT CCI_REG16(0x0112) +#define IMX471_CSI_DATA_FORMAT_RAW10 0x0a0a + +#define IMX471_REG_CSI_LANE_MODE CCI_REG8(0x0114) +#define IMX471_CSI_2_LANE_MODE 1 +#define IMX471_CSI_4_LANE_MODE 3 + +#define IMX471_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX471_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX471_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX471_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX471_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define IMX471_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) +#define IMX471_REG_X_EVEN_INC CCI_REG8(0x0381) +#define IMX471_REG_X_ODD_INC CCI_REG8(0x0383) +#define IMX471_REG_Y_EVEN_INC CCI_REG8(0x0385) +#define IMX471_REG_Y_ODD_INC CCI_REG8(0x0387) + +#define IMX471_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX471_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX471_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) +#define IMX471_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) + +/* Binning mode */ +#define IMX471_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX471_BINNING_NONE 0 +#define IMX471_BINNING_ENABLE 1 +#define IMX471_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX471_REG_BINNING_WEIGHTING CCI_REG8(0x0902) + +#define to_imx471(_sd) container_of_const(_sd, struct imx471, sd) + +static const char * const imx471_supply_name[] = { + "vana", +}; + +struct imx471_mode { + u32 width; + u32 height; + + /* V-timing */ + u32 fll_def; + u32 fll_min; + + /* H-timing */ + u32 llp; + + const struct cci_reg_sequence *default_mode_regs; + unsigned int default_mode_regs_length; +}; + +struct imx471 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *exposure; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx471_supply_name)]; + struct clk *img_clk; + + struct device *dev; + struct regmap *regmap; +}; + +static const struct cci_reg_sequence imx471_global_regs[] = { + { IMX471_REG_EXCK_FREQ, 0x1333 }, + { CCI_REG8(0x3c7e), 0x08 }, + { CCI_REG8(0x3c7f), 0x05 }, + { CCI_REG8(0x3e35), 0x00 }, + { CCI_REG8(0x3e36), 0x00 }, + { CCI_REG8(0x3e37), 0x00 }, + { CCI_REG8(0x3f7f), 0x01 }, + { CCI_REG8(0x4431), 0x04 }, + { CCI_REG8(0x531c), 0x01 }, + { CCI_REG8(0x531d), 0x02 }, + { CCI_REG8(0x531e), 0x04 }, + { CCI_REG8(0x5928), 0x00 }, + { CCI_REG8(0x5929), 0x2f }, + { CCI_REG8(0x592a), 0x00 }, + { CCI_REG8(0x592b), 0x85 }, + { CCI_REG8(0x592c), 0x00 }, + { CCI_REG8(0x592d), 0x32 }, + { CCI_REG8(0x592e), 0x00 }, + { CCI_REG8(0x592f), 0x88 }, + { CCI_REG8(0x5930), 0x00 }, + { CCI_REG8(0x5931), 0x3d }, + { CCI_REG8(0x5932), 0x00 }, + { CCI_REG8(0x5933), 0x93 }, + { CCI_REG8(0x5938), 0x00 }, + { CCI_REG8(0x5939), 0x24 }, + { CCI_REG8(0x593a), 0x00 }, + { CCI_REG8(0x593b), 0x7a }, + { CCI_REG8(0x593c), 0x00 }, + { CCI_REG8(0x593d), 0x24 }, + { CCI_REG8(0x593e), 0x00 }, + { CCI_REG8(0x593f), 0x7a }, + { CCI_REG8(0x5940), 0x00 }, + { CCI_REG8(0x5941), 0x2f }, + { CCI_REG8(0x5942), 0x00 }, + { CCI_REG8(0x5943), 0x85 }, + { CCI_REG8(0x5f0e), 0x6e }, + { CCI_REG8(0x5f11), 0xc6 }, + { CCI_REG8(0x5f17), 0x5e }, + { CCI_REG8(0x7990), 0x01 }, + { CCI_REG8(0x7993), 0x5d }, + { CCI_REG8(0x7994), 0x5d }, + { CCI_REG8(0x7995), 0xa1 }, + { CCI_REG8(0x799a), 0x01 }, + { CCI_REG8(0x799d), 0x00 }, + { CCI_REG8(0x8169), 0x01 }, + { CCI_REG8(0x8359), 0x01 }, + { CCI_REG8(0x9302), 0x1e }, + { CCI_REG8(0x9306), 0x1f }, + { CCI_REG8(0x930a), 0x26 }, + { CCI_REG8(0x930e), 0x23 }, + { CCI_REG8(0x9312), 0x23 }, + { CCI_REG8(0x9316), 0x2c }, + { CCI_REG8(0x9317), 0x19 }, + { CCI_REG8(0xb046), 0x01 }, + { CCI_REG8(0xb048), 0x01 }, +}; + +static const struct cci_reg_sequence mode_1928x1088_regs[] = { + { IMX471_REG_CSI_DATA_FORMAT, IMX471_CSI_DATA_FORMAT_RAW10 }, + { IMX471_REG_CSI_LANE_MODE, IMX471_CSI_4_LANE_MODE }, + { IMX471_REG_X_ADD_STA, 8 }, + { IMX471_REG_Y_ADD_STA, 408 }, + { IMX471_REG_X_ADD_END, 4647 }, + { IMX471_REG_Y_ADD_END, 3051 }, + { IMX471_REG_X_EVEN_INC, 1 }, + { IMX471_REG_X_ODD_INC, 1 }, + { IMX471_REG_Y_EVEN_INC, 1 }, + { IMX471_REG_Y_ODD_INC, 1 }, + { IMX471_REG_BINNING_MODE, IMX471_BINNING_ENABLE }, + { IMX471_REG_BINNING_TYPE, 0x22 }, + { IMX471_REG_BINNING_WEIGHTING, 0x08 }, + { IMX471_REG_DIG_CROP_X_OFFSET, 208 }, + { IMX471_REG_DIG_CROP_Y_OFFSET, 108 }, + { IMX471_REG_DIG_CROP_WIDTH, 1928 }, + { IMX471_REG_DIG_CROP_HEIGHT, 1088 }, + { IMX471_REG_X_OUTPUT_SIZE, 1928 }, + { IMX471_REG_Y_OUTPUT_SIZE, 1088 }, + { IMX471_REG_VTPXCK_DIV, 0x06 }, + { IMX471_REG_VTSYCK_DIV, 0x02 }, + { IMX471_REG_PREPLLCK_VT_DIV, 0x02 }, + { IMX471_REG_PLL_VT_MPY, 0x0079 }, + { IMX471_REG_OPSYCK_DIV, 0x01 }, + { CCI_REG8(0x030d), 0x02 }, + { CCI_REG8(0x030e), 0x00 }, + { CCI_REG8(0x030f), 0x53 }, + { IMX471_REG_PLL_MULT_DRIV, IMX471_PLL_DUAL }, + { CCI_REG8(0x3f4c), 0x81 }, + { CCI_REG8(0x3f4d), 0x81 }, + { CCI_REG8(0x3f78), 0x01 }, + { CCI_REG8(0x3f79), 0x31 }, + { CCI_REG8(0x3ffe), 0x00 }, + { CCI_REG8(0x3fff), 0x8a }, + { CCI_REG8(0x5f0a), 0xb6 }, +}; + +static const char * const imx471_test_pattern_menu[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", +}; + +static const s64 link_freq_menu_items[] = { + IMX471_LINK_FREQ_DEFAULT, +}; + +/* + * The Bayer formats for the flipping. + * - no flip + * - h flip + * - v flip + * - h and v flips + */ +static const u32 imx471_hv_flips_bayer_order[] = { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static const struct imx471_mode imx471_modes[] = { + { + .width = 1928, + .height = 1088, + .fll_def = 1308, + .fll_min = 1308, + .llp = 2328, + .default_mode_regs = mode_1928x1088_regs, + .default_mode_regs_length = ARRAY_SIZE(mode_1928x1088_regs), + }, +}; + +static int imx471_get_regulators(struct device *dev, struct imx471 *sensor) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(imx471_supply_name); i++) + sensor->supplies[i].supply = imx471_supply_name[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx471_supply_name), + sensor->supplies); +} + +static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx471 *sensor = container_of_const(ctrl->handler, + struct imx471, + ctrl_handler); + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->sd); + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + int ret; + + if (ctrl->id == V4L2_CID_VBLANK) { + s64 exposure_max = format->height + ctrl->val - + IMX471_EXPOSURE_MARGIN; + ret = __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, + sensor->exposure->step, + exposure_max); + if (ret) + return ret; + } + + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(sensor->regmap, IMX471_REG_ANALOG_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = cci_write(sensor->regmap, IMX471_REG_DIG_GAIN_GLOBAL, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE, + ctrl->val, &ret); + break; + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = cci_write(sensor->regmap, IMX471_REG_FLL, + format->height + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN: + ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN, + ctrl->val, NULL); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = cci_write(sensor->regmap, IMX471_REG_ORIENTATION, + sensor->hflip->val | sensor->vflip->val << 1, + NULL); + break; + default: + ret = -EINVAL; + dev_err(sensor->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(sensor->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx471_ctrl_ops = { + .s_ctrl = imx471_set_ctrl, +}; + +static u32 imx471_get_format_code(struct imx471 *sensor) +{ + unsigned int i; + + i = (sensor->vflip->val ? 2 : 0) | (sensor->hflip->val ? 1 : 0); + + return imx471_hv_flips_bayer_order[i]; +} + +static int imx471_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx471 *sensor = to_imx471(sd); + + if (code->index >= (ARRAY_SIZE(imx471_hv_flips_bayer_order) / 4)) + return -EINVAL; + + code->code = imx471_get_format_code(sensor); + + return 0; +} + +static int imx471_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(imx471_modes)) + return -EINVAL; + + fse->min_width = imx471_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = imx471_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx471_update_pad_format(struct imx471 *sensor, + const struct imx471_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.code = imx471_get_format_code(sensor); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int imx471_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx471 *sensor = to_imx471(sd); + const struct imx471_mode *mode; + int h_blank, ret; + + mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), + width, height, fmt->format.width, + fmt->format.height); + + imx471_update_pad_format(sensor, mode, fmt); + + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (media_entity_is_streaming(&sensor->sd.entity)) + return -EBUSY; + + ret = __v4l2_ctrl_modify_range(sensor->vblank, + mode->fll_min - mode->height, + IMX471_FLL_MAX - mode->height, + 1, + mode->fll_def - mode->height); + if (ret) + return ret; + + h_blank = mode->llp - mode->width; + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. + */ + return __v4l2_ctrl_modify_range(sensor->hblank, h_blank, + h_blank, 1, h_blank); +} + +static int imx471_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); + break; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX471_NATIVE_WIDTH; + sel->r.height = IMX471_NATIVE_HEIGHT; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = IMX471_PIXEL_ARRAY_TOP; + sel->r.left = IMX471_PIXEL_ARRAY_LEFT; + sel->r.width = IMX471_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX471_PIXEL_ARRAY_HEIGHT; + return 0; + } + + return -EINVAL; +} + +static int imx471_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = imx471_modes[0].width, + .height = imx471_modes[0].height, + }, + }; + + return imx471_set_pad_format(sd, sd_state, &fmt); +} + +static int imx471_identify_module(struct imx471 *sensor) +{ + int ret; + u64 val; + + ret = cci_read(sensor->regmap, IMX471_REG_CHIP_ID, &val, NULL); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to read chip id\n"); + + if (val != IMX471_CHIP_ID) + return dev_err_probe(sensor->dev, -EIO, + "chip id mismatch: %x!=%llx\n", + IMX471_CHIP_ID, val); + + return 0; +} + +static int imx471_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx471 *sensor = to_imx471(sd); + + clk_disable_unprepare(sensor->img_clk); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + + return 0; +} + +static int imx471_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx471 *sensor = to_imx471(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->img_clk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + dev_err(dev, "failed to enable imaging clock: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + + usleep_range(10000, 15000); + + return 0; +} + +static int imx471_enable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct imx471 *sensor = to_imx471(sd); + const struct imx471_mode *mode; + struct v4l2_mbus_framefmt *fmt; + int ret; + + ret = pm_runtime_resume_and_get(sensor->dev); + if (ret) + return ret; + + ret = imx471_identify_module(sensor); + if (ret) + goto error_powerdown; + + ret = cci_multi_reg_write(sensor->regmap, imx471_global_regs, + ARRAY_SIZE(imx471_global_regs), NULL); + if (ret) { + dev_err(sensor->dev, "failed to set global settings: %d\n", + ret); + goto error_powerdown; + } + + fmt = v4l2_subdev_state_get_format(state, 0); + mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), + width, height, fmt->width, fmt->height); + + ret = cci_multi_reg_write(sensor->regmap, mode->default_mode_regs, + mode->default_mode_regs_length, NULL); + if (ret) { + dev_err(sensor->dev, "failed to set mode: %d\n", ret); + goto error_powerdown; + } + + ret = cci_write(sensor->regmap, IMX471_REG_DPGA_USE_GLOBAL_GAIN, 1, + NULL); + if (ret) + goto error_powerdown; + + ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); + if (ret) + goto error_powerdown; + + ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, + IMX471_MODE_STREAMING, NULL); + if (ret) + goto error_powerdown; + + __v4l2_ctrl_grab(sensor->vflip, true); + __v4l2_ctrl_grab(sensor->hflip, true); + + return ret; + +error_powerdown: + pm_runtime_put(sensor->dev); + + return ret; +} + +static int imx471_disable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct imx471 *sensor = to_imx471(sd); + int ret; + + ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, + IMX471_MODE_STANDBY, NULL); + pm_runtime_put(sensor->dev); + + if (ret) + dev_err(sensor->dev, + "failed to disable stream with return value: %d\n", + ret); + + __v4l2_ctrl_grab(sensor->vflip, false); + __v4l2_ctrl_grab(sensor->hflip, false); + + return 0; +} + +static const struct v4l2_subdev_video_ops imx471_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx471_pad_ops = { + .enum_mbus_code = imx471_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx471_set_pad_format, + .get_selection = imx471_get_selection, + .enum_frame_size = imx471_enum_frame_size, + .enable_streams = imx471_enable_stream, + .disable_streams = imx471_disable_stream, +}; + +static const struct v4l2_subdev_ops imx471_subdev_ops = { + .video = &imx471_video_ops, + .pad = &imx471_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx471_internal_ops = { + .init_state = imx471_init_state, +}; + +static int imx471_init_controls(struct imx471 *sensor) +{ + const struct imx471_mode *mode = &imx471_modes[0]; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_ctrl *link_freq; + s64 exposure_max, hblank; + u64 pixel_rate; + int ret; + + ret = v4l2_fwnode_device_parse(sensor->dev, &props); + if (ret) { + dev_err(sensor->dev, "failed to parse fwnode: %d\n", ret); + return ret; + } + + ctrl_hdlr = &sensor->ctrl_handler; + v4l2_ctrl_handler_init(ctrl_hdlr, 12); + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx471_ctrl_ops, &props); + + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx471_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, + link_freq_menu_items); + + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = div_u64(IMX471_LINK_FREQ_DEFAULT * 2 * 4, 10); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + + sensor->vblank = v4l2_ctrl_new_std(ctrl_hdlr, + &imx471_ctrl_ops, + V4L2_CID_VBLANK, + mode->fll_min - mode->height, + IMX471_FLL_MAX - mode->height, + 1, + mode->fll_def - mode->height); + + hblank = mode->llp - mode->width; + sensor->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + + /* fll >= exposure time + adjust parameter (default value is 18) */ + exposure_max = mode->fll_def - IMX471_EXPOSURE_MARGIN; + sensor->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX471_EXPOSURE_MIN, exposure_max, + IMX471_EXPOSURE_STEP, + IMX471_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX471_ANA_GAIN_MIN, IMX471_ANA_GAIN_MAX, + IMX471_ANA_GAIN_STEP, IMX471_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX471_DGTL_GAIN_MIN, IMX471_DGTL_GAIN_MAX, + IMX471_DGTL_GAIN_STEP, IMX471_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx471_test_pattern_menu) - 1, + 0, 0, imx471_test_pattern_menu); + + sensor->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + sensor->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (ctrl_hdlr->error) { + dev_err(sensor->dev, "%s control init failed: %d\n", + __func__, ctrl_hdlr->error); + goto error; + } + + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ctrl_hdlr->error; +} + +static int imx471_check_hwcfg(struct imx471 *sensor) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep, *fwnode = dev_fwnode(sensor->dev); + unsigned long link_freq_bitmap; + struct clk *clk; + int ret; + + clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(sensor->dev, PTR_ERR(clk), + "can't get clock frequency\n"); + + if (clk_get_rate(clk) != IMX471_EXT_CLK) + return dev_err_probe(sensor->dev, -EINVAL, + "external clock %lu is not supported\n", + clk_get_rate(clk)); + + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(sensor->dev, ret, + "parsing endpoint failed\n"); + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(sensor->dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + goto done_endpoint_free; + } + + ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int imx471_probe(struct i2c_client *client) +{ + struct imx471 *sensor; + int ret; + + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return dev_err_probe(&client->dev, -ENOMEM, + "failed to allocate memory\n"); + + sensor->dev = &client->dev; + + ret = imx471_check_hwcfg(sensor); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to check hwcfg: %d\n", ret); + + ret = imx471_get_regulators(sensor->dev, sensor); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to get regulators\n"); + + sensor->reset_gpio = devm_gpiod_get_optional(sensor->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset_gpio), + "failed to get reset gpio\n"); + + sensor->img_clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); + if (IS_ERR(sensor->img_clk)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->img_clk), + "failed to get imaging clock\n"); + + v4l2_i2c_subdev_init(&sensor->sd, client, &imx471_subdev_ops); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->regmap), + "failed to initialize CCI\n"); + + ret = imx471_power_on(sensor->dev); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to power on\n"); + + ret = imx471_identify_module(sensor); + if (ret) { + dev_err_probe(sensor->dev, ret, "failed to find sensor: %d\n", + ret); + goto error_power_off; + } + + ret = imx471_init_controls(sensor); + if (ret) { + dev_err_probe(sensor->dev, ret, "failed to init controls: %d\n", + ret); + goto error_power_off; + } + + sensor->sd.internal_ops = &imx471_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) { + dev_err_probe(sensor->dev, ret, + "failed to init entity pads: %d\n", ret); + goto error_v4l2_ctrl_handler_free; + } + + sensor->sd.state_lock = sensor->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret < 0) { + dev_err_probe(sensor->dev, ret, "failed to init subdev: %d\n", + ret); + goto error_media_entity_pm; + } + + pm_runtime_set_active(sensor->dev); + pm_runtime_enable(sensor->dev); + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret < 0) + goto error_v4l2_subdev_cleanup; + + pm_runtime_idle(sensor->dev); + + return 0; + +error_v4l2_subdev_cleanup: + pm_runtime_disable(sensor->dev); + pm_runtime_set_suspended(sensor->dev); + v4l2_subdev_cleanup(&sensor->sd); + +error_media_entity_pm: + media_entity_cleanup(&sensor->sd.entity); + +error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); + +error_power_off: + imx471_power_off(sensor->dev); + + return ret; +} + +static void imx471_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + + if (!pm_runtime_status_suspended(&client->dev)) { + imx471_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx471_pm_ops, imx471_power_off, + imx471_power_on, NULL); + +static const struct acpi_device_id imx471_acpi_ids[] __maybe_unused = { + { "SONY471A" }, + { "TBE20A0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, imx471_acpi_ids); + +static struct i2c_driver imx471_i2c_driver = { + .driver = { + .name = "imx471", + .acpi_match_table = ACPI_PTR(imx471_acpi_ids), + .pm = pm_sleep_ptr(&imx471_pm_ops), + }, + .probe = imx471_probe, + .remove = imx471_remove, +}; +module_i2c_driver(imx471_i2c_driver); + +MODULE_AUTHOR("Jimmy Su <jimmy.su@intel.com>"); +MODULE_AUTHOR("Serin Yeh <serin.yeh@intel.com>"); +MODULE_AUTHOR("Kate Hsuan <hpa@redhat.com>"); +MODULE_DESCRIPTION("Sony imx471 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/imx678.c b/drivers/media/i2c/imx678.c new file mode 100644 index 000000000000..0efbf43d2fe6 --- /dev/null +++ b/drivers/media/i2c/imx678.c @@ -0,0 +1,1447 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 driver for Sony IMX678 + * + * Diagonal 8.86 mm (Type 1/1.8) CMOS image sensor with 8.40 M effective pixels. + * + * Copyright (C) 2026 Ideas On Board Oy. + * + * Based on Sony IMX678 driver prepared by Will Whang & Soho Enterprise Ltd. + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/media-bus-format.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-rect.h> +#include <media/v4l2-subdev.h> + +/* Standby or streaming mode */ +#define IMX678_REG_MODE_SELECT CCI_REG8(0x3000) +#define IMX678_MODE_STANDBY 0x01 +#define IMX678_MODE_STREAMING 0x00 +#define IMX678_STREAM_DELAY_US 25000 +#define IMX678_STREAM_DELAY_RANGE_US 1000 + +/* XVS/XHS sync control */ +#define IMX678_REG_XMSTA CCI_REG8(0x3002) +#define IMX678_REG_XXS_DRV CCI_REG8(0x30a6) +#define IMX678_REG_XXS_OUTSEL CCI_REG8(0x30a4) + +/* Clk selection */ +#define IMX678_REG_INCK_SEL CCI_REG8(0x3014) + +/* Link Speed */ +#define IMX678_REG_DATARATE_SEL CCI_REG8(0x3015) + +/* Lane Count */ +#define IMX678_REG_LANEMODE CCI_REG8(0x3040) + +/* + * The internal readout clock runs at 74.25 MHz. In one cycle the AD reads 8 + * pixels, thus giving us a rate of 74.25 * 8 = 594 MPix/s + */ +#define IMX678_PIXEL_RATE 594000000 +#define IMX678_PIX_PER_CLK 8 + +/* VMAX - Frame Length in Lines */ +#define IMX678_REG_VMAX CCI_REG24_LE(0x3028) +#define IMX678_VMAX_MAX 0xfffff +#define IMX678_VMAX_DEFAULT 2250 + +/* HMAX - Line Length in Cycles (8 Pixels) */ +#define IMX678_REG_HMAX CCI_REG16_LE(0x302c) +#define IMX678_HMAX_MAX 0xffff + +/* SHR internal */ +#define IMX678_REG_SHR CCI_REG24_LE(0x3050) +#define IMX678_SHR_MIN 8 + +/* Exposure control */ +#define IMX678_EXPOSURE_MIN 2 +#define IMX678_EXPOSURE_STEP 1 +#define IMX678_EXPOSURE_DEFAULT 1000 + +/* + * Analogue gain control + * Range is from 0 to 100 (0dB - 30dB) with 0.3dB step size + * Values from 101 to 240 are valid but correspond to additional digital gain + * (0.3dB - 42dB) so don't expose it to userspace + */ +#define IMX678_REG_GAIN CCI_REG16_LE(0x3070) +#define IMX678_ANA_GAIN_MIN_NORMAL 0 +#define IMX678_ANA_GAIN_MAX_NORMAL 100 +#define IMX678_ANA_GAIN_STEP 1 +#define IMX678_ANA_GAIN_DEFAULT 0 + +/* Crop */ +#define IMX678_REG_WINMODE CCI_REG8(0x3018) +#define IMX678_REG_PIX_HST CCI_REG16_LE(0x303c) +#define IMX678_REG_PIX_HWIDTH CCI_REG16_LE(0x303e) +#define IMX678_REG_PIX_VST CCI_REG16_LE(0x3044) +#define IMX678_REG_PIX_VWIDTH CCI_REG16_LE(0x3046) + +/* Flip */ +#define IMX678_REG_WINMODEH CCI_REG8(0x3020) +#define IMX678_REG_WINMODEV CCI_REG8(0x3021) + +/* Sensor Identification */ +#define IMX678_REG_MONOCHROME CCI_REG8(0x4d18) +#define IMX678_TYPE BIT(0) +#define IMX678_REG_MODULE_ID CCI_REG16_LE(0x4d1c) +#define IMX678_ID 0x02a6 +#define IMX678_MODULE_ID_DELAY 80000 + +/* Common configuration registers */ +#define IMX678_REG_WDMODE CCI_REG8(0x301a) +#define IMX678_REG_ADDMODE CCI_REG8(0x301b) +#define IMX678_REG_THIN_V_EN CCI_REG8(0x301c) +#define IMX678_REG_VCMODE CCI_REG8(0x301e) +#define IMX678_REG_ADBIT CCI_REG8(0x3022) +#define IMX678_REG_MDBIT CCI_REG8(0x3023) +#define IMX678_REG_GAIN_PGC_FIDMD CCI_REG8(0x3400) + +/* Test pattern generator */ +#define IMX678_REG_TPG_EN_DUOUT CCI_REG8(0x30e0) +#define IMX678_REG_TPG_PATSEL_DUOUT CCI_REG8(0x30e2) +#define IMX678_TPG_ALL_000 0 +#define IMX678_TPG_ALL_FFF 1 +#define IMX678_TPG_ALL_555 2 +#define IMX678_TPG_ALL_AAA 3 +#define IMX678_TPG_TOG_555_AAA 4 +#define IMX678_TPG_TOG_AAA_555 5 +#define IMX678_TPG_TOG_000_555 6 +#define IMX678_TPG_TOG_555_000 7 +#define IMX678_TPG_TOG_000_FFF 8 +#define IMX678_TPG_TOG_FFF_000 9 +#define IMX678_TPG_H_COLOR_BARS 10 +#define IMX678_TPG_V_COLOR_BARS 11 +#define IMX678_REG_TPG_COLORWIDTH CCI_REG8(0x30e4) +#define IMX678_TPG_COLORWIDTH_80PIX 0 +#define IMX678_TPG_COLORWIDTH_160PIX 1 +#define IMX678_TPG_COLORWIDTH_320PIX 2 +#define IMX678_TPG_COLORWIDTH_640PIX 3 + +#define IMX678_REG_INTERFACE_SEL CCI_REG8(0x4e3c) +#define IMX678_INTERFACE_2L_4L 0x07 +#define IMX678_INTERFACE_8L_2x4L 0x7f + +/* Minimum output resolution */ +#define IMX678_PIXEL_ARRAY_MIN_WIDTH 1040 +#define IMX678_PIXEL_ARRAY_MIN_HEIGHT 956 + +/* Sensor windowing register alignment */ +#define IMX678_CROP_HWIDTH_ALIGN 16 +#define IMX678_CROP_VWIDTH_ALIGN 4 +#define IMX678_CROP_HST_ALIGN 4 +#define IMX678_CROP_VST_ALIGN 4 + +/* Subdev pads */ +#define IMX678_SOURCE_PAD 0 + +/* IMX678 native and active pixel array size. */ +static const struct v4l2_rect imx678_native_area = { + .top = 0, + .left = 0, + .width = 3857, + .height = 2201, +}; + +static const struct v4l2_rect imx678_active_area = { + .top = 20, + .left = 0, + .width = 3856, + .height = 2180, +}; + +enum imx678_type { + IMX678_COLOR = 0, + IMX678_MONOCHROME = 1, +}; + +struct imx678_model_info { + enum imx678_type type; + const u32 *codes; + unsigned int num_codes; +}; + +enum imx678_lanemode { + IMX678_LANEMODE_2L = 1, + IMX678_LANEMODE_4L = 3, +}; + +/* Link frequency setup (DDR: lane rate = 2 x link freq) */ +enum { + IMX678_LINK_FREQ_297MHZ, + IMX678_LINK_FREQ_360MHZ, + IMX678_LINK_FREQ_445MHZ, + IMX678_LINK_FREQ_594MHZ, + IMX678_LINK_FREQ_720MHZ, + IMX678_LINK_FREQ_891MHZ, + IMX678_LINK_FREQ_1039MHZ, + IMX678_LINK_FREQ_1188MHZ, +}; + +static const u8 link_freqs_reg_value[] = { + [IMX678_LINK_FREQ_297MHZ] = 0x07, + [IMX678_LINK_FREQ_360MHZ] = 0x06, + [IMX678_LINK_FREQ_445MHZ] = 0x05, + [IMX678_LINK_FREQ_594MHZ] = 0x04, + [IMX678_LINK_FREQ_720MHZ] = 0x03, + [IMX678_LINK_FREQ_891MHZ] = 0x02, + [IMX678_LINK_FREQ_1039MHZ] = 0x01, + [IMX678_LINK_FREQ_1188MHZ] = 0x00, +}; + +static const u64 link_freqs[] = { + [IMX678_LINK_FREQ_297MHZ] = 297000000, + [IMX678_LINK_FREQ_360MHZ] = 360000000, + [IMX678_LINK_FREQ_445MHZ] = 445500000, + [IMX678_LINK_FREQ_594MHZ] = 594000000, + [IMX678_LINK_FREQ_720MHZ] = 720000000, + [IMX678_LINK_FREQ_891MHZ] = 891000000, + [IMX678_LINK_FREQ_1039MHZ] = 1039500000, + [IMX678_LINK_FREQ_1188MHZ] = 1188000000, +}; + +static const u16 min_hmax_4lane[] = { + [IMX678_LINK_FREQ_297MHZ] = 1584, + [IMX678_LINK_FREQ_360MHZ] = 1320, + [IMX678_LINK_FREQ_445MHZ] = 1100, + [IMX678_LINK_FREQ_594MHZ] = 792, + [IMX678_LINK_FREQ_720MHZ] = 660, + [IMX678_LINK_FREQ_891MHZ] = 550, + [IMX678_LINK_FREQ_1039MHZ] = 550, + [IMX678_LINK_FREQ_1188MHZ] = 550, +}; + +struct imx678_inck_cfg { + u32 xclk_hz; /* platform clock rate */ + u8 inck_sel; /* value for reg */ +}; + +static const struct imx678_inck_cfg imx678_inck_table[] = { + { 74250000, 0x00 }, + { 37125000, 0x01 }, + { 72000000, 0x02 }, + { 27000000, 0x03 }, + { 24000000, 0x04 }, + { 36000000, 0x05 }, + { 18000000, 0x06 }, + { 13500000, 0x07 }, +}; + +static const char * const imx678_tpg_menu[] = { + "Disabled", + "All 000h", + "All FFFh", + "All 555h", + "All AAAh", + "Toggle 555/AAAh", + "Toggle AAA/555h", + "Toggle 000/555h", + "Toggle 555/000h", + "Toggle 000/FFFh", + "Toggle FFF/000h", + "Horizontal color bars", + "Vertical color bars", +}; + +static const int imx678_tpg_val[] = { + IMX678_TPG_ALL_000, + IMX678_TPG_ALL_000, + IMX678_TPG_ALL_FFF, + IMX678_TPG_ALL_555, + IMX678_TPG_ALL_AAA, + IMX678_TPG_TOG_555_AAA, + IMX678_TPG_TOG_AAA_555, + IMX678_TPG_TOG_000_555, + IMX678_TPG_TOG_555_000, + IMX678_TPG_TOG_000_FFF, + IMX678_TPG_TOG_FFF_000, + IMX678_TPG_H_COLOR_BARS, + IMX678_TPG_V_COLOR_BARS, +}; + +/* Common configuration */ +static const struct cci_reg_sequence common_regs[] = { + { IMX678_REG_THIN_V_EN, 0x00 }, + { IMX678_REG_VCMODE, 0x01 }, + { CCI_REG8(0x306b), 0x00 }, + { IMX678_REG_GAIN_PGC_FIDMD, 0x01 }, + { CCI_REG8(0x3460), 0x22 }, + { CCI_REG8(0x355a), 0x64 }, + { CCI_REG8(0x3a02), 0x7a }, + { CCI_REG8(0x3a10), 0xec }, + { CCI_REG8(0x3a12), 0x71 }, + { CCI_REG8(0x3a14), 0xde }, + { CCI_REG8(0x3a20), 0x2b }, + { CCI_REG8(0x3a24), 0x22 }, + { CCI_REG8(0x3a25), 0x25 }, + { CCI_REG8(0x3a26), 0x2a }, + { CCI_REG8(0x3a27), 0x2c }, + { CCI_REG8(0x3a28), 0x39 }, + { CCI_REG8(0x3a29), 0x38 }, + { CCI_REG8(0x3a30), 0x04 }, + { CCI_REG8(0x3a31), 0x04 }, + { CCI_REG8(0x3a32), 0x03 }, + { CCI_REG8(0x3a33), 0x03 }, + { CCI_REG8(0x3a34), 0x09 }, + { CCI_REG8(0x3a35), 0x06 }, + { CCI_REG8(0x3a38), 0xcd }, + { CCI_REG8(0x3a3a), 0x4c }, + { CCI_REG8(0x3a3c), 0xb9 }, + { CCI_REG8(0x3a3e), 0x30 }, + { CCI_REG8(0x3a40), 0x2c }, + { CCI_REG8(0x3a42), 0x39 }, + { CCI_REG8(0x3a4e), 0x00 }, + { CCI_REG8(0x3a52), 0x00 }, + { CCI_REG8(0x3a56), 0x00 }, + { CCI_REG8(0x3a5a), 0x00 }, + { CCI_REG8(0x3a5e), 0x00 }, + { CCI_REG8(0x3a62), 0x00 }, + { CCI_REG8(0x3a64), 0x00 }, + { CCI_REG8(0x3a6e), 0xa0 }, + { CCI_REG8(0x3a70), 0x50 }, + { CCI_REG8(0x3a8c), 0x04 }, + { CCI_REG8(0x3a8d), 0x03 }, + { CCI_REG8(0x3a8e), 0x09 }, + { CCI_REG8(0x3a90), 0x38 }, + { CCI_REG8(0x3a91), 0x42 }, + { CCI_REG8(0x3a92), 0x3c }, + { CCI_REG8(0x3b0e), 0xf3 }, + { CCI_REG8(0x3b12), 0xe5 }, + { CCI_REG8(0x3b27), 0xc0 }, + { CCI_REG8(0x3b2e), 0xef }, + { CCI_REG8(0x3b30), 0x6a }, + { CCI_REG8(0x3b32), 0xf6 }, + { CCI_REG8(0x3b36), 0xe1 }, + { CCI_REG8(0x3b3a), 0xe8 }, + { CCI_REG8(0x3b5a), 0x17 }, + { CCI_REG8(0x3b5e), 0xef }, + { CCI_REG8(0x3b60), 0x6a }, + { CCI_REG8(0x3b62), 0xf6 }, + { CCI_REG8(0x3b66), 0xe1 }, + { CCI_REG8(0x3b6a), 0xe8 }, + { CCI_REG8(0x3b88), 0xec }, + { CCI_REG8(0x3b8a), 0xed }, + { CCI_REG8(0x3b94), 0x71 }, + { CCI_REG8(0x3b96), 0x72 }, + { CCI_REG8(0x3b98), 0xde }, + { CCI_REG8(0x3b9a), 0xdf }, + { CCI_REG8(0x3c0f), 0x06 }, + { CCI_REG8(0x3c10), 0x06 }, + { CCI_REG8(0x3c11), 0x06 }, + { CCI_REG8(0x3c12), 0x06 }, + { CCI_REG8(0x3c13), 0x06 }, + { CCI_REG8(0x3c18), 0x20 }, + { CCI_REG8(0x3c37), 0x10 }, + { CCI_REG8(0x3c3a), 0x7a }, + { CCI_REG8(0x3c40), 0xf4 }, + { CCI_REG8(0x3c48), 0xe6 }, + { CCI_REG8(0x3c54), 0xce }, + { CCI_REG8(0x3c56), 0xd0 }, + { CCI_REG8(0x3c6c), 0x53 }, + { CCI_REG8(0x3c6e), 0x55 }, + { CCI_REG8(0x3c70), 0xc0 }, + { CCI_REG8(0x3c72), 0xc2 }, + { CCI_REG8(0x3c7e), 0xce }, + { CCI_REG8(0x3c8c), 0xcf }, + { CCI_REG8(0x3c8e), 0xeb }, + { CCI_REG8(0x3c98), 0x54 }, + { CCI_REG8(0x3c9a), 0x70 }, + { CCI_REG8(0x3c9c), 0xc1 }, + { CCI_REG8(0x3c9e), 0xdd }, + { CCI_REG8(0x3cb0), 0x7a }, + { CCI_REG8(0x3cb2), 0xba }, + { CCI_REG8(0x3cc8), 0xbc }, + { CCI_REG8(0x3cca), 0x7c }, + { CCI_REG8(0x3cd4), 0xea }, + { CCI_REG8(0x3cd5), 0x01 }, + { CCI_REG8(0x3cd6), 0x4a }, + { CCI_REG8(0x3cd8), 0x00 }, + { CCI_REG8(0x3cd9), 0x00 }, + { CCI_REG8(0x3cda), 0xff }, + { CCI_REG8(0x3cdb), 0x03 }, + { CCI_REG8(0x3cdc), 0x00 }, + { CCI_REG8(0x3cdd), 0x00 }, + { CCI_REG8(0x3cde), 0xff }, + { CCI_REG8(0x3cdf), 0x03 }, + { CCI_REG8(0x3ce4), 0x4c }, + { CCI_REG8(0x3ce6), 0xec }, + { CCI_REG8(0x3ce7), 0x01 }, + { CCI_REG8(0x3ce8), 0xff }, + { CCI_REG8(0x3ce9), 0x03 }, + { CCI_REG8(0x3cea), 0x00 }, + { CCI_REG8(0x3ceb), 0x00 }, + { CCI_REG8(0x3cec), 0xff }, + { CCI_REG8(0x3ced), 0x03 }, + { CCI_REG8(0x3cee), 0x00 }, + { CCI_REG8(0x3cef), 0x00 }, + { CCI_REG8(0x3cf2), 0xff }, + { CCI_REG8(0x3cf3), 0x03 }, + { CCI_REG8(0x3cf4), 0x00 }, + { CCI_REG8(0x3e28), 0x82 }, + { CCI_REG8(0x3e2a), 0x80 }, + { CCI_REG8(0x3e30), 0x85 }, + { CCI_REG8(0x3e32), 0x7d }, + { CCI_REG8(0x3e5c), 0xce }, + { CCI_REG8(0x3e5e), 0xd3 }, + { CCI_REG8(0x3e70), 0x53 }, + { CCI_REG8(0x3e72), 0x58 }, + { CCI_REG8(0x3e74), 0xc0 }, + { CCI_REG8(0x3e76), 0xc5 }, + { CCI_REG8(0x3e78), 0xc0 }, + { CCI_REG8(0x3e79), 0x01 }, + { CCI_REG8(0x3e7a), 0xd4 }, + { CCI_REG8(0x3e7b), 0x01 }, + { CCI_REG8(0x3eb4), 0x0b }, + { CCI_REG8(0x3eb5), 0x02 }, + { CCI_REG8(0x3eb6), 0x4d }, + { CCI_REG8(0x3eb7), 0x42 }, + { CCI_REG8(0x3eec), 0xf3 }, + { CCI_REG8(0x3eee), 0xe7 }, + { CCI_REG8(0x3f01), 0x01 }, + { CCI_REG8(0x3f24), 0x10 }, + { CCI_REG8(0x3f28), 0x2d }, + { CCI_REG8(0x3f2a), 0x2d }, + { CCI_REG8(0x3f2c), 0x2d }, + { CCI_REG8(0x3f2e), 0x2d }, + { CCI_REG8(0x3f30), 0x23 }, + { CCI_REG8(0x3f38), 0x2d }, + { CCI_REG8(0x3f3a), 0x2d }, + { CCI_REG8(0x3f3c), 0x2d }, + { CCI_REG8(0x3f3e), 0x28 }, + { CCI_REG8(0x3f40), 0x1e }, + { CCI_REG8(0x3f48), 0x2d }, + { CCI_REG8(0x3f4a), 0x2d }, + { CCI_REG8(0x3f4c), 0x00 }, + { CCI_REG8(0x4004), 0xe4 }, + { CCI_REG8(0x4006), 0xff }, + { CCI_REG8(0x4018), 0x69 }, + { CCI_REG8(0x401a), 0x84 }, + { CCI_REG8(0x401c), 0xd6 }, + { CCI_REG8(0x401e), 0xf1 }, + { CCI_REG8(0x4038), 0xde }, + { CCI_REG8(0x403a), 0x00 }, + { CCI_REG8(0x403b), 0x01 }, + { CCI_REG8(0x404c), 0x63 }, + { CCI_REG8(0x404e), 0x85 }, + { CCI_REG8(0x4050), 0xd0 }, + { CCI_REG8(0x4052), 0xf2 }, + { CCI_REG8(0x4108), 0xdd }, + { CCI_REG8(0x410a), 0xf7 }, + { CCI_REG8(0x411c), 0x62 }, + { CCI_REG8(0x411e), 0x7c }, + { CCI_REG8(0x4120), 0xcf }, + { CCI_REG8(0x4122), 0xe9 }, + { CCI_REG8(0x4138), 0xe6 }, + { CCI_REG8(0x413a), 0xf1 }, + { CCI_REG8(0x414c), 0x6b }, + { CCI_REG8(0x414e), 0x76 }, + { CCI_REG8(0x4150), 0xd8 }, + { CCI_REG8(0x4152), 0xe3 }, + { CCI_REG8(0x417e), 0x03 }, + { CCI_REG8(0x417f), 0x01 }, + { CCI_REG8(0x4186), 0xe0 }, + { CCI_REG8(0x4190), 0xf3 }, + { CCI_REG8(0x4192), 0xf7 }, + { CCI_REG8(0x419c), 0x78 }, + { CCI_REG8(0x419e), 0x7c }, + { CCI_REG8(0x41a0), 0xe5 }, + { CCI_REG8(0x41a2), 0xe9 }, + { CCI_REG8(0x41c8), 0xe2 }, + { CCI_REG8(0x41ca), 0xfd }, + { CCI_REG8(0x41dc), 0x67 }, + { CCI_REG8(0x41de), 0x82 }, + { CCI_REG8(0x41e0), 0xd4 }, + { CCI_REG8(0x41e2), 0xef }, + { CCI_REG8(0x4200), 0xde }, + { CCI_REG8(0x4202), 0xda }, + { CCI_REG8(0x4218), 0x63 }, + { CCI_REG8(0x421a), 0x5f }, + { CCI_REG8(0x421c), 0xd0 }, + { CCI_REG8(0x421e), 0xcc }, + { CCI_REG8(0x425a), 0x82 }, + { CCI_REG8(0x425c), 0xef }, + { CCI_REG8(0x4348), 0xfe }, + { CCI_REG8(0x4349), 0x06 }, + { CCI_REG8(0x4352), 0xce }, + { CCI_REG8(0x4420), 0x0b }, + { CCI_REG8(0x4421), 0x02 }, + { CCI_REG8(0x4422), 0x4d }, + { CCI_REG8(0x4423), 0x0a }, + { CCI_REG8(0x4426), 0xf5 }, + { CCI_REG8(0x442a), 0xe7 }, + { CCI_REG8(0x4432), 0xf5 }, + { CCI_REG8(0x4436), 0xe7 }, + { CCI_REG8(0x4466), 0xb4 }, + { CCI_REG8(0x446e), 0x32 }, + { CCI_REG8(0x449f), 0x1c }, + { CCI_REG8(0x44a4), 0x2c }, + { CCI_REG8(0x44a6), 0x2c }, + { CCI_REG8(0x44a8), 0x2c }, + { CCI_REG8(0x44aa), 0x2c }, + { CCI_REG8(0x44b4), 0x2c }, + { CCI_REG8(0x44b6), 0x2c }, + { CCI_REG8(0x44b8), 0x2c }, + { CCI_REG8(0x44ba), 0x2c }, + { CCI_REG8(0x44c4), 0x2c }, + { CCI_REG8(0x44c6), 0x2c }, + { CCI_REG8(0x44c8), 0x2c }, + { CCI_REG8(0x4506), 0xf3 }, + { CCI_REG8(0x450e), 0xe5 }, + { CCI_REG8(0x4516), 0xf3 }, + { CCI_REG8(0x4522), 0xe5 }, + { CCI_REG8(0x4524), 0xf3 }, + { CCI_REG8(0x452c), 0xe5 }, + { CCI_REG8(0x453c), 0x22 }, + { CCI_REG8(0x453d), 0x1b }, + { CCI_REG8(0x453e), 0x1b }, + { CCI_REG8(0x453f), 0x15 }, + { CCI_REG8(0x4540), 0x15 }, + { CCI_REG8(0x4541), 0x15 }, + { CCI_REG8(0x4542), 0x15 }, + { CCI_REG8(0x4543), 0x15 }, + { CCI_REG8(0x4544), 0x15 }, + { CCI_REG8(0x4548), 0x00 }, + { CCI_REG8(0x4549), 0x01 }, + { CCI_REG8(0x454a), 0x01 }, + { CCI_REG8(0x454b), 0x06 }, + { CCI_REG8(0x454c), 0x06 }, + { CCI_REG8(0x454d), 0x06 }, + { CCI_REG8(0x454e), 0x06 }, + { CCI_REG8(0x454f), 0x06 }, + { CCI_REG8(0x4550), 0x06 }, + { CCI_REG8(0x4554), 0x55 }, + { CCI_REG8(0x4555), 0x02 }, + { CCI_REG8(0x4556), 0x42 }, + { CCI_REG8(0x4557), 0x05 }, + { CCI_REG8(0x4558), 0xfd }, + { CCI_REG8(0x4559), 0x05 }, + { CCI_REG8(0x455a), 0x94 }, + { CCI_REG8(0x455b), 0x06 }, + { CCI_REG8(0x455d), 0x06 }, + { CCI_REG8(0x455e), 0x49 }, + { CCI_REG8(0x455f), 0x07 }, + { CCI_REG8(0x4560), 0x7f }, + { CCI_REG8(0x4561), 0x07 }, + { CCI_REG8(0x4562), 0xa5 }, + { CCI_REG8(0x4564), 0x55 }, + { CCI_REG8(0x4565), 0x02 }, + { CCI_REG8(0x4566), 0x42 }, + { CCI_REG8(0x4567), 0x05 }, + { CCI_REG8(0x4568), 0xfd }, + { CCI_REG8(0x4569), 0x05 }, + { CCI_REG8(0x456a), 0x94 }, + { CCI_REG8(0x456b), 0x06 }, + { CCI_REG8(0x456d), 0x06 }, + { CCI_REG8(0x456e), 0x49 }, + { CCI_REG8(0x456f), 0x07 }, + { CCI_REG8(0x4572), 0xa5 }, + { CCI_REG8(0x460c), 0x7d }, + { CCI_REG8(0x460e), 0xb1 }, + { CCI_REG8(0x4614), 0xa8 }, + { CCI_REG8(0x4616), 0xb2 }, + { CCI_REG8(0x461c), 0x7e }, + { CCI_REG8(0x461e), 0xa7 }, + { CCI_REG8(0x4624), 0xa8 }, + { CCI_REG8(0x4626), 0xb2 }, + { CCI_REG8(0x462c), 0x7e }, + { CCI_REG8(0x462e), 0x8a }, + { CCI_REG8(0x4630), 0x94 }, + { CCI_REG8(0x4632), 0xa7 }, + { CCI_REG8(0x4634), 0xfb }, + { CCI_REG8(0x4636), 0x2f }, + { CCI_REG8(0x4638), 0x81 }, + { CCI_REG8(0x4639), 0x01 }, + { CCI_REG8(0x463a), 0xb5 }, + { CCI_REG8(0x463b), 0x01 }, + { CCI_REG8(0x463c), 0x26 }, + { CCI_REG8(0x463e), 0x30 }, + { CCI_REG8(0x4640), 0xac }, + { CCI_REG8(0x4641), 0x01 }, + { CCI_REG8(0x4642), 0xb6 }, + { CCI_REG8(0x4643), 0x01 }, + { CCI_REG8(0x4644), 0xfc }, + { CCI_REG8(0x4646), 0x25 }, + { CCI_REG8(0x4648), 0x82 }, + { CCI_REG8(0x4649), 0x01 }, + { CCI_REG8(0x464a), 0xab }, + { CCI_REG8(0x464b), 0x01 }, + { CCI_REG8(0x464c), 0x26 }, + { CCI_REG8(0x464e), 0x30 }, + { CCI_REG8(0x4654), 0xfc }, + { CCI_REG8(0x4656), 0x08 }, + { CCI_REG8(0x4658), 0x12 }, + { CCI_REG8(0x465a), 0x25 }, + { CCI_REG8(0x4662), 0xfc }, + { CCI_REG8(0x46a2), 0xfb }, + { CCI_REG8(0x46d6), 0xf3 }, + { CCI_REG8(0x46e6), 0x00 }, + { CCI_REG8(0x46e8), 0xff }, + { CCI_REG8(0x46e9), 0x03 }, + { CCI_REG8(0x46ec), 0x7a }, + { CCI_REG8(0x46ee), 0xe5 }, + { CCI_REG8(0x46f4), 0xee }, + { CCI_REG8(0x46f6), 0xf2 }, + { CCI_REG8(0x470c), 0xff }, + { CCI_REG8(0x470d), 0x03 }, + { CCI_REG8(0x470e), 0x00 }, + { CCI_REG8(0x4714), 0xe0 }, + { CCI_REG8(0x4716), 0xe4 }, + { CCI_REG8(0x471e), 0xed }, + { CCI_REG8(0x472e), 0x00 }, + { CCI_REG8(0x4730), 0xff }, + { CCI_REG8(0x4731), 0x03 }, + { CCI_REG8(0x4734), 0x7b }, + { CCI_REG8(0x4736), 0xdf }, + { CCI_REG8(0x4754), 0x7d }, + { CCI_REG8(0x4756), 0x8b }, + { CCI_REG8(0x4758), 0x93 }, + { CCI_REG8(0x475a), 0xb1 }, + { CCI_REG8(0x475c), 0xfb }, + { CCI_REG8(0x475e), 0x09 }, + { CCI_REG8(0x4760), 0x11 }, + { CCI_REG8(0x4762), 0x2f }, + { CCI_REG8(0x4766), 0xcc }, + { CCI_REG8(0x4776), 0xcb }, + { CCI_REG8(0x477e), 0x4a }, + { CCI_REG8(0x478e), 0x49 }, + { CCI_REG8(0x4794), 0x7c }, + { CCI_REG8(0x4796), 0x8f }, + { CCI_REG8(0x4798), 0xb3 }, + { CCI_REG8(0x4799), 0x00 }, + { CCI_REG8(0x479a), 0xcc }, + { CCI_REG8(0x479c), 0xc1 }, + { CCI_REG8(0x479e), 0xcb }, + { CCI_REG8(0x47a4), 0x7d }, + { CCI_REG8(0x47a6), 0x8e }, + { CCI_REG8(0x47a8), 0xb4 }, + { CCI_REG8(0x47a9), 0x00 }, + { CCI_REG8(0x47aa), 0xc0 }, + { CCI_REG8(0x47ac), 0xfa }, + { CCI_REG8(0x47ae), 0x0d }, + { CCI_REG8(0x47b0), 0x31 }, + { CCI_REG8(0x47b1), 0x01 }, + { CCI_REG8(0x47b2), 0x4a }, + { CCI_REG8(0x47b3), 0x01 }, + { CCI_REG8(0x47b4), 0x3f }, + { CCI_REG8(0x47b6), 0x49 }, + { CCI_REG8(0x47bc), 0xfb }, + { CCI_REG8(0x47be), 0x0c }, + { CCI_REG8(0x47c0), 0x32 }, + { CCI_REG8(0x47c1), 0x01 }, + { CCI_REG8(0x47c2), 0x3e }, + { CCI_REG8(0x47c3), 0x01 }, + { IMX678_REG_WDMODE, 0x00 }, + { IMX678_REG_MDBIT, 0x01 }, + { IMX678_REG_XXS_DRV, 0x00 }, +}; + +static const u32 codes_bayer[] = { + MEDIA_BUS_FMT_SRGGB12_1X12, +}; + +static const u32 codes_monochrome[] = { + MEDIA_BUS_FMT_Y12_1X12, +}; + +static const struct imx678_model_info imx678_aaqr_info = { + .type = IMX678_COLOR, + .codes = codes_bayer, + .num_codes = ARRAY_SIZE(codes_bayer), +}; + +static const struct imx678_model_info imx678_aamr_info = { + .type = IMX678_MONOCHROME, + .codes = codes_monochrome, + .num_codes = ARRAY_SIZE(codes_monochrome), +}; + +static const char * const imx678_supply_name[] = { + "avdd", /* Analog (3.3V) supply */ + "dvdd", /* Digital Core (1.1V) supply */ + "ovdd", /* IF (1.8V) supply */ +}; + +struct imx678 { + struct v4l2_subdev sd; + struct media_pad pad; + struct regmap *cci; + + const struct imx678_model_info *info; + + struct clk *xclk; + u32 xclk_freq; + + /* chosen INCK_SEL register value */ + u8 inck_sel_val; + + /* Link configurations */ + enum imx678_lanemode lane_mode; + unsigned long link_freq_bitmap; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx678_supply_name)]; + + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + + /* Track VMAX for exposure updates */ + u32 vmax; +}; + +static inline struct imx678 *to_imx678(struct v4l2_subdev *_sd) +{ + return container_of_const(_sd, struct imx678, sd); +} + +static u32 imx678_default_mbus_code(struct imx678 *imx678) +{ + return imx678->info->codes[0]; +} + +static bool imx678_mbus_code_supported(struct imx678 *imx678, u32 code) +{ + for (unsigned int i = 0; i < imx678->info->num_codes; i++) { + if (imx678->info->codes[i] == code) + return true; + } + + return false; +} + +static int imx678_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx678 *imx678 = container_of_const(ctrl->handler, struct + imx678, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx678->sd); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&imx678->sd); + format = v4l2_subdev_state_get_format(state, IMX678_SOURCE_PAD); + + if (ctrl->id == V4L2_CID_VBLANK) { + u32 current_exposure = imx678->exposure->cur.val; + + imx678->vmax = format->height + ctrl->val; + + current_exposure = clamp_t(u32, current_exposure, + IMX678_EXPOSURE_MIN, + imx678->vmax - IMX678_SHR_MIN); + ret = __v4l2_ctrl_modify_range(imx678->exposure, + IMX678_EXPOSURE_MIN, + imx678->vmax - IMX678_SHR_MIN, + 1, current_exposure); + if (ret) + return ret; + } + + /* + * Only apply control values when device is powered on (RPM ACTIVE) + * and streaming (usage count != 0) + */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + cci_write(imx678->cci, IMX678_REG_VMAX, imx678->vmax, &ret); + fallthrough; /* SHR = VMAX - exposure, so update it */ + case V4L2_CID_EXPOSURE: { + u32 shr = imx678->vmax - imx678->exposure->val; + + cci_write(imx678->cci, IMX678_REG_SHR, shr, &ret); + break; + } + case V4L2_CID_ANALOGUE_GAIN: + cci_write(imx678->cci, IMX678_REG_GAIN, ctrl->val, &ret); + break; + case V4L2_CID_HBLANK: { + u32 hmax = (format->width + ctrl->val) / IMX678_PIX_PER_CLK; + + cci_write(imx678->cci, IMX678_REG_HMAX, hmax, &ret); + break; + } + case V4L2_CID_TEST_PATTERN: { + cci_write(imx678->cci, IMX678_REG_TPG_COLORWIDTH, + IMX678_TPG_COLORWIDTH_160PIX, &ret); + cci_write(imx678->cci, IMX678_REG_TPG_PATSEL_DUOUT, + imx678_tpg_val[ctrl->val], &ret); + cci_write(imx678->cci, IMX678_REG_TPG_EN_DUOUT, + (ctrl->val) ? 1 : 0, + &ret); + break; + } + case V4L2_CID_HFLIP: + cci_write(imx678->cci, IMX678_REG_WINMODEH, ctrl->val, &ret); + break; + case V4L2_CID_VFLIP: + cci_write(imx678->cci, IMX678_REG_WINMODEV, ctrl->val, &ret); + break; + default: + dev_warn(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx678_ctrl_ops = { + .s_ctrl = imx678_set_ctrl, +}; + +static int imx678_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx678 *imx678 = to_imx678(sd); + + if (code->index >= imx678->info->num_codes) + return -EINVAL; + + code->code = imx678->info->codes[code->index]; + + return 0; +} + +static int imx678_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx678 *imx678 = to_imx678(sd); + const struct v4l2_rect *crop; + + if (fse->index) + return -EINVAL; + + if (!imx678_mbus_code_supported(imx678, fse->code)) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(sd_state, fse->pad); + + fse->min_width = crop->width; + fse->max_width = fse->min_width; + fse->min_height = crop->height; + fse->max_height = fse->min_height; + + return 0; +} + +static int imx678_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); + return 0; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r = imx678_native_area; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = imx678_active_area; + return 0; + } + + return -EINVAL; +} + +static int imx678_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct imx678 *imx678 = to_imx678(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_state_get_crop(state, IMX678_SOURCE_PAD); + *crop = imx678_active_area; + + format = v4l2_subdev_state_get_format(state, IMX678_SOURCE_PAD); + format->code = imx678_default_mbus_code(imx678); + format->width = imx678_active_area.width; + format->height = imx678_active_area.height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static int imx678_write_common(struct imx678 *imx678) +{ + int ret = 0; + + cci_multi_reg_write(imx678->cci, common_regs, ARRAY_SIZE(common_regs), + &ret); + + cci_write(imx678->cci, IMX678_REG_INCK_SEL, imx678->inck_sel_val, &ret); + cci_write(imx678->cci, IMX678_REG_DATARATE_SEL, + link_freqs_reg_value[__ffs(imx678->link_freq_bitmap)], &ret); + cci_write(imx678->cci, IMX678_REG_LANEMODE, imx678->lane_mode, &ret); + + cci_write(imx678->cci, IMX678_REG_INTERFACE_SEL, IMX678_INTERFACE_2L_4L, + &ret); + + return ret; +} + +static int imx678_program_window(struct imx678 *imx678, + const struct v4l2_rect *crop) +{ + int ret = 0; + + cci_write(imx678->cci, IMX678_REG_ADDMODE, 0x00, &ret); + cci_write(imx678->cci, IMX678_REG_WINMODE, + v4l2_rect_equal(crop, &imx678_active_area) ? 0x00 : 0x04, + &ret); + cci_write(imx678->cci, IMX678_REG_PIX_HST, + crop->left - imx678_active_area.left, &ret); + cci_write(imx678->cci, IMX678_REG_PIX_HWIDTH, crop->width, &ret); + cci_write(imx678->cci, IMX678_REG_PIX_VST, + crop->top - imx678_active_area.top, &ret); + cci_write(imx678->cci, IMX678_REG_PIX_VWIDTH, crop->height, &ret); + cci_write(imx678->cci, IMX678_REG_ADBIT, 0x01, &ret); + + return ret; +} + +static int imx678_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx678 *imx678 = to_imx678(sd); + const struct v4l2_rect *crop; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + crop = v4l2_subdev_state_get_crop(state, pad); + ret = imx678_program_window(imx678, crop); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + + ret = __v4l2_ctrl_handler_setup(imx678->sd.ctrl_handler); + if (ret) { + dev_err(&client->dev, "%s failed to apply user values\n", + __func__); + goto err_rpm_put; + } + + cci_write(imx678->cci, IMX678_REG_MODE_SELECT, IMX678_MODE_STREAMING, + &ret); + usleep_range(IMX678_STREAM_DELAY_US, IMX678_STREAM_DELAY_US + + IMX678_STREAM_DELAY_RANGE_US); + cci_write(imx678->cci, IMX678_REG_XMSTA, 0x00, &ret); + + if (ret) { + dev_err(&client->dev, "%s failed to start streaming\n", + __func__); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + + return ret; +} + +static int imx678_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx678 *imx678 = to_imx678(sd); + int ret = 0; + + /* Master mode disable */ + cci_write(imx678->cci, IMX678_REG_XMSTA, 0x01, &ret); + /* Standby */ + cci_write(imx678->cci, IMX678_REG_MODE_SELECT, IMX678_MODE_STANDBY, + &ret); + if (ret) + dev_err(&client->dev, "%s failed to stop stream\n", __func__); + + pm_runtime_put(&client->dev); + + return ret; +} + +static int imx678_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + fsleep(1); /* Tlow > 500ns */ + + gpiod_set_value_cansleep(imx678->reset_gpio, 0); + + fsleep(1); /* T3 > 1us */ + + ret = clk_prepare_enable(imx678->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + + fsleep(20); /* T4 > 20us */ + + ret = imx678_write_common(imx678); + if (ret) { + dev_err(&client->dev, "%s failed to write registers\n", + __func__); + goto clk_off; + } + + return 0; + +clk_off: + clk_disable_unprepare(imx678->xclk); + +reg_off: + gpiod_set_value_cansleep(imx678->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + + return ret; +} + +static int imx678_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + clk_disable_unprepare(imx678->xclk); + gpiod_set_value_cansleep(imx678->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + + return 0; +} + +static int imx678_identify_model(struct imx678 *imx678) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx678->sd); + const struct imx678_model_info *info; + enum imx678_type detected; + int ret = 0; + u64 val = 0; + + info = device_get_match_data(&client->dev); + + /* + * This sensor's ID registers become accessible 80ms after coming out + * of STANDBY mode. + */ + cci_write(imx678->cci, IMX678_REG_MODE_SELECT, 0, &ret); + fsleep(IMX678_MODULE_ID_DELAY); + + cci_read(imx678->cci, IMX678_REG_MODULE_ID, &val, &ret); + + if (ret) { + dev_err(&client->dev, + "I2C transaction failed ret = %d\n", ret); + return ret; + } + + if (val != IMX678_ID) { + dev_err(&client->dev, + "Chip ID mismatch: %x!=%llx\n", IMX678_ID, val); + return -ENXIO; + } + + cci_read(imx678->cci, IMX678_REG_MONOCHROME, &val, &ret); + + if (ret) { + dev_err(&client->dev, + "I2C transaction failed ret = %d\n", ret); + return ret; + } + + detected = val & IMX678_TYPE; + + /* Prefer to use sensor type specified in device tree */ + if (info) { + imx678->info = info; + if (detected != info->type) + dev_err(&client->dev, + "detected %s sensor, DT specifies %s; using DT value\n", + detected == IMX678_COLOR ? "color" : "mono", + info->type == IMX678_COLOR ? "color" : "mono"); + } else { + imx678->info = detected == IMX678_MONOCHROME ? + &imx678_aamr_info : &imx678_aaqr_info; + dev_info(&client->dev, + "sensor type missing in DT; detected %s sensor\n", + detected == IMX678_MONOCHROME ? "mono" : "color"); + } + + return 0; +} + +static const struct v4l2_subdev_video_ops imx678_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx678_pad_ops = { + .enum_mbus_code = imx678_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = v4l2_subdev_get_fmt, + .get_selection = imx678_get_selection, + .enum_frame_size = imx678_enum_frame_size, + .enable_streams = imx678_enable_streams, + .disable_streams = imx678_disable_streams, +}; + +static const struct v4l2_subdev_ops imx678_subdev_ops = { + .video = &imx678_video_ops, + .pad = &imx678_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx678_internal_ops = { + .init_state = imx678_init_state, +}; + +static int imx678_init_controls(struct imx678 *imx678) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + const u32 hmax_4lane = min_hmax_4lane[__ffs(imx678->link_freq_bitmap)]; + const u32 lane_scale = imx678->lane_mode == IMX678_LANEMODE_2L ? 2 : 1; + struct i2c_client *client = v4l2_get_subdevdata(&imx678->sd); + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *link_freq; + s32 hblank, max_hblank, vblank, max_vblank; + u32 hmax; + int ret; + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret < 0) + return ret; + + ctrl_hdlr = &imx678->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 11); + if (ret) + return ret; + + imx678->vmax = IMX678_VMAX_DEFAULT; + hmax = hmax_4lane * lane_scale; + + /* PIXEL_RATE is fixed and read-only */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_PIXEL_RATE, + IMX678_PIXEL_RATE, IMX678_PIXEL_RATE, 1, + IMX678_PIXEL_RATE); + + /* LINK_FREQ is also read only */ + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freqs) - 1, + __ffs(imx678->link_freq_bitmap), + link_freqs); + + if (link_freq) + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank = imx678->vmax - imx678_active_area.height; + max_vblank = IMX678_VMAX_MAX - imx678_active_area.height; + imx678->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_VBLANK, vblank, max_vblank, + 2, vblank); + + hblank = hmax * IMX678_PIX_PER_CLK - imx678_active_area.width; + max_hblank = IMX678_HMAX_MAX * IMX678_PIX_PER_CLK - + imx678_active_area.width; + imx678->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_HBLANK, hblank, max_hblank, + IMX678_PIX_PER_CLK, hblank); + + imx678->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX678_EXPOSURE_MIN, + IMX678_VMAX_DEFAULT - + IMX678_SHR_MIN, + IMX678_EXPOSURE_STEP, + IMX678_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX678_ANA_GAIN_MIN_NORMAL, + IMX678_ANA_GAIN_MAX_NORMAL, IMX678_ANA_GAIN_STEP, + IMX678_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx678_tpg_menu) - 1, 0, 0, + imx678_tpg_menu); + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx678_ctrl_ops, &props); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ret; + } + + imx678->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static int imx678_check_hwcfg(struct device *dev, struct imx678 *imx678) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + dev_err(dev, "could not parse endpoint\n"); + goto error_out; + } + + switch (ep_cfg.bus.mipi_csi2.num_data_lanes) { + case 2: + imx678->lane_mode = IMX678_LANEMODE_2L; + break; + case 4: + imx678->lane_mode = IMX678_LANEMODE_4L; + break; + default: + dev_err(dev, + "only 2 or 4 CSI2 data lanes are currently supported\n"); + goto error_out; + } + + ret = v4l2_link_freq_to_bitmap(dev, ep_cfg.link_frequencies, + ep_cfg.nr_of_link_frequencies, + link_freqs, ARRAY_SIZE(link_freqs), + &imx678->link_freq_bitmap); + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int imx678_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct imx678 *imx678; + int ret, i; + + imx678 = devm_kzalloc(&client->dev, sizeof(*imx678), GFP_KERNEL); + if (!imx678) + return -ENOMEM; + + v4l2_i2c_subdev_init(&imx678->sd, client, &imx678_subdev_ops); + + imx678->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx678->cci)) + return dev_err_probe(dev, PTR_ERR(imx678->cci), + "failed to init CCI\n"); + + if (imx678_check_hwcfg(dev, imx678)) + return -EINVAL; + + imx678->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(imx678->xclk)) + return dev_err_probe(dev, PTR_ERR(imx678->xclk), + "failed to get xclk\n"); + + imx678->xclk_freq = clk_get_rate(imx678->xclk); + + for (i = 0; i < ARRAY_SIZE(imx678_inck_table); ++i) { + if (imx678_inck_table[i].xclk_hz == imx678->xclk_freq) { + imx678->inck_sel_val = imx678_inck_table[i].inck_sel; + break; + } + } + + if (i == ARRAY_SIZE(imx678_inck_table)) + return dev_err_probe(dev, -EINVAL, + "unsupported XCLK rate %u Hz\n", + imx678->xclk_freq); + + for (i = 0; i < ARRAY_SIZE(imx678_supply_name); i++) + imx678->supplies[i].supply = imx678_supply_name[i]; + + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + imx678->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(imx678->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(imx678->reset_gpio), + "failed to get reset GPIO\n"); + + ret = imx678_power_on(dev); + if (ret) + return ret; + + ret = imx678_identify_model(imx678); + if (ret) + goto error_power_off; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = imx678_init_controls(imx678); + if (ret) + goto error_pm_runtime; + + imx678->sd.internal_ops = &imx678_internal_ops; + imx678->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx678->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + imx678->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&imx678->sd.entity, 1, &imx678->pad); + if (ret) { + dev_err_probe(dev, ret, "failed to init entity pads\n"); + goto error_handler_free; + } + + imx678->sd.state_lock = imx678->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx678->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "subdev init error\n"); + goto error_media_entity; + } + + ret = v4l2_async_register_subdev_sensor(&imx678->sd); + if (ret < 0) { + dev_err_probe(dev, ret, + "failed to register sensor sub-device\n"); + goto error_subdev_cleanup; + } + + pm_runtime_idle(dev); + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx678->sd); + +error_media_entity: + media_entity_cleanup(&imx678->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(imx678->sd.ctrl_handler); + +error_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + +error_power_off: + imx678_power_off(&client->dev); + + return ret; +} + +static void imx678_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(imx678->sd.ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx678_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct dev_pm_ops imx678_pm_ops = { + SET_RUNTIME_PM_OPS(imx678_power_off, imx678_power_on, NULL) +}; + +static const struct of_device_id imx678_of_match[] = { + { .compatible = "sony,imx678-aamr", .data = &imx678_aamr_info }, + { .compatible = "sony,imx678-aaqr", .data = &imx678_aaqr_info }, + /* for non-conforming DTs that rely on runtime check */ + { .compatible = "sony,imx678" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, imx678_of_match); + +static struct i2c_driver imx678_i2c_driver = { + .driver = { + .name = "imx678", + .of_match_table = imx678_of_match, + .pm = pm_ptr(&imx678_pm_ops), + }, + .probe = imx678_probe, + .remove = imx678_remove, +}; + +module_i2c_driver(imx678_i2c_driver); + +MODULE_AUTHOR("Will Whang <will@willwhang.com>"); +MODULE_AUTHOR("Tetsuya NOMURA <tetsuya.nomura@soho-enterprise.com>"); +MODULE_AUTHOR("Jai Luthra <jai.luthra@ideasonboard.com>"); +MODULE_DESCRIPTION("Sony imx678 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 143dcfe10445..0150e4d296af 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -820,18 +820,16 @@ static int ov02a10_check_hwcfg(struct device *dev, struct ov02a10 *ov02a10) if (!ep) return -ENXIO; + /* Optional indication of MIPI clock voltage unit */ + if (!fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage", + &clk_volt)) + ov02a10->mipi_clock_voltage = clk_volt; + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) return ret; - /* Optional indication of MIPI clock voltage unit */ - ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage", - &clk_volt); - - if (!ret) - ov02a10->mipi_clock_voltage = clk_volt; - for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { if (link_freq_menu_items[i] == diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index c2e02f191816..b4e14171556f 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1116,18 +1116,14 @@ static void ov7740_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); - mutex_destroy(&ov7740->mutex); - v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler); - media_entity_cleanup(&ov7740->subdev.entity); v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&ov7740->subdev.entity); ov7740_free_controls(ov7740); - pm_runtime_get_sync(&client->dev); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov7740_set_power(ov7740, 0); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); - - ov7740_set_power(ov7740, 0); } static int __maybe_unused ov7740_runtime_suspend(struct device *dev) diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index f01ecdb0b627..cf50898f9a92 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -14,7 +14,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/dvb/ca.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.h b/drivers/media/pci/hws/hws_v4l2_ioctl.h index 53044f78d6fa..9a81e940e33e 100644 --- a/drivers/media/pci/hws/hws_v4l2_ioctl.h +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.h @@ -17,14 +17,10 @@ int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *setfp int hws_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i); int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *i); int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i); -int hws_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a); -int hws_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a); int hws_vidioc_dv_timings_cap(struct file *file, void *fh, struct v4l2_dv_timings_cap *cap); int hws_vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings); - -int hws_vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); int hws_vidioc_g_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings); int hws_vidioc_enum_dv_timings(struct file *file, void *fh, diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 88581a4c081d..50818a10168e 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -97,6 +97,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), /* Sony IMX471 */ IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), + /* Sony IMX471 (found on Lenovo X1 Carbon G14) */ + IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000), /* Toshiba T4KA3 */ IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), }; @@ -134,6 +136,45 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "OVTI02C1", }, + /* + * The first four characters of DMI_BOARD_NAME identify the Lenovo + * machine type/model. For example, a DMI_BOARD_NAME starting with + * "21Q6" indicates a ThinkPad X9-15. + * + * Reference: https://psref.lenovo.com/ + */ + { + /* Lenovo X9-14 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21QA"), + }, + .driver_data = "SONY471A", + }, + { + /* Lenovo X9-14 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21QB"), + }, + .driver_data = "SONY471A", + }, + { + /* Lenovo X9-15 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21Q6"), + }, + .driver_data = "SONY471A", + }, + { + /* Lenovo X9-15 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21Q7"), + }, + .driver_data = "SONY471A", + }, {} /* Terminating entry */ }; @@ -171,6 +212,7 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { { "INTC10DE" }, /* LNL */ { "INTC10E0" }, /* ARL */ { "INTC10E1" }, /* PTL */ + { "INTC10FA" }, /* NVL */ }; static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index 221e3c447f36..14eedd1ceb27 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/errno.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -412,7 +411,7 @@ static int s3c_camif_probe(struct platform_device *pdev) camif->dev = dev; - if (!pdata || !pdata->gpio_get || !pdata->gpio_put) { + if (!pdata) { dev_err(dev, "wrong platform data\n"); return -EINVAL; } @@ -429,9 +428,7 @@ static int s3c_camif_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = pdata->gpio_get(); - if (ret < 0) - return ret; + /* FIXME: get GPIOs here */ ret = s3c_camif_create_subdev(camif); if (ret < 0) @@ -504,14 +501,12 @@ err_disable: err_clk: s3c_camif_unregister_subdev(camif); err_sd: - pdata->gpio_put(); return ret; } static void s3c_camif_remove(struct platform_device *pdev) { struct camif_dev *camif = platform_get_drvdata(pdev); - struct s3c_camif_plat_data *pdata = &camif->pdata; media_device_unregister(&camif->media_dev); media_device_cleanup(&camif->media_dev); @@ -521,7 +516,6 @@ static void s3c_camif_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); camif_clk_put(camif); s3c_camif_unregister_subdev(camif); - pdata->gpio_put(); } static int s3c_camif_runtime_resume(struct device *dev) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 57edb42463e8..358a66ab8e48 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -464,14 +464,21 @@ static void airspy_disconnect(struct usb_interface *intf) dev_dbg(s->dev, "\n"); - mutex_lock(&s->vb_queue_lock); + /* + * vb2_video_unregister_device() releases the vb2 queue, which + * triggers airspy_stop_streaming() if streaming is active. + * stop_streaming() dereferences s->udev via airspy_ctrl_msg() and + * airspy_free_stream_bufs(), so it must run before s->udev is + * cleared. vb2_video_unregister_device() locks vb_queue_lock + * internally and stop_streaming() locks v4l2_lock, so neither may + * be held by the caller. + */ + v4l2_device_disconnect(&s->v4l2_dev); + vb2_video_unregister_device(&s->vdev); + mutex_lock(&s->v4l2_lock); - /* No need to keep the urbs around after disconnection */ s->udev = NULL; - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); v4l2_device_put(&s->v4l2_dev); } diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index 9c71b32552df..b24ceef497e4 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -443,6 +443,11 @@ static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) int ret; struct cx231xx *dev = snd_pcm_substream_chip(substream); + if (!dev) { + pr_err("cx231xx: called with null device\n"); + return -ENODEV; + } + dev_dbg(dev->dev, "closing device\n"); /* inform hardware to stop streaming */ diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 2cd4e333bc4b..70aa99fead27 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -898,7 +898,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (rc) return rc; - if (vb2_is_busy(&dev->vidq)) { + if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) { dev_err(dev->dev, "%s: queue busy\n", __func__); return -EBUSY; } @@ -933,7 +933,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) if (dev->norm == norm) return 0; - if (vb2_is_busy(&dev->vidq)) + if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) return -EBUSY; dev->norm = norm; diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index fbfb74eab475..e3c0f5e491e3 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2677,6 +2677,28 @@ const struct em28xx_board em28xx_boards[] = { .gpio = mygica_utv3_tuner_audio_gpio, } }, }, + /* eb1a:8286 StarTech SVID2USB232 + * Empia EM28281 with integrated TVP5150-compatible video decoder. + * Composite and S-Video inputs, stereo line-in audio. + */ + [EM28281_BOARD_STARTECH_SVID2USB232] = { + .name = "StarTech SVID2USB232", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .has_dvb = 0, + .decoder = EM28XX_BUILTIN, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = EM2828X_COMPOSITE, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = EM2828X_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2828X_BOARD_HAUPPAUGE_USB_LIVE2] = { .name = "Hauppauge USB Live2", .vchannels = 2, @@ -2946,6 +2968,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD }, { USB_DEVICE(0x2040, 0xc220), .driver_info = EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 }, + { USB_DEVICE(0xeb1a, 0x8286), + .driver_info = EM28281_BOARD_STARTECH_SVID2USB232 }, { USB_DEVICE(0x2040, 0x0360), .driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 }, { USB_DEVICE(0x2040, 0x8360), @@ -3859,6 +3883,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, dev->wait_after_write = 0; dev->eeprom_addrwidth_16bit = 1; break; + case CHIP_ID_EM28281: + chip_name = "em28281"; + dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; + break; case CHIP_ID_EM2883: chip_name = "em2882/3"; dev->wait_after_write = 0; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 938f1980d448..8482fc4045ea 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -29,7 +29,7 @@ #include <media/dmxdev.h> #include <media/tuner.h> #include "tuner-simple.h" -#include <linux/gpio.h> +#include <linux/gpio/legacy.h> #include "lgdt330x.h" #include "lgdt3305.h" diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 68a0fcc2fa72..8931733a8e24 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -283,6 +283,7 @@ enum em28xx_chip_id { CHIP_ID_EM2884 = 68, CHIP_ID_EM28174 = 113, CHIP_ID_EM28178 = 114, + CHIP_ID_EM28281 = 145, CHIP_ID_EM2828X = 148, }; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 711f281613f5..b26f2d4a228d 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -150,6 +150,7 @@ #define EM2828X_BOARD_HAUPPAUGE_955_V2 110 #define EM2828X_BOARD_HAUPPAUGE_975_V2 111 #define EM28178_BOARD_PCTV_461E_V3 112 +#define EM28281_BOARD_STARTECH_SVID2USB232 113 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index ec80ea9cc173..458ce99916ad 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -3,6 +3,7 @@ * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/dma-mapping.h> @@ -10,6 +11,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -22,6 +24,9 @@ #include "mc.h" +static DEFINE_MUTEX(tegra_mc_debugfs_root_lock); +static struct dentry *tegra_mc_debugfs_root; + static const struct of_device_id tegra_mc_of_match[] = { #ifdef CONFIG_ARCH_TEGRA_2x_SOC { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, @@ -598,6 +603,13 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) if (!status) return IRQ_NONE; + if (!mc->soc->regs) { + dev_err_ratelimited(mc->dev, + "MC error interrupt 0x%08lx with no error register map, Clearing.\n", + status); + goto clear; + } + for_each_set_bit(bit, &status, 32) { const char *error = tegra_mc_status_names[bit] ?: "unknown"; const char *client = "unknown", *desc; @@ -736,6 +748,7 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) desc, perm); } +clear: /* clear interrupts */ if (mc->soc->num_channels) { mc_ch_writel(mc, channel, status, MC_INTSTATUS); @@ -778,7 +791,7 @@ struct icc_node *tegra_mc_icc_xlate(const struct of_phandle_args *spec, void *da struct icc_node *node; list_for_each_entry(node, &mc->provider.nodes, node_list) { - if (node->id == spec->args[0]) + if (tegra_mc_client_id_from_node(node) == spec->args[0]) return node; } @@ -834,6 +847,7 @@ const struct tegra_mc_icc_ops tegra_mc_icc_ops = { */ static int tegra_mc_interconnect_setup(struct tegra_mc *mc) { + int node_id = dev_to_node(mc->dev); struct icc_node *node; unsigned int i; int err; @@ -854,31 +868,40 @@ static int tegra_mc_interconnect_setup(struct tegra_mc *mc) icc_provider_init(&mc->provider); /* create Memory Controller node */ - node = icc_node_create(TEGRA_ICC_MC); + node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_MC); if (IS_ERR(node)) return PTR_ERR(node); - node->name = "Memory Controller"; + if (node_id == NUMA_NO_NODE) + node->name = "Memory Controller"; + else + node->name = dev_name(mc->dev); + icc_node_add(node, &mc->provider); /* link Memory Controller to External Memory Controller */ - err = icc_link_create(node, TEGRA_ICC_EMC); + err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_EMC); if (err) goto remove_nodes; for (i = 0; i < mc->soc->num_clients; i++) { /* create MC client node */ - node = icc_node_create(mc->soc->clients[i].id); + node = tegra_mc_icc_node_create(node_id, mc->soc->clients[i].id); if (IS_ERR(node)) { err = PTR_ERR(node); goto remove_nodes; } - node->name = mc->soc->clients[i].name; + if (node_id == NUMA_NO_NODE) + node->name = mc->soc->clients[i].name; + else + node->name = devm_kasprintf(mc->dev, GFP_KERNEL, "%d-%s", + node_id, mc->soc->clients[i].name); + icc_node_add(node, &mc->provider); /* link Memory Client to Memory Controller */ - err = icc_link_create(node, TEGRA_ICC_MC); + err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_MC); if (err) goto remove_nodes; @@ -957,7 +980,16 @@ static int tegra_mc_probe(struct platform_device *pdev) if (IS_ERR(mc->regs)) return PTR_ERR(mc->regs); - mc->debugfs.root = debugfs_create_dir("mc", NULL); + scoped_guard(mutex, &tegra_mc_debugfs_root_lock) { + if (!tegra_mc_debugfs_root) + tegra_mc_debugfs_root = debugfs_create_dir("mc", NULL); + + if (dev_to_node(mc->dev) == NUMA_NO_NODE) + mc->debugfs.root = tegra_mc_debugfs_root; + else + mc->debugfs.root = debugfs_create_dir(dev_name(mc->dev), + tegra_mc_debugfs_root); + } if (mc->soc->ops && mc->soc->ops->probe) { err = mc->soc->ops->probe(mc); diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index e94d265d7b67..01499292dd5f 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -8,6 +8,7 @@ #include <linux/bits.h> #include <linux/io.h> +#include <linux/numa.h> #include <linux/types.h> #include <soc/tegra/mc.h> @@ -167,6 +168,42 @@ icc_provider_to_tegra_mc(struct icc_provider *provider) return container_of(provider, struct tegra_mc, provider); } +/* + * Compose a globally-unique ICC node ID. On single-socket + * systems (NUMA_NO_NODE), the SoC client ID is returned unchanged. + * On multi-socket systems, the NUMA node ID is encoded in the + * upper bits of the returned ID. + * + * The client ID field is sized to keep composed IDs below + * ICC_DYN_ID_START (the start of the ICC core's dynamic-ID range). + */ +#define TEGRA_MC_CLIENT_ID_BITS 12 +#define TEGRA_MC_CLIENT_ID_MASK ((1U << TEGRA_MC_CLIENT_ID_BITS) - 1) + +static inline u32 tegra_mc_get_client_id(int node_id, int id) +{ + if (node_id == NUMA_NO_NODE) + return id; + + return ((node_id + 1) << TEGRA_MC_CLIENT_ID_BITS) | id; +} + +static inline struct icc_node *tegra_mc_icc_node_create(int node_id, int id) +{ + return icc_node_create(tegra_mc_get_client_id(node_id, id)); +} + +static inline int tegra_mc_icc_link_create(struct icc_node *node, int node_id, int id) +{ + return icc_link_create(node, tegra_mc_get_client_id(node_id, id)); +} + +/* Return the SoC client ID encoded in an ICC node ID. */ +static inline u32 tegra_mc_client_id_from_node(const struct icc_node *node) +{ + return node->id & TEGRA_MC_CLIENT_ID_MASK; +} + static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch, unsigned long offset) { diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index 2a4cb64c4c4c..c9583b66f18d 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -3,15 +3,21 @@ * Copyright (C) 2019-2025 NVIDIA CORPORATION. All rights reserved. */ +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/debugfs.h> +#include <linux/fs.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <soc/tegra/bpmp.h> #include "mc.h" +static DEFINE_MUTEX(tegra_emc_debugfs_root_lock); +static struct dentry *tegra_emc_debugfs_root; + struct tegra186_emc_dvfs { unsigned long latency; unsigned long rate; @@ -206,7 +212,17 @@ static int tegra186_emc_get_emc_dvfs_latency(struct tegra186_emc *emc) return err; } - emc->debugfs.root = debugfs_create_dir("emc", NULL); + scoped_guard(mutex, &tegra_emc_debugfs_root_lock) { + if (!tegra_emc_debugfs_root) + tegra_emc_debugfs_root = debugfs_create_dir("emc", NULL); + + if (dev_to_node(emc->dev) == NUMA_NO_NODE) + emc->debugfs.root = tegra_emc_debugfs_root; + else + emc->debugfs.root = debugfs_create_dir(dev_name(emc->dev), + tegra_emc_debugfs_root); + } + debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc, &tegra186_emc_debug_available_rates_fops); debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc, @@ -238,7 +254,7 @@ tegra186_emc_of_icc_xlate(const struct of_phandle_args *spec, void *data) /* External Memory is the only possible ICC route */ list_for_each_entry(node, &provider->nodes, node_list) { - if (node->id != TEGRA_ICC_EMEM) + if (tegra_mc_client_id_from_node(node) != TEGRA_ICC_EMEM) continue; return node; @@ -257,6 +273,7 @@ static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pe static int tegra186_emc_interconnect_init(struct tegra186_emc *emc) { + int node_id = dev_to_node(emc->dev->parent); struct icc_node *node; int err; @@ -270,26 +287,36 @@ static int tegra186_emc_interconnect_init(struct tegra186_emc *emc) icc_provider_init(&emc->provider); /* create External Memory Controller node */ - node = icc_node_create(TEGRA_ICC_EMC); - if (IS_ERR(node)) - return PTR_ERR(node); + node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_EMC); + if (IS_ERR(node)) { + err = PTR_ERR(node); + goto remove_nodes; + } + + if (node_id == NUMA_NO_NODE) + node->name = "External Memory Controller"; + else + node->name = dev_name(emc->dev); - node->name = "External Memory Controller"; icc_node_add(node, &emc->provider); /* link External Memory Controller to External Memory (DRAM) */ - err = icc_link_create(node, TEGRA_ICC_EMEM); + err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_EMEM); if (err) goto remove_nodes; /* create External Memory node */ - node = icc_node_create(TEGRA_ICC_EMEM); + node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_EMEM); if (IS_ERR(node)) { err = PTR_ERR(node); goto remove_nodes; } - node->name = "External Memory (DRAM)"; + if (node_id == NUMA_NO_NODE) + node->name = "External Memory (DRAM)"; + else + node->name = devm_kasprintf(emc->dev, GFP_KERNEL, "%d-dram", node_id); + icc_node_add(node, &emc->provider); err = icc_provider_register(&emc->provider); @@ -382,6 +409,16 @@ static void tegra186_emc_remove(struct platform_device *pdev) debugfs_remove_recursive(emc->debugfs.root); + scoped_guard(mutex, &tegra_emc_debugfs_root_lock) { + if (emc->debugfs.root == tegra_emc_debugfs_root) { + tegra_emc_debugfs_root = NULL; + } else if (tegra_emc_debugfs_root && + simple_empty(tegra_emc_debugfs_root)) { + debugfs_remove(tegra_emc_debugfs_root); + tegra_emc_debugfs_root = NULL; + } + } + mc->bpmp = NULL; tegra_bpmp_put(emc->bpmp); } diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f0bc0b5a6f4a..86fa99022cb3 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -19,7 +19,7 @@ #include <linux/mfd/core.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/of.h> /* diff --git a/drivers/mfd/altera-a10sr.c b/drivers/mfd/altera-a10sr.c index d53e433ab5c1..9c0262228655 100644 --- a/drivers/mfd/altera-a10sr.c +++ b/drivers/mfd/altera-a10sr.c @@ -155,7 +155,7 @@ MODULE_DEVICE_TABLE(of, altr_a10sr_spi_of_match); static const struct spi_device_id altr_a10sr_spi_ids[] = { { .name = "a10sr" }, - { }, + { } }; MODULE_DEVICE_TABLE(spi, altr_a10sr_spi_ids); diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index eaa2b2bc5dd0..04baf5a1c652 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -255,12 +255,12 @@ static void arizona_spi_remove(struct spi_device *spi) } static const struct spi_device_id arizona_spi_ids[] = { - { "wm5102", WM5102 }, - { "wm5110", WM5110 }, - { "wm8280", WM8280 }, - { "wm1831", WM1831 }, - { "cs47l24", CS47L24 }, - { }, + { .name = "wm5102", .driver_data = WM5102 }, + { .name = "wm5110", .driver_data = WM5110 }, + { .name = "wm8280", .driver_data = WM8280 }, + { .name = "wm1831", .driver_data = WM1831 }, + { .name = "cs47l24", .driver_data = CS47L24 }, + { } }; MODULE_DEVICE_TABLE(spi, arizona_spi_ids); diff --git a/drivers/mfd/cs40l50-spi.c b/drivers/mfd/cs40l50-spi.c index 53526b595a0d..a635951ca6b7 100644 --- a/drivers/mfd/cs40l50-spi.c +++ b/drivers/mfd/cs40l50-spi.c @@ -40,8 +40,8 @@ static void cs40l50_spi_remove(struct spi_device *spi) } static const struct spi_device_id cs40l50_id_spi[] = { - { "cs40l50" }, - {} + { .name = "cs40l50" }, + { } }; MODULE_DEVICE_TABLE(spi, cs40l50_id_spi); diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index be5f2b34e18a..29cf2c17fde1 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -63,12 +63,12 @@ static void da9052_spi_remove(struct spi_device *spi) } static const struct spi_device_id da9052_spi_id[] = { - {"da9052", DA9052}, - {"da9053-aa", DA9053_AA}, - {"da9053-ba", DA9053_BA}, - {"da9053-bb", DA9053_BB}, - {"da9053-bc", DA9053_BC}, - {} + { .name = "da9052", .driver_data = DA9052 }, + { .name = "da9053-aa", .driver_data = DA9053_AA }, + { .name = "da9053-ba", .driver_data = DA9053_BA }, + { .name = "da9053-bb", .driver_data = DA9053_BB }, + { .name = "da9053-bc", .driver_data = DA9053_BC }, + { } }; static struct spi_driver da9052_spi_driver = { diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 21e68a382b11..6672c55f2ebc 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -32,7 +32,7 @@ #include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/mfd/core.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h> @@ -2285,7 +2285,7 @@ void db8500_prcmu_system_reset(u16 reset_code) /** * db8500_prcmu_get_reset_code - Retrieve SW reset reason code * - * Retrieves the reset reason code stored by prcmu_system_reset() before + * Retrieves the reset reason code stored by db8500_prcmu_system_reset() before * last restart. */ u16 db8500_prcmu_get_reset_code(void) @@ -3041,7 +3041,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev) db8500_irq_init(np); - prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); + db8500_prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); err = mfd_add_devices(&pdev->dev, 0, common_prcmu_devs, ARRAY_SIZE(common_prcmu_devs), NULL, 0, db8500_irq_domain); diff --git a/drivers/mfd/intel-m10-bmc-spi.c b/drivers/mfd/intel-m10-bmc-spi.c index cfa620f0c70e..94b9c99bb4f8 100644 --- a/drivers/mfd/intel-m10-bmc-spi.c +++ b/drivers/mfd/intel-m10-bmc-spi.c @@ -160,9 +160,9 @@ static const struct intel_m10bmc_platform_info m10bmc_spi_n5010 = { }; static const struct spi_device_id m10bmc_spi_id[] = { - { "m10-n3000", (kernel_ulong_t)&m10bmc_spi_n3000 }, - { "m10-d5005", (kernel_ulong_t)&m10bmc_spi_d5005 }, - { "m10-n5010", (kernel_ulong_t)&m10bmc_spi_n5010 }, + { .name = "m10-n3000", .driver_data = (kernel_ulong_t)&m10bmc_spi_n3000 }, + { .name = "m10-d5005", .driver_data = (kernel_ulong_t)&m10bmc_spi_d5005 }, + { .name = "m10-n5010", .driver_data = (kernel_ulong_t)&m10bmc_spi_n5010 }, { } }; MODULE_DEVICE_TABLE(spi, m10bmc_spi_id); diff --git a/drivers/mfd/madera-spi.c b/drivers/mfd/madera-spi.c index ce9e90322c9c..3fffa21ceadc 100644 --- a/drivers/mfd/madera-spi.c +++ b/drivers/mfd/madera-spi.c @@ -112,15 +112,15 @@ static void madera_spi_remove(struct spi_device *spi) } static const struct spi_device_id madera_spi_ids[] = { - { "cs47l15", CS47L15 }, - { "cs47l35", CS47L35 }, - { "cs47l85", CS47L85 }, - { "cs47l90", CS47L90 }, - { "cs47l91", CS47L91 }, - { "cs42l92", CS42L92 }, - { "cs47l92", CS47L92 }, - { "cs47l93", CS47L93 }, - { "wm1840", WM1840 }, + { .name = "cs47l15", .driver_data = CS47L15 }, + { .name = "cs47l35", .driver_data = CS47L35 }, + { .name = "cs47l85", .driver_data = CS47L85 }, + { .name = "cs47l90", .driver_data = CS47L90 }, + { .name = "cs47l91", .driver_data = CS47L91 }, + { .name = "cs42l92", .driver_data = CS42L92 }, + { .name = "cs47l92", .driver_data = CS47L92 }, + { .name = "cs47l93", .driver_data = CS47L93 }, + { .name = "wm1840", .driver_data = WM1840 }, { } }; MODULE_DEVICE_TABLE(spi, madera_spi_ids); diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index feeccb2c6655..4ed8704c985a 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -202,9 +202,9 @@ static const struct of_device_id cpcap_of_match[] = { MODULE_DEVICE_TABLE(of, cpcap_of_match); static const struct spi_device_id cpcap_spi_ids[] = { - { .name = "cpcap", }, - { .name = "6556002", }, - {}, + { .name = "cpcap" }, + { .name = "6556002" }, + { } }; MODULE_DEVICE_TABLE(spi, cpcap_spi_ids); diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c index fc30663824bb..d3ab4868a523 100644 --- a/drivers/mfd/ocelot-spi.c +++ b/drivers/mfd/ocelot-spi.c @@ -270,7 +270,7 @@ static int ocelot_spi_probe(struct spi_device *spi) } static const struct spi_device_id ocelot_spi_ids[] = { - { "vsc7512", 0 }, + { .name = "vsc7512" }, { } }; MODULE_DEVICE_TABLE(spi, ocelot_spi_ids); diff --git a/drivers/mfd/rk8xx-spi.c b/drivers/mfd/rk8xx-spi.c index 3405fb82ff9f..bb85fe60518f 100644 --- a/drivers/mfd/rk8xx-spi.c +++ b/drivers/mfd/rk8xx-spi.c @@ -104,7 +104,7 @@ static const struct of_device_id rk8xx_spi_of_match[] = { MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match); static const struct spi_device_id rk8xx_spi_id_table[] = { - { "rk806", 0 }, + { .name = "rk806" }, { } }; MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table); diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c index e07f21482439..cdb0f9797ec6 100644 --- a/drivers/mfd/rsmu_spi.c +++ b/drivers/mfd/rsmu_spi.c @@ -239,12 +239,12 @@ static void rsmu_spi_remove(struct spi_device *client) } static const struct spi_device_id rsmu_spi_id[] = { - { "8a34000", RSMU_CM }, - { "8a34001", RSMU_CM }, - { "8a34002", RSMU_CM }, - { "82p33810", RSMU_SABRE }, - { "82p33811", RSMU_SABRE }, - {} + { .name = "8a34000", .driver_data = RSMU_CM }, + { .name = "8a34001", .driver_data = RSMU_CM }, + { .name = "8a34002", .driver_data = RSMU_CM }, + { .name = "82p33810", .driver_data = RSMU_SABRE }, + { .name = "82p33811", .driver_data = RSMU_SABRE }, + { } }; MODULE_DEVICE_TABLE(spi, rsmu_spi_id); diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c index aa052f646623..9a8e6add8fca 100644 --- a/drivers/mfd/sprd-sc27xx-spi.c +++ b/drivers/mfd/sprd-sc27xx-spi.c @@ -61,6 +61,7 @@ static const struct mfd_cell sc2730_devices[] = { MFD_CELL_OF("sc2730-efuse", NULL, NULL, 0, 0, "sprd,sc2730-efuse"), MFD_CELL_OF("sc2730-eic", NULL, NULL, 0, 0, "sprd,sc2730-eic"), MFD_CELL_OF("sc2730-fgu", NULL, NULL, 0, 0, "sprd,sc2730-fgu"), + MFD_CELL_NAME("sc2730-regulator"), MFD_CELL_OF("sc2730-rtc", NULL, NULL, 0, 0, "sprd,sc2730-rtc"), MFD_CELL_OF("sc2730-vibrator", NULL, NULL, 0, 0, "sprd,sc2730-vibrator"), }; @@ -294,7 +295,7 @@ MODULE_DEVICE_TABLE(of, sprd_pmic_match); static const struct spi_device_id sprd_pmic_spi_ids[] = { { .name = "sc2730", .driver_data = PMIC_TYPE_SC2730 }, { .name = "sc2731", .driver_data = PMIC_TYPE_SC2731 }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, sprd_pmic_spi_ids); diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c index dea31efface6..22a3da062dee 100644 --- a/drivers/mfd/stmpe-spi.c +++ b/drivers/mfd/stmpe-spi.c @@ -121,12 +121,12 @@ static const struct of_device_id stmpe_spi_of_match[] = { MODULE_DEVICE_TABLE(of, stmpe_spi_of_match); static const struct spi_device_id stmpe_spi_id[] = { - { "stmpe610", STMPE610 }, - { "stmpe801", STMPE801 }, - { "stmpe811", STMPE811 }, - { "stmpe1601", STMPE1601 }, - { "stmpe2401", STMPE2401 }, - { "stmpe2403", STMPE2403 }, + { .name = "stmpe610", .driver_data = STMPE610 }, + { .name = "stmpe801", .driver_data = STMPE801 }, + { .name = "stmpe811", .driver_data = STMPE811 }, + { .name = "stmpe1601", .driver_data = STMPE1601 }, + { .name = "stmpe2401", .driver_data = STMPE2401 }, + { .name = "stmpe2403", .driver_data = STMPE2403 }, { } }; MODULE_DEVICE_TABLE(spi, stmpe_spi_id); diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c index 2a77dccd6059..2442a2e67d67 100644 --- a/drivers/mfd/tps65912-spi.c +++ b/drivers/mfd/tps65912-spi.c @@ -43,7 +43,7 @@ static int tps65912_spi_probe(struct spi_device *spi) } static const struct spi_device_id tps65912_spi_id_table[] = { - { "tps65912", 0 }, + { .name = "tps65912" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(spi, tps65912_spi_id_table); diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index 888737b8e7be..36be8e69f45c 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -96,8 +96,7 @@ static int vprbrd_probe(struct usb_interface *interface, return 0; error: - if (vb) - kfree(vb); + kfree(vb); return ret; } diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 54c87267917b..1e519fd9a9e1 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -77,14 +77,14 @@ static const struct dev_pm_ops wm831x_spi_pm = { }; static const struct spi_device_id wm831x_spi_ids[] = { - { "wm8310", WM8310 }, - { "wm8311", WM8311 }, - { "wm8312", WM8312 }, - { "wm8320", WM8320 }, - { "wm8321", WM8321 }, - { "wm8325", WM8325 }, - { "wm8326", WM8326 }, - { }, + { .name = "wm8310", .driver_data = WM8310 }, + { .name = "wm8311", .driver_data = WM8311 }, + { .name = "wm8312", .driver_data = WM8312 }, + { .name = "wm8320", .driver_data = WM8320 }, + { .name = "wm8321", .driver_data = WM8321 }, + { .name = "wm8325", .driver_data = WM8325 }, + { .name = "wm8326", .driver_data = WM8326 }, + { } }; static struct spi_driver wm831x_spi_driver = { diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e044fc733b8c..e1bec8164221 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4857,12 +4857,6 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) __func__, slave); res = dev_set_mac_address(slave->dev, addr, NULL); if (res) { - /* TODO: consider downing the slave - * and retry ? - * User should expect communications - * breakage anyway until ARP finish - * updating, so... - */ slave_dbg(bond_dev, slave->dev, "%s: err %d\n", __func__, res); goto unwind; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index fd282a1700fb..d394f1f43b68 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2668,8 +2668,25 @@ static void macb_free_consistent(struct macb *bp) dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - kfree(queue->tx_skb); - queue->tx_skb = NULL; + if (queue->tx_skb) { + unsigned int dropped = 0, tail; + + for (tail = queue->tx_tail; tail != queue->tx_head; + tail++) { + if (macb_tx_skb(queue, tail)->skb) + dropped++; + macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0); + } + + queue->stats.tx_dropped += dropped; + bp->dev->stats.tx_dropped += dropped; + + kfree(queue->tx_skb); + queue->tx_skb = NULL; + } + + queue->tx_head = 0; + queue->tx_tail = 0; queue->tx_ring = NULL; queue->rx_ring = NULL; } diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h index 63ec253ac788..9e696d55e512 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.h +++ b/drivers/net/ethernet/intel/igb/e1000_82575.h @@ -87,6 +87,27 @@ union e1000_adv_rx_desc { } wb; /* writeback */ }; +#define E1000_RSS_TYPE_NO_HASH 0 +#define E1000_RSS_TYPE_HASH_TCP_IPV4 1 +#define E1000_RSS_TYPE_HASH_IPV4 2 +#define E1000_RSS_TYPE_HASH_TCP_IPV6 3 +#define E1000_RSS_TYPE_HASH_IPV6_EX 4 +#define E1000_RSS_TYPE_HASH_IPV6 5 +#define E1000_RSS_TYPE_HASH_TCP_IPV6_EX 6 +#define E1000_RSS_TYPE_HASH_UDP_IPV4 7 +#define E1000_RSS_TYPE_HASH_UDP_IPV6 8 +#define E1000_RSS_TYPE_HASH_UDP_IPV6_EX 9 + +#define E1000_RSS_TYPE_MASK GENMASK(3, 0) + +#define E1000_RSS_L4_TYPES_MASK \ + (BIT(E1000_RSS_TYPE_HASH_TCP_IPV4) | \ + BIT(E1000_RSS_TYPE_HASH_TCP_IPV6) | \ + BIT(E1000_RSS_TYPE_HASH_TCP_IPV6_EX) | \ + BIT(E1000_RSS_TYPE_HASH_UDP_IPV4) | \ + BIT(E1000_RSS_TYPE_HASH_UDP_IPV6) | \ + BIT(E1000_RSS_TYPE_HASH_UDP_IPV6_EX)) + #define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0 #define E1000_RXDADV_HDRBUFLEN_SHIFT 5 #define E1000_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */ diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 0fff1df81b7b..8c9b02058cec 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -495,6 +495,7 @@ struct hwmon_buff { #define IGB_N_PEROUT 2 #define IGB_N_SDP 4 #define IGB_RETA_SIZE 128 +#define IGB_RSS_KEY_SIZE 40 enum igb_filter_match_flags { IGB_FILTER_FLAG_ETHER_TYPE = 0x1, @@ -655,6 +656,7 @@ struct igb_adapter { struct i2c_client *i2c_client; u32 rss_indir_tbl_init; u8 rss_indir_tbl[IGB_RETA_SIZE]; + u8 rss_key[IGB_RSS_KEY_SIZE]; unsigned long link_check_timeout; int copper_tries; @@ -735,6 +737,7 @@ void igb_down(struct igb_adapter *); void igb_reinit_locked(struct igb_adapter *); void igb_reset(struct igb_adapter *); int igb_reinit_queues(struct igb_adapter *); +void igb_write_rss_key(struct igb_adapter *adapter); void igb_write_rss_indir_tbl(struct igb_adapter *); int igb_set_spd_dplx(struct igb_adapter *, u32, u8); int igb_setup_tx_resources(struct igb_ring *); diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index f7938c1da835..65014a54a6d1 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -3019,6 +3019,27 @@ static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) return ret; } +/** + * igb_write_rss_key - Program the RSS key into device registers + * @adapter: board private structure + * + * Write the RSS key stored in adapter->rss_key to the E1000 hardware registers. + * Each 32-bit chunk of the key is read using get_unaligned_le32() and written + * to the appropriate register. + */ +void igb_write_rss_key(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + + ASSERT_RTNL(); + + for (int i = 0; i < IGB_RSS_KEY_SIZE / 4; i++) { + u32 val = get_unaligned_le32(&adapter->rss_key[i * 4]); + + wr32(E1000_RSSRK(i), val); + } +} + static int igb_get_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct igb_adapter *adapter = netdev_priv(netdev); @@ -3276,10 +3297,12 @@ static int igb_get_rxfh(struct net_device *netdev, int i; rxfh->hfunc = ETH_RSS_HASH_TOP; - if (!rxfh->indir) - return 0; - for (i = 0; i < IGB_RETA_SIZE; i++) - rxfh->indir[i] = adapter->rss_indir_tbl[i]; + if (rxfh->indir) + for (i = 0; i < IGB_RETA_SIZE; i++) + rxfh->indir[i] = adapter->rss_indir_tbl[i]; + + if (rxfh->key) + memcpy(rxfh->key, adapter->rss_key, sizeof(adapter->rss_key)); return 0; } @@ -3319,6 +3342,11 @@ void igb_write_rss_indir_tbl(struct igb_adapter *adapter) } } +static u32 igb_get_rxfh_key_size(struct net_device *netdev) +{ + return IGB_RSS_KEY_SIZE; +} + static int igb_set_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh, struct netlink_ext_ack *extack) @@ -3329,35 +3357,39 @@ static int igb_set_rxfh(struct net_device *netdev, u32 num_queues; /* We do not allow change in unsupported parameters */ - if (rxfh->key || - (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && - rxfh->hfunc != ETH_RSS_HASH_TOP)) + if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && + rxfh->hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; - if (!rxfh->indir) - return 0; - num_queues = adapter->rss_queues; + if (rxfh->indir) { + num_queues = adapter->rss_queues; - switch (hw->mac.type) { - case e1000_82576: - /* 82576 supports 2 RSS queues for SR-IOV */ - if (adapter->vfs_allocated_count) - num_queues = 2; - break; - default: - break; - } + switch (hw->mac.type) { + case e1000_82576: + /* 82576 supports 2 RSS queues for SR-IOV */ + if (adapter->vfs_allocated_count) + num_queues = 2; + break; + default: + break; + } + + /* Verify user input. */ + for (i = 0; i < IGB_RETA_SIZE; i++) + if (rxfh->indir[i] >= num_queues) + return -EINVAL; - /* Verify user input. */ - for (i = 0; i < IGB_RETA_SIZE; i++) - if (rxfh->indir[i] >= num_queues) - return -EINVAL; + for (i = 0; i < IGB_RETA_SIZE; i++) + adapter->rss_indir_tbl[i] = rxfh->indir[i]; - for (i = 0; i < IGB_RETA_SIZE; i++) - adapter->rss_indir_tbl[i] = rxfh->indir[i]; + igb_write_rss_indir_tbl(adapter); + } - igb_write_rss_indir_tbl(adapter); + if (rxfh->key) { + memcpy(adapter->rss_key, rxfh->key, sizeof(adapter->rss_key)); + igb_write_rss_key(adapter); + } return 0; } @@ -3483,6 +3515,7 @@ static const struct ethtool_ops igb_ethtool_ops = { .get_module_eeprom = igb_get_module_eeprom, .get_rxfh_indir_size = igb_get_rxfh_indir_size, .get_rxfh = igb_get_rxfh, + .get_rxfh_key_size = igb_get_rxfh_key_size, .set_rxfh = igb_set_rxfh, .get_rxfh_fields = igb_get_rxfh_fields, .set_rxfh_fields = igb_set_rxfh_fields, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index a1e89a375744..d4a897a8c82c 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -4048,6 +4048,9 @@ static int igb_sw_init(struct igb_adapter *adapter) pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word); + /* init RSS key */ + netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key)); + /* set default ring sizes */ adapter->tx_ring_count = IGB_DEFAULT_TXD; adapter->rx_ring_count = IGB_DEFAULT_RXD; @@ -4522,11 +4525,8 @@ static void igb_setup_mrqc(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; u32 mrqc, rxcsum; u32 j, num_rx_queues; - u32 rss_key[10]; - netdev_rss_key_fill(rss_key, sizeof(rss_key)); - for (j = 0; j < 10; j++) - wr32(E1000_RSSRK(j), rss_key[j]); + igb_write_rss_key(adapter); num_rx_queues = adapter->rss_queues; @@ -8820,10 +8820,19 @@ static inline void igb_rx_hash(struct igb_ring *ring, union e1000_adv_rx_desc *rx_desc, struct sk_buff *skb) { - if (ring->netdev->features & NETIF_F_RXHASH) - skb_set_hash(skb, - le32_to_cpu(rx_desc->wb.lower.hi_dword.rss), - PKT_HASH_TYPE_L3); + u16 rss_type; + + if (!(ring->netdev->features & NETIF_F_RXHASH)) + return; + + rss_type = le16_to_cpu(rx_desc->wb.lower.lo_dword.pkt_info) & + E1000_RSS_TYPE_MASK; + if (!rss_type) + return; + + skb_set_hash(skb, le32_to_cpu(rx_desc->wb.lower.hi_dword.rss), + (E1000_RSS_L4_TYPES_MASK & BIT(rss_type)) ? + PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); } /** diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 46d625b15f44..17f213cc93e4 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -30,6 +30,7 @@ void igc_ethtool_set_ops(struct net_device *); #define MAX_ETYPE_FILTER 8 #define IGC_RETA_SIZE 128 +#define IGC_RSS_KEY_SIZE 40 /* SDP support */ #define IGC_N_EXTTS 2 @@ -302,6 +303,7 @@ struct igc_adapter { unsigned int nfc_rule_count; u8 rss_indir_tbl[IGC_RETA_SIZE]; + u8 rss_key[IGC_RSS_KEY_SIZE]; unsigned long link_check_timeout; struct igc_info ei; @@ -361,6 +363,7 @@ unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter); void igc_set_flag_queue_pairs(struct igc_adapter *adapter, const u32 max_rss_queues); int igc_reinit_queues(struct igc_adapter *adapter); +void igc_write_rss_key(struct igc_adapter *adapter); void igc_write_rss_indir_tbl(struct igc_adapter *adapter); bool igc_has_link(struct igc_adapter *adapter); void igc_reset(struct igc_adapter *adapter); diff --git a/drivers/net/ethernet/intel/igc/igc_base.c b/drivers/net/ethernet/intel/igc/igc_base.c index 1613b562d17c..ab9120a3127f 100644 --- a/drivers/net/ethernet/intel/igc/igc_base.c +++ b/drivers/net/ethernet/intel/igc/igc_base.c @@ -114,11 +114,35 @@ static s32 igc_setup_copper_link_base(struct igc_hw *hw) u32 ctrl; ctrl = rd32(IGC_CTRL); - ctrl |= IGC_CTRL_SLU; - ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX); - wr32(IGC_CTRL, ctrl); - - ret_val = igc_setup_copper_link(hw); + ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX | + IGC_CTRL_SPEED_MASK | IGC_CTRL_FD); + + if (hw->mac.autoneg_enabled) { + ctrl |= IGC_CTRL_SLU; + wr32(IGC_CTRL, ctrl); + ret_val = igc_setup_copper_link(hw); + } else { + ctrl |= IGC_CTRL_SLU | IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX; + + switch (hw->mac.forced_speed_duplex) { + case IGC_FORCED_10H: + ctrl |= IGC_CTRL_SPEED_10; + break; + case IGC_FORCED_10F: + ctrl |= IGC_CTRL_SPEED_10 | IGC_CTRL_FD; + break; + case IGC_FORCED_100H: + ctrl |= IGC_CTRL_SPEED_100; + break; + case IGC_FORCED_100F: + ctrl |= IGC_CTRL_SPEED_100 | IGC_CTRL_FD; + break; + default: + return -IGC_ERR_CONFIG; + } + wr32(IGC_CTRL, ctrl); + ret_val = igc_setup_copper_link(hw); + } return ret_val; } @@ -443,6 +467,7 @@ static const struct igc_phy_operations igc_phy_ops_base = { .reset = igc_phy_hw_reset, .read_reg = igc_read_phy_reg_gpy, .write_reg = igc_write_phy_reg_gpy, + .force_speed_duplex = igc_force_speed_duplex, }; const struct igc_info igc_base_info = { diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 9482ab11f050..3f504751c2d9 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -129,10 +129,13 @@ #define IGC_ERR_SWFW_SYNC 13 /* Device Control */ +#define IGC_CTRL_FD BIT(0) /* Full Duplex */ #define IGC_CTRL_RST 0x04000000 /* Global reset */ - #define IGC_CTRL_PHY_RST 0x80000000 /* PHY Reset */ #define IGC_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define IGC_CTRL_SPEED_MASK GENMASK(10, 8) +#define IGC_CTRL_SPEED_10 FIELD_PREP(IGC_CTRL_SPEED_MASK, 0) +#define IGC_CTRL_SPEED_100 FIELD_PREP(IGC_CTRL_SPEED_MASK, 1) #define IGC_CTRL_FRCSPD 0x00000800 /* Force Speed */ #define IGC_CTRL_FRCDPX 0x00001000 /* Force Duplex */ #define IGC_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ @@ -673,6 +676,10 @@ #define IGC_GEN_POLL_TIMEOUT 1920 /* PHY Control Register */ +#define MII_CR_SPEED_MASK (BIT(6) | BIT(13)) +#define MII_CR_SPEED_10 0x0000 /* SSM=0, SSL=0: 10 Mb/s */ +#define MII_CR_SPEED_100 BIT(13) /* SSM=0, SSL=1: 100 Mb/s */ +#define MII_CR_DUPLEX_EN BIT(8) /* 0 = Half Duplex, 1 = Full Duplex */ #define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ #define MII_CR_POWER_DOWN 0x0800 /* Power down */ #define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 0122009bedd0..89fe2788a565 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -1460,6 +1460,26 @@ static int igc_ethtool_set_rxnfc(struct net_device *dev, } } +/** + * igc_write_rss_key - Program the RSS key into device registers + * @adapter: board private structure + * + * Write the RSS key stored in adapter->rss_key to the IGC_RSSRK registers. + * Each 32-bit chunk of the key is read using get_unaligned_le32() and written + * to the appropriate register. + */ +void igc_write_rss_key(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + u32 val; + int i; + + for (i = 0; i < IGC_RSS_KEY_SIZE / 4; i++) { + val = get_unaligned_le32(&adapter->rss_key[i * 4]); + wr32(IGC_RSSRK(i), val); + } +} + void igc_write_rss_indir_tbl(struct igc_adapter *adapter) { struct igc_hw *hw = &adapter->hw; @@ -1482,6 +1502,11 @@ void igc_write_rss_indir_tbl(struct igc_adapter *adapter) } } +static u32 igc_ethtool_get_rxfh_key_size(struct net_device *netdev) +{ + return IGC_RSS_KEY_SIZE; +} + static u32 igc_ethtool_get_rxfh_indir_size(struct net_device *netdev) { return IGC_RETA_SIZE; @@ -1494,10 +1519,13 @@ static int igc_ethtool_get_rxfh(struct net_device *netdev, int i; rxfh->hfunc = ETH_RSS_HASH_TOP; - if (!rxfh->indir) - return 0; - for (i = 0; i < IGC_RETA_SIZE; i++) - rxfh->indir[i] = adapter->rss_indir_tbl[i]; + + if (rxfh->indir) + for (i = 0; i < IGC_RETA_SIZE; i++) + rxfh->indir[i] = adapter->rss_indir_tbl[i]; + + if (rxfh->key) + memcpy(rxfh->key, adapter->rss_key, sizeof(adapter->rss_key)); return 0; } @@ -1511,24 +1539,28 @@ static int igc_ethtool_set_rxfh(struct net_device *netdev, int i; /* We do not allow change in unsupported parameters */ - if (rxfh->key || - (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && - rxfh->hfunc != ETH_RSS_HASH_TOP)) + if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && + rxfh->hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; - if (!rxfh->indir) - return 0; - num_queues = adapter->rss_queues; + if (rxfh->indir) { + num_queues = adapter->rss_queues; - /* Verify user input. */ - for (i = 0; i < IGC_RETA_SIZE; i++) - if (rxfh->indir[i] >= num_queues) - return -EINVAL; + /* Verify user input. */ + for (i = 0; i < IGC_RETA_SIZE; i++) + if (rxfh->indir[i] >= num_queues) + return -EINVAL; - for (i = 0; i < IGC_RETA_SIZE; i++) - adapter->rss_indir_tbl[i] = rxfh->indir[i]; + for (i = 0; i < IGC_RETA_SIZE; i++) + adapter->rss_indir_tbl[i] = rxfh->indir[i]; + + igc_write_rss_indir_tbl(adapter); + } - igc_write_rss_indir_tbl(adapter); + if (rxfh->key) { + memcpy(adapter->rss_key, rxfh->key, sizeof(adapter->rss_key)); + igc_write_rss_key(adapter); + } return 0; } @@ -1914,44 +1946,58 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev, ethtool_link_ksettings_add_link_mode(cmd, supported, TP); ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); - /* advertising link modes */ - if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half); - if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full); - if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half); - if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full); - if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full); - if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full); - /* set autoneg settings */ ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); - ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); + if (hw->mac.autoneg_enabled) { + ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); + cmd->base.autoneg = AUTONEG_ENABLE; + + /* advertising link modes only apply when autoneg is on */ + if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Half); + if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Full); + if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Half); + if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Full); + if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 1000baseT_Full); + if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 2500baseT_Full); + + /* Set pause flow control advertising */ + switch (hw->fc.requested_mode) { + case igc_fc_full: + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Pause); + break; + case igc_fc_rx_pause: + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Pause); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); + break; + case igc_fc_tx_pause: + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); + break; + default: + break; + } + } else { + cmd->base.autoneg = AUTONEG_DISABLE; + } - /* Set pause flow control settings */ + /* Pause is always supported */ ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); - switch (hw->fc.requested_mode) { - case igc_fc_full: - ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); - break; - case igc_fc_rx_pause: - ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Asym_Pause); - break; - case igc_fc_tx_pause: - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Asym_Pause); - break; - default: - break; - } - status = pm_runtime_suspended(&adapter->pdev->dev) ? 0 : rd32(IGC_STATUS); @@ -1983,7 +2029,6 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev, cmd->base.duplex = DUPLEX_UNKNOWN; } cmd->base.speed = speed; - cmd->base.autoneg = AUTONEG_ENABLE; /* MDI-X => 2; MDI =>1; Invalid =>0 */ if (hw->phy.media_type == igc_media_type_copper) @@ -2000,37 +2045,50 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev, return 0; } -static int -igc_ethtool_set_link_ksettings(struct net_device *netdev, - const struct ethtool_link_ksettings *cmd) +/** + * igc_handle_autoneg_disabled - Configure forced speed/duplex settings + * @adapter: private driver structure + * @speed: requested speed (must be SPEED_10 or SPEED_100) + * @duplex: requested duplex + * + * Records forced speed/duplex when autoneg is disabled. + * Caller must validate speed before calling this function. + */ +static void igc_handle_autoneg_disabled(struct igc_adapter *adapter, u32 speed, + u8 duplex) { - struct igc_adapter *adapter = netdev_priv(netdev); - struct net_device *dev = adapter->netdev; - struct igc_hw *hw = &adapter->hw; - u16 advertised = 0; + struct igc_mac_info *mac = &adapter->hw.mac; - /* When adapter in resetting mode, autoneg/speed/duplex - * cannot be changed - */ - if (igc_check_reset_block(hw)) { - netdev_err(dev, "Cannot change link characteristics when reset is active\n"); - return -EINVAL; + switch (speed) { + case SPEED_10: + mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ? + IGC_FORCED_10F : IGC_FORCED_10H; + break; + case SPEED_100: + mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ? + IGC_FORCED_100F : IGC_FORCED_100H; + break; + default: + WARN_ONCE(1, "Unsupported speed %u\n", speed); + return; } - /* MDI setting is only allowed when autoneg enabled because - * some hardware doesn't allow MDI setting when speed or - * duplex is forced. - */ - if (cmd->base.eth_tp_mdix_ctrl) { - if (cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO && - cmd->base.autoneg != AUTONEG_ENABLE) { - netdev_err(dev, "Forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n"); - return -EINVAL; - } - } + mac->autoneg_enabled = false; +} - while (test_and_set_bit(__IGC_RESETTING, &adapter->state)) - usleep_range(1000, 2000); +/** + * igc_handle_autoneg_enabled - Configure autonegotiation advertisement + * @adapter: private driver structure + * @cmd: ethtool link ksettings from user + * + * Records advertised speeds and flow control settings when autoneg + * is enabled. + */ +static void igc_handle_autoneg_enabled(struct igc_adapter *adapter, + const struct ethtool_link_ksettings *cmd) +{ + struct igc_hw *hw = &adapter->hw; + u16 advertised = 0; if (ethtool_link_ksettings_test_link_mode(cmd, advertising, 2500baseT_Full)) @@ -2056,14 +2114,66 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev, 10baseT_Half)) advertised |= ADVERTISE_10_HALF; - if (cmd->base.autoneg == AUTONEG_ENABLE) { - hw->phy.autoneg_advertised = advertised; - if (adapter->fc_autoneg) - hw->fc.requested_mode = igc_fc_default; - } else { - netdev_info(dev, "Force mode currently not supported\n"); + hw->mac.autoneg_enabled = true; + hw->phy.autoneg_advertised = advertised; + if (adapter->fc_autoneg) + hw->fc.requested_mode = igc_fc_default; +} + +static int +igc_ethtool_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct igc_adapter *adapter = netdev_priv(netdev); + struct net_device *dev = adapter->netdev; + struct igc_hw *hw = &adapter->hw; + + /* When adapter in resetting mode, autoneg/speed/duplex + * cannot be changed + */ + if (igc_check_reset_block(hw)) { + netdev_err(dev, "Cannot change link characteristics when reset is active\n"); + return -EINVAL; + } + + if (cmd->base.autoneg != AUTONEG_ENABLE && + cmd->base.autoneg != AUTONEG_DISABLE) { + netdev_info(dev, "Unsupported autoneg setting\n"); + return -EINVAL; + } + + /* MDI setting is only allowed when autoneg enabled because + * some hardware doesn't allow MDI setting when speed or + * duplex is forced. + */ + if (cmd->base.eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO && + cmd->base.autoneg != AUTONEG_ENABLE) { + netdev_err(dev, "Forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n"); + return -EINVAL; + } } + if (cmd->base.autoneg == AUTONEG_DISABLE) { + if (cmd->base.speed != SPEED_10 && cmd->base.speed != SPEED_100) { + netdev_info(dev, "Unsupported speed for forced link\n"); + return -EINVAL; + } + if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL) { + netdev_info(dev, "Duplex must be half or full for forced link\n"); + return -EINVAL; + } + } + + while (test_and_set_bit(__IGC_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + + if (cmd->base.autoneg == AUTONEG_ENABLE) + igc_handle_autoneg_enabled(adapter, cmd); + else + igc_handle_autoneg_disabled(adapter, cmd->base.speed, + cmd->base.duplex); + /* MDI-X => 2; MDI => 1; Auto => 3 */ if (cmd->base.eth_tp_mdix_ctrl) { /* fix up the value for auto (3 => 0) as zero is mapped @@ -2175,6 +2285,7 @@ static const struct ethtool_ops igc_ethtool_ops = { .get_rxnfc = igc_ethtool_get_rxnfc, .set_rxnfc = igc_ethtool_set_rxnfc, .get_rx_ring_count = igc_ethtool_get_rx_ring_count, + .get_rxfh_key_size = igc_ethtool_get_rxfh_key_size, .get_rxfh_indir_size = igc_ethtool_get_rxfh_indir_size, .get_rxfh = igc_ethtool_get_rxfh, .set_rxfh = igc_ethtool_set_rxfh, diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h index be8a49a86d09..62aaee55668a 100644 --- a/drivers/net/ethernet/intel/igc/igc_hw.h +++ b/drivers/net/ethernet/intel/igc/igc_hw.h @@ -73,6 +73,13 @@ struct igc_info { extern const struct igc_info igc_base_info; +enum igc_forced_speed_duplex { + IGC_FORCED_10H, + IGC_FORCED_10F, + IGC_FORCED_100H, + IGC_FORCED_100F, +}; + struct igc_mac_info { struct igc_mac_operations ops; @@ -92,8 +99,9 @@ struct igc_mac_info { bool asf_firmware_present; bool arc_subsystem_valid; - bool autoneg_failed; bool get_link_status; + bool autoneg_enabled; + enum igc_forced_speed_duplex forced_speed_duplex; }; struct igc_nvm_operations { diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c index 7ac6637f8db7..d6f3f6618469 100644 --- a/drivers/net/ethernet/intel/igc/igc_mac.c +++ b/drivers/net/ethernet/intel/igc/igc_mac.c @@ -438,26 +438,23 @@ void igc_config_collision_dist(struct igc_hw *hw) * Checks the status of auto-negotiation after link up to ensure that the * speed and duplex were not forced. If the link needed to be forced, then * flow control needs to be forced also. If auto-negotiation is enabled - * and did not fail, then we configure flow control based on our link - * partner. + * then we configure flow control based on our link partner. */ s32 igc_config_fc_after_link_up(struct igc_hw *hw) { u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg; - struct igc_mac_info *mac = &hw->mac; u16 speed, duplex; s32 ret_val = 0; - /* Check for the case where we have fiber media and auto-neg failed - * so we had to force link. In this case, we need to force the - * configuration of the MAC to match the "fc" parameter. + /* Without autoneg, flow control capability is not exchanged with the + * link partner. IEEE 802.3 prohibits flow control in half-duplex mode. */ - if (mac->autoneg_failed) - ret_val = igc_force_mac_fc(hw); + if (!hw->mac.autoneg_enabled) { + if (hw->mac.forced_speed_duplex == IGC_FORCED_10H || + hw->mac.forced_speed_duplex == IGC_FORCED_100H) + hw->fc.current_mode = igc_fc_none; - if (ret_val) { - hw_dbg("Error forcing flow control settings\n"); - goto out; + goto force_fc; } /* In auto-neg, we need to check and see if Auto-Neg has completed, @@ -472,15 +469,15 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw) ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); if (ret_val) - goto out; + return ret_val; if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) { hw_dbg("Copper PHY and Auto Neg has not completed.\n"); - goto out; + return ret_val; } /* The AutoNeg process has completed, so we now need to @@ -492,11 +489,11 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw) ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_nway_adv_reg); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY, &mii_nway_lp_ability_reg); if (ret_val) - goto out; + return ret_val; /* Two bits in the Auto Negotiation Advertisement Register * (Address 4) and two bits in the Auto Negotiation Base * Page Ability Register (Address 5) determine flow control @@ -612,7 +609,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw) ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex); if (ret_val) { hw_dbg("Error getting link speed and duplex\n"); - goto out; + return ret_val; } if (duplex == HALF_DUPLEX) @@ -621,13 +618,13 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw) /* Now we call a subroutine to actually force the MAC * controller to use the correct flow control settings. */ +force_fc: ret_val = igc_force_mac_fc(hw); if (ret_val) { hw_dbg("Error forcing flow control settings\n"); - goto out; + return ret_val; } -out: return ret_val; } diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 2c9e2dfd8499..e6e9441fc3d4 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -785,11 +785,8 @@ static void igc_setup_mrqc(struct igc_adapter *adapter) struct igc_hw *hw = &adapter->hw; u32 j, num_rx_queues; u32 mrqc, rxcsum; - u32 rss_key[10]; - netdev_rss_key_fill(rss_key, sizeof(rss_key)); - for (j = 0; j < 10; j++) - wr32(IGC_RSSRK(j), rss_key[j]); + igc_write_rss_key(adapter); num_rx_queues = adapter->rss_queues; @@ -5048,6 +5045,9 @@ static int igc_sw_init(struct igc_adapter *adapter) pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word); + /* init RSS key */ + netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key)); + /* set default ring sizes */ adapter->tx_ring_count = IGC_DEFAULT_TXD; adapter->rx_ring_count = IGC_DEFAULT_RXD; @@ -7298,7 +7298,7 @@ static int igc_probe(struct pci_dev *pdev, /* Initialize link properties that are user-changeable */ adapter->fc_autoneg = true; hw->phy.autoneg_advertised = 0xaf; - + hw->mac.autoneg_enabled = true; hw->fc.requested_mode = igc_fc_default; hw->fc.current_mode = igc_fc_default; diff --git a/drivers/net/ethernet/intel/igc/igc_phy.c b/drivers/net/ethernet/intel/igc/igc_phy.c index 6c4d204aecfa..4cf737fb3b21 100644 --- a/drivers/net/ethernet/intel/igc/igc_phy.c +++ b/drivers/net/ethernet/intel/igc/igc_phy.c @@ -494,12 +494,20 @@ s32 igc_setup_copper_link(struct igc_hw *hw) s32 ret_val = 0; bool link; - /* Setup autoneg and flow control advertisement and perform - * autonegotiation. - */ - ret_val = igc_copper_link_autoneg(hw); - if (ret_val) - goto out; + if (hw->mac.autoneg_enabled) { + /* Setup autoneg and flow control advertisement and perform + * autonegotiation. + */ + ret_val = igc_copper_link_autoneg(hw); + if (ret_val) + goto out; + } else { + ret_val = hw->phy.ops.force_speed_duplex(hw); + if (ret_val) { + hw_dbg("Error Forcing Speed/Duplex\n"); + goto out; + } + } /* Check link status. Wait up to 100 microseconds for link to become * valid. @@ -778,3 +786,48 @@ u16 igc_read_phy_fw_version(struct igc_hw *hw) return gphy_version; } + +/** + * igc_force_speed_duplex - Force PHY speed and duplex settings + * @hw: pointer to the HW structure + * + * Programs the GPY PHY control register to disable autonegotiation + * and force the speed/duplex indicated by hw->mac.forced_speed_duplex. + */ +s32 igc_force_speed_duplex(struct igc_hw *hw) +{ + struct igc_phy_info *phy = &hw->phy; + u16 phy_ctrl; + s32 ret_val; + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + return ret_val; + + phy_ctrl &= ~(MII_CR_SPEED_MASK | MII_CR_DUPLEX_EN | + MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + + switch (hw->mac.forced_speed_duplex) { + case IGC_FORCED_10H: + phy_ctrl |= MII_CR_SPEED_10; + break; + case IGC_FORCED_10F: + phy_ctrl |= MII_CR_SPEED_10 | MII_CR_DUPLEX_EN; + break; + case IGC_FORCED_100H: + phy_ctrl |= MII_CR_SPEED_100; + break; + case IGC_FORCED_100F: + phy_ctrl |= MII_CR_SPEED_100 | MII_CR_DUPLEX_EN; + break; + default: + return -IGC_ERR_CONFIG; + } + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + return ret_val; + + hw->mac.get_link_status = true; + return 0; +} diff --git a/drivers/net/ethernet/intel/igc/igc_phy.h b/drivers/net/ethernet/intel/igc/igc_phy.h index 832a7e359f18..d37a89174826 100644 --- a/drivers/net/ethernet/intel/igc/igc_phy.h +++ b/drivers/net/ethernet/intel/igc/igc_phy.h @@ -18,5 +18,6 @@ void igc_power_down_phy_copper(struct igc_hw *hw); s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data); s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data); u16 igc_read_phy_fw_version(struct igc_hw *hw); +s32 igc_force_speed_duplex(struct igc_hw *hw); #endif diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 8e3bb47eb3ba..78667a0977c0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4587,7 +4587,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base); } else { - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false); } @@ -4597,7 +4597,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, pfvf->rx_chan_base, pfvf->rx_chan_cnt); else - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c index 4571c56ec3c9..97f6097d4c70 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c @@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, int reformat_type) { mutex_lock(&tun_entropy->lock); - if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN) + if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN || + reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL) tun_entropy->num_enabling_entries--; else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE && --tun_entropy->num_disabling_entries == 0) diff --git a/drivers/net/ethernet/microsoft/Kconfig b/drivers/net/ethernet/microsoft/Kconfig index 3f36ee6a8ece..e9be18c92ca5 100644 --- a/drivers/net/ethernet/microsoft/Kconfig +++ b/drivers/net/ethernet/microsoft/Kconfig @@ -21,6 +21,7 @@ config MICROSOFT_MANA depends on X86_64 || (ARM64 && !CPU_BIG_ENDIAN) depends on PCI_HYPERV select AUXILIARY_BUS + select DIMLIB select PAGE_POOL select NET_SHAPER help diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index e8b7ffb47eb9..aef3b77229c1 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright (c) 2021, Microsoft Corporation. */ +#include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/module.h> #include <linux/pci.h> @@ -466,6 +467,7 @@ static int mana_gd_disable_queue(struct gdma_queue *queue) #define DOORBELL_OFFSET_RQ 0x400 #define DOORBELL_OFFSET_CQ 0x800 #define DOORBELL_OFFSET_EQ 0xFF8 +#define DOORBELL_OFFSET_DIM 0x820 static void mana_gd_ring_doorbell(struct gdma_context *gc, u32 db_index, enum gdma_queue_type q_type, u32 qid, @@ -506,6 +508,16 @@ static void mana_gd_ring_doorbell(struct gdma_context *gc, u32 db_index, addr += DOORBELL_OFFSET_SQ; break; + case GDMA_DIM: + e.dim.id = qid; + e.dim.mod_usec = FIELD_GET(MANA_INTR_MODR_USEC_MAX, tail_ptr); + e.dim.mod_usec_vld = !!(tail_ptr & MANA_INTR_MODR_USEC_VLD); + e.dim.mod_comps = FIELD_GET(MANA_INTR_MODR_COMP_MASK, tail_ptr); + e.dim.mod_comps_vld = num_req; + + addr += DOORBELL_OFFSET_DIM; + break; + default: WARN_ON(1); return; @@ -540,6 +552,23 @@ void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit) } EXPORT_SYMBOL_NS(mana_gd_ring_cq, "NET_MANA"); +void mana_gd_ring_dim(struct gdma_queue *cq, u32 mod_usec, bool mod_usec_vld, + u32 mod_comps, bool mod_comps_vld) +{ + struct gdma_context *gc = cq->gdma_dev->gdma_context; + u32 dim_val; + + /* Convert the DIM values to doorbell parameters */ + dim_val = FIELD_PREP(MANA_INTR_MODR_USEC_MAX, mod_usec) | + FIELD_PREP(MANA_INTR_MODR_COMP_MASK, mod_comps); + if (mod_usec_vld) + dim_val |= MANA_INTR_MODR_USEC_VLD; + + mana_gd_ring_doorbell(gc, cq->gdma_dev->doorbell, GDMA_DIM, cq->id, + dim_val, mod_comps_vld); +} +EXPORT_SYMBOL_NS(mana_gd_ring_dim, "NET_MANA"); + #define MANA_SERVICE_PERIOD 10 static void mana_serv_rescan(struct pci_dev *pdev) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 7438ea6b3f26..89e7f59f635d 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -1591,6 +1591,15 @@ int mana_create_wq_obj(struct mana_port_context *apc, mana_gd_init_req_hdr(&req.hdr, MANA_CREATE_WQ_OBJ, sizeof(req), sizeof(resp)); + + /* Our driver uses different message versions for request and + * response in this case. + * Our firmware is forward compatible with newer message versions, so + * the old firmware still properly handles this message, just the new + * feature fields are ignored, and queue creation will be successful. + */ + req.hdr.req.msg_version = GDMA_MESSAGE_V3; + req.hdr.resp.msg_version = GDMA_MESSAGE_V2; req.vport = vport; req.wq_type = wq_type; req.wq_gdma_region = wq_spec->gdma_region; @@ -1599,6 +1608,9 @@ int mana_create_wq_obj(struct mana_port_context *apc, req.cq_size = cq_spec->queue_size; req.cq_moderation_ctx_id = cq_spec->modr_ctx_id; req.cq_parent_qid = cq_spec->attached_eq; + req.req_cq_moderation = cq_spec->req_cq_moderation; + req.cq_moderation_comp = cq_spec->cq_moderation_comp; + req.cq_moderation_usec = cq_spec->cq_moderation_usec; err = mana_send_request(apc->ac, &req, sizeof(req), &resp, sizeof(resp)); @@ -1856,6 +1868,7 @@ static void mana_poll_tx_cq(struct mana_cq *cq) struct gdma_posted_wqe_info *wqe_info; unsigned int pkt_transmitted = 0; unsigned int wqe_unit_cnt = 0; + unsigned int tx_bytes = 0; struct mana_txq *txq = cq->txq; struct mana_port_context *apc; struct netdev_queue *net_txq; @@ -1937,6 +1950,8 @@ static void mana_poll_tx_cq(struct mana_cq *cq) mana_unmap_skb(skb, apc); + tx_bytes += skb->len; + napi_consume_skb(skb, cq->budget); pkt_transmitted++; @@ -1967,6 +1982,10 @@ static void mana_poll_tx_cq(struct mana_cq *cq) if (atomic_sub_return(pkt_transmitted, &txq->pending_sends) < 0) WARN_ON_ONCE(1); + /* Feed DIM with the completion rate observed here, in NAPI context. */ + cq->tx_dim_pkts += pkt_transmitted; + cq->tx_dim_bytes += tx_bytes; + cq->work_done = pkt_transmitted; } @@ -2120,12 +2139,16 @@ drop: } static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, - dma_addr_t *da, bool *from_pool) + dma_addr_t *da, bool *from_pool, + struct page **pp_page, u32 *dma_sync_offset) { struct page *page; u32 offset; void *va; + *from_pool = false; + *pp_page = NULL; + *dma_sync_offset = 0; /* Don't use fragments for jumbo frames or XDP where it's 1 fragment * per page. @@ -2163,31 +2186,47 @@ static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, va = page_to_virt(page) + offset; *da = page_pool_get_dma_addr(page) + offset + rxq->headroom; *from_pool = true; + *pp_page = page; + *dma_sync_offset = offset + rxq->headroom; return va; } /* Allocate frag for rx buffer, and save the old buf */ static void mana_refill_rx_oob(struct device *dev, struct mana_rxq *rxq, - struct mana_recv_buf_oob *rxoob, void **old_buf, - bool *old_fp) + struct mana_recv_buf_oob *rxoob, u32 pktlen, + void **old_buf, bool *old_fp) { + struct page *pp_page; + u32 dma_sync_offset; bool from_pool; dma_addr_t da; void *va; - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return; - if (!rxoob->from_pool || rxq->frag_count == 1) + if (!rxoob->from_pool || rxq->frag_count == 1) { dma_unmap_single(dev, rxoob->sgl[0].address, rxq->datasize, DMA_FROM_DEVICE); + } else { + /* The page pool maps the whole page and only syncs for device + * automatically (PP_FLAG_DMA_SYNC_DEV). Sync the received bytes + * for the CPU before they are read: this is required if DMA + * is incoherent or bounce buffers are used. + */ + page_pool_dma_sync_for_cpu(rxq->page_pool, rxoob->pp_page, + rxoob->dma_sync_offset, pktlen); + } *old_buf = rxoob->buf_va; *old_fp = rxoob->from_pool; rxoob->buf_va = va; rxoob->sgl[0].address = da; rxoob->from_pool = from_pool; + rxoob->pp_page = pp_page; + rxoob->dma_sync_offset = dma_sync_offset; } static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, @@ -2246,12 +2285,26 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, rxbuf_oob = &rxq->rx_oobs[curr]; WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1); - mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + if (unlikely(pktlen > rxq->datasize)) { + /* Increase it even if mana_rx_skb() isn't called. */ + rxq->rx_cq.work_done++; - /* Unsuccessful refill will have old_buf == NULL. - * In this case, mana_rx_skb() will drop the packet. - */ - mana_rx_skb(old_buf, old_fp, oob, rxq, i); + ++ndev->stats.rx_dropped; + netdev_warn_once(ndev, + "Dropped oversized RX packet: len=%u, datasize=%u\n", + pktlen, rxq->datasize); + + /* Reuse the RX buffer since rxbuf_oob is unchanged. */ + } else { + + mana_refill_rx_oob(dev, rxq, rxbuf_oob, pktlen, + &old_buf, &old_fp); + + /* Unsuccessful refill will have old_buf == NULL. + * In this case, mana_rx_skb() will drop the packet. + */ + mana_rx_skb(old_buf, old_fp, oob, rxq, i); + } mana_move_wq_tail(rxq->gdma_rq, rxbuf_oob->wqe_inf.wqe_size_in_bu); @@ -2318,6 +2371,117 @@ static void mana_poll_rx_cq(struct mana_cq *cq) xdp_do_flush(); } +static void mana_rx_dim_work(struct work_struct *work) +{ + struct dim *dim = container_of(work, struct dim, work); + struct dim_cq_moder cur_moder; + struct mana_cq *cq; + + cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix); + cq = container_of(dim, struct mana_cq, dim); + + cur_moder.usec = min_t(u16, cur_moder.usec, MANA_INTR_MODR_USEC_MAX); + cur_moder.pkts = min_t(u16, cur_moder.pkts, MANA_INTR_MODR_COMP_MAX); + + mana_gd_ring_dim(cq->gdma_cq, cur_moder.usec, true, + cur_moder.pkts, true); + + dim->state = DIM_START_MEASURE; +} + +static void mana_tx_dim_work(struct work_struct *work) +{ + struct dim *dim = container_of(work, struct dim, work); + struct dim_cq_moder cur_moder; + struct mana_cq *cq; + + cur_moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix); + cq = container_of(dim, struct mana_cq, dim); + + cur_moder.usec = min_t(u16, cur_moder.usec, MANA_INTR_MODR_USEC_MAX); + cur_moder.pkts = min_t(u16, cur_moder.pkts, MANA_INTR_MODR_COMP_MAX); + + mana_gd_ring_dim(cq->gdma_cq, cur_moder.usec, true, + cur_moder.pkts, true); + + dim->state = DIM_START_MEASURE; +} + +/* The caller must update apc->rx/tx_dim_enabled before disabling and + * after enabling. And synchronize_net() before draining the DIM work, + * so that NAPI cannot observe a stale flag. + */ +void mana_dim_change(struct mana_cq *cq, bool enable) +{ + bool is_rx = cq->type == MANA_CQ_TYPE_RX; + struct mana_port_context *apc; + work_func_t work_func; + u32 usec, comp; + + if (is_rx) { + apc = netdev_priv(cq->rxq->ndev); + usec = apc->intr_modr_rx_usec; + comp = apc->intr_modr_rx_comp; + work_func = mana_rx_dim_work; + } else { + apc = netdev_priv(cq->txq->ndev); + usec = apc->intr_modr_tx_usec; + comp = apc->intr_modr_tx_comp; + work_func = mana_tx_dim_work; + } + + /* On enable, zero the DIM state so net_dim() starts measuring from + * scratch. + * On disable, drain any pending DIM work and restore the static + * moderation values. + */ + if (enable) { + memset(&cq->dim, 0, sizeof(cq->dim)); + cq->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; + INIT_WORK(&cq->dim.work, work_func); + } else { + cancel_work_sync(&cq->dim.work); + mana_gd_ring_dim(cq->gdma_cq, usec, true, comp, true); + } +} + +static void mana_update_rx_dim(struct mana_cq *cq) +{ + struct mana_port_context *apc = netdev_priv(cq->rxq->ndev); + struct dim_sample dim_sample = {}; + struct mana_rxq *rxq = cq->rxq; + + /* Pairs with smp_store_release() in mana_set_coalesce(): observing the + * enable flag set guarantees the DIM (re)initialization is visible. + */ + if (!smp_load_acquire(&apc->rx_dim_enabled)) + return; + + dim_update_sample(READ_ONCE(cq->dim_event_ctr), rxq->stats.packets, + rxq->stats.bytes, &dim_sample); + net_dim(&cq->dim, &dim_sample); +} + +static void mana_update_tx_dim(struct mana_cq *cq) +{ + struct mana_port_context *apc = netdev_priv(cq->txq->ndev); + struct dim_sample dim_sample = {}; + + /* Pairs with smp_store_release() in mana_set_coalesce(): observing the + * enable flag set guarantees the DIM (re)initialization is visible. + */ + if (!smp_load_acquire(&apc->tx_dim_enabled)) + return; + + /* cq->tx_dim_pkts/bytes are accumulated in mana_poll_tx_cq(), in the + * same NAPI context as this read, so they track the hardware + * completion rate and need no u64_stats_sync protection. + */ + dim_update_sample(READ_ONCE(cq->dim_event_ctr), cq->tx_dim_pkts, + cq->tx_dim_bytes, &dim_sample); + net_dim(&cq->dim, &dim_sample); +} + static int mana_cq_handler(void *context, struct gdma_queue *gdma_queue) { struct mana_cq *cq = context; @@ -2336,6 +2500,15 @@ static int mana_cq_handler(void *context, struct gdma_queue *gdma_queue) if (w < cq->budget) { mana_gd_ring_cq(gdma_queue, SET_ARM_BIT); cq->work_done_since_doorbell = 0; + + /* Update DIM before napi_complete_done() to prevent running + * net_dim() concurrently. + */ + if (cq->type == MANA_CQ_TYPE_RX) + mana_update_rx_dim(cq); + else + mana_update_tx_dim(cq); + napi_complete_done(&cq->napi, w); } else if (cq->work_done_since_doorbell >= (cq->gdma_cq->queue_size / COMP_ENTRY_SIZE) * 4) { @@ -2368,6 +2541,7 @@ static void mana_schedule_napi(void *context, struct gdma_queue *gdma_queue) { struct mana_cq *cq = context; + WRITE_ONCE(cq->dim_event_ctr, cq->dim_event_ctr + 1); napi_schedule_irqoff(&cq->napi); } @@ -2410,6 +2584,7 @@ static void mana_destroy_txq(struct mana_port_context *apc) if (apc->tx_qp[i]->txq.napi_initialized) { napi_synchronize(napi); napi_disable_locked(napi); + cancel_work_sync(&apc->tx_qp[i]->tx_cq.dim.work); netif_napi_del_locked(napi); apc->tx_qp[i]->txq.napi_initialized = false; } @@ -2543,6 +2718,11 @@ static int mana_create_txq(struct mana_port_context *apc, cq_spec.modr_ctx_id = 0; cq_spec.attached_eq = cq->gdma_cq->cq.parent->id; + /* DIM setting can be changed at runtime */ + cq_spec.req_cq_moderation = true; + cq_spec.cq_moderation_usec = apc->intr_modr_tx_usec; + cq_spec.cq_moderation_comp = apc->intr_modr_tx_comp; + err = mana_create_wq_obj(apc, apc->port_handle, GDMA_SQ, &wq_spec, &cq_spec, &apc->tx_qp[i]->tx_object); @@ -2573,6 +2753,13 @@ static int mana_create_txq(struct mana_port_context *apc, set_bit(NAPI_STATE_NO_BUSY_POLL, &cq->napi.state); netif_napi_add_locked(net, &cq->napi, mana_poll); + + /* Initialize the DIM work before enabling NAPI, so that a poll + * cannot reach net_dim() with an uninitialized cq->dim.work. + */ + INIT_WORK(&cq->dim.work, mana_tx_dim_work); + cq->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; + napi_enable_locked(&cq->napi); txq->napi_initialized = true; @@ -2610,6 +2797,7 @@ static void mana_destroy_rxq(struct mana_port_context *apc, napi_synchronize(napi); napi_disable_locked(napi); + cancel_work_sync(&rxq->rx_cq.dim.work); netif_napi_del_locked(napi); } @@ -2655,6 +2843,8 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, struct mana_rxq *rxq, struct device *dev) { struct mana_port_context *mpc = netdev_priv(rxq->ndev); + struct page *pp_page = NULL; + u32 dma_sync_offset = 0; bool from_pool = false; dma_addr_t da; void *va; @@ -2662,13 +2852,16 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, if (mpc->rxbufs_pre) va = mana_get_rxbuf_pre(rxq, &da); else - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return -ENOMEM; rx_oob->buf_va = va; rx_oob->from_pool = from_pool; + rx_oob->pp_page = pp_page; + rx_oob->dma_sync_offset = dma_sync_offset; rx_oob->sgl[0].address = da; rx_oob->sgl[0].size = rxq->datasize; @@ -2848,6 +3041,11 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, cq_spec.modr_ctx_id = 0; cq_spec.attached_eq = cq->gdma_cq->cq.parent->id; + /* DIM setting can be changed at runtime */ + cq_spec.req_cq_moderation = true; + cq_spec.cq_moderation_usec = apc->intr_modr_rx_usec; + cq_spec.cq_moderation_comp = apc->intr_modr_rx_comp; + err = mana_create_wq_obj(apc, apc->port_handle, GDMA_RQ, &wq_spec, &cq_spec, &rxq->rxobj); if (err) @@ -2880,6 +3078,12 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, WARN_ON(xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL, rxq->page_pool)); + /* Initialize the DIM work before enabling NAPI, so that a poll + * cannot reach net_dim() with an uninitialized cq->dim.work. + */ + INIT_WORK(&cq->dim.work, mana_rx_dim_work); + cq->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; + napi_enable_locked(&cq->napi); mana_gd_ring_cq(cq->gdma_cq, SET_ARM_BIT); @@ -3546,6 +3750,16 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, apc->link_cfg_error = 1; apc->cqe_coalescing_enable = 0; + /* Initialize interrupt moderation settings if supported by HW */ + if (gc->pf_cap_flags1 & GDMA_PF_CAP_FLAG_1_DYN_INTERRUPT_MODERATION) { + apc->intr_modr_rx_usec = MANA_INTR_MODR_USEC_DEF; + apc->intr_modr_rx_comp = MANA_INTR_MODR_COMP_DEF; + apc->intr_modr_tx_usec = MANA_INTR_MODR_USEC_DEF; + apc->intr_modr_tx_comp = MANA_INTR_MODR_COMP_DEF; + apc->rx_dim_enabled = MANA_ADAPTIVE_RX_DEF; + apc->tx_dim_enabled = MANA_ADAPTIVE_TX_DEF; + } + mutex_init(&apc->vport_mutex); apc->vport_use_count = 0; diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c index 881df597d7f9..9e31e2595ae3 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c @@ -419,6 +419,15 @@ static int mana_get_coalesce(struct net_device *ndev, !kernel_coal->rx_cqe_nsecs) kernel_coal->rx_cqe_nsecs = MANA_RX_CQE_NSEC_DEF; + ec->rx_coalesce_usecs = apc->intr_modr_rx_usec; + ec->rx_max_coalesced_frames = apc->intr_modr_rx_comp; + + ec->tx_coalesce_usecs = apc->intr_modr_tx_usec; + ec->tx_max_coalesced_frames = apc->intr_modr_tx_comp; + + ec->use_adaptive_rx_coalesce = apc->rx_dim_enabled; + ec->use_adaptive_tx_coalesce = apc->tx_dim_enabled; + return 0; } @@ -428,9 +437,34 @@ static int mana_set_coalesce(struct net_device *ndev, struct netlink_ext_ack *extack) { struct mana_port_context *apc = netdev_priv(ndev); - u8 saved_cqe_coalescing_enable; + struct { + u16 intr_modr_rx_usec; + u16 intr_modr_rx_comp; + u16 intr_modr_tx_usec; + u16 intr_modr_tx_comp; + u8 cqe_coalescing_enable; + bool rx_dim_enabled; + bool tx_dim_enabled; + } saved; + bool modr_changed = false; + bool dim_changed = false; + struct gdma_context *gc; int err; + gc = apc->ac->gdma_dev->gdma_context; + + /* Both static and dynamic interrupt moderation (DIM) rely on the + * same HW capability advertised by the PF. + */ + if ((ec->use_adaptive_rx_coalesce || ec->use_adaptive_tx_coalesce || + ec->rx_coalesce_usecs || ec->tx_coalesce_usecs || + ec->rx_max_coalesced_frames || ec->tx_max_coalesced_frames) && + !(gc->pf_cap_flags1 & GDMA_PF_CAP_FLAG_1_DYN_INTERRUPT_MODERATION)) { + NL_SET_ERR_MSG(extack, + "Interrupt Moderation is not supported by HW"); + return -EOPNOTSUPP; + } + if (kernel_coal->rx_cqe_frames != 1 && kernel_coal->rx_cqe_frames != MANA_RXCOMP_OOB_NUM_PPI) { NL_SET_ERR_MSG_FMT(extack, @@ -440,18 +474,129 @@ static int mana_set_coalesce(struct net_device *ndev, return -EINVAL; } - saved_cqe_coalescing_enable = apc->cqe_coalescing_enable; + if (ec->rx_coalesce_usecs > MANA_INTR_MODR_USEC_MAX || + ec->tx_coalesce_usecs > MANA_INTR_MODR_USEC_MAX) { + NL_SET_ERR_MSG_FMT(extack, + "coalesce usecs must be <= %lu", + MANA_INTR_MODR_USEC_MAX); + return -EINVAL; + } + + if (ec->rx_max_coalesced_frames > MANA_INTR_MODR_COMP_MAX || + ec->tx_max_coalesced_frames > MANA_INTR_MODR_COMP_MAX) { + NL_SET_ERR_MSG_FMT(extack, + "coalesce frames must be <= %lu", + MANA_INTR_MODR_COMP_MAX); + return -EINVAL; + } + + if (ec->rx_coalesce_usecs != apc->intr_modr_rx_usec || + ec->rx_max_coalesced_frames != apc->intr_modr_rx_comp || + ec->tx_coalesce_usecs != apc->intr_modr_tx_usec || + ec->tx_max_coalesced_frames != apc->intr_modr_tx_comp) + modr_changed = true; + + saved.intr_modr_rx_usec = apc->intr_modr_rx_usec; + saved.intr_modr_rx_comp = apc->intr_modr_rx_comp; + saved.intr_modr_tx_usec = apc->intr_modr_tx_usec; + saved.intr_modr_tx_comp = apc->intr_modr_tx_comp; + + apc->intr_modr_rx_usec = ec->rx_coalesce_usecs; + apc->intr_modr_rx_comp = ec->rx_max_coalesced_frames; + apc->intr_modr_tx_usec = ec->tx_coalesce_usecs; + apc->intr_modr_tx_comp = ec->tx_max_coalesced_frames; + + if (!!ec->use_adaptive_rx_coalesce != apc->rx_dim_enabled || + !!ec->use_adaptive_tx_coalesce != apc->tx_dim_enabled) + dim_changed = true; + + saved.rx_dim_enabled = apc->rx_dim_enabled; + saved.tx_dim_enabled = apc->tx_dim_enabled; + + saved.cqe_coalescing_enable = apc->cqe_coalescing_enable; apc->cqe_coalescing_enable = kernel_coal->rx_cqe_frames == MANA_RXCOMP_OOB_NUM_PPI; - if (!apc->port_is_up) + if (!apc->port_is_up) { + WRITE_ONCE(apc->rx_dim_enabled, !!ec->use_adaptive_rx_coalesce); + WRITE_ONCE(apc->tx_dim_enabled, !!ec->use_adaptive_tx_coalesce); return 0; + } - err = mana_config_rss(apc, TRI_STATE_TRUE, false, false); - if (err) - apc->cqe_coalescing_enable = saved_cqe_coalescing_enable; + if (apc->cqe_coalescing_enable != saved.cqe_coalescing_enable) { + /* CQE coalescing setting is applied via RSS configuration. */ + err = mana_config_rss(apc, TRI_STATE_TRUE, false, false); + if (err) { + netdev_err(ndev, "Change CQE coalescing failed: %d\n", + err); + apc->cqe_coalescing_enable = + saved.cqe_coalescing_enable; + apc->intr_modr_rx_usec = saved.intr_modr_rx_usec; + apc->intr_modr_rx_comp = saved.intr_modr_rx_comp; + apc->intr_modr_tx_usec = saved.intr_modr_tx_usec; + apc->intr_modr_tx_comp = saved.intr_modr_tx_comp; + return err; + } + } - return err; + if (modr_changed || dim_changed) { + bool new_rx_dim = !!ec->use_adaptive_rx_coalesce; + bool new_tx_dim = !!ec->use_adaptive_tx_coalesce; + bool disable_rx_dim = saved.rx_dim_enabled && !new_rx_dim; + bool disable_tx_dim = saved.tx_dim_enabled && !new_tx_dim; + bool enable_rx_dim = !saved.rx_dim_enabled && new_rx_dim; + bool enable_tx_dim = !saved.tx_dim_enabled && new_tx_dim; + int q; + + /* On disable: clear the per-port flag first and + * synchronize_net() so any in-flight NAPI poll observes + * the new value and will not schedule further DIM work; + * then drain pending work and restore the static + * moderation values. + */ + if (disable_rx_dim) + WRITE_ONCE(apc->rx_dim_enabled, false); + if (disable_tx_dim) + WRITE_ONCE(apc->tx_dim_enabled, false); + if (disable_rx_dim || disable_tx_dim) + synchronize_net(); + + for (q = 0; q < apc->num_queues; q++) { + struct mana_cq *rx_cq = &apc->rxqs[q]->rx_cq; + struct mana_cq *tx_cq = &apc->tx_qp[q]->tx_cq; + + if (disable_rx_dim) + mana_dim_change(rx_cq, false); + else if (enable_rx_dim) + mana_dim_change(rx_cq, true); + else if (!new_rx_dim && modr_changed) + mana_gd_ring_dim(rx_cq->gdma_cq, + apc->intr_modr_rx_usec, true, + apc->intr_modr_rx_comp, true); + + if (disable_tx_dim) + mana_dim_change(tx_cq, false); + else if (enable_tx_dim) + mana_dim_change(tx_cq, true); + else if (!new_tx_dim && modr_changed) + mana_gd_ring_dim(tx_cq->gdma_cq, + apc->intr_modr_tx_usec, true, + apc->intr_modr_tx_comp, true); + } + + /* Publish the enable flag with release semantics so a + * concurrent NAPI poll that observes it set also sees the DIM + * (re)init done by mana_dim_change() above. + */ + if (enable_rx_dim) + /* pairs with smp_load_acquire() in mana_update_rx_dim() */ + smp_store_release(&apc->rx_dim_enabled, true); + if (enable_tx_dim) + /* pairs with smp_load_acquire() in mana_update_tx_dim() */ + smp_store_release(&apc->tx_dim_enabled, true); + } + + return 0; } /* mana_set_channels - change the number of queues on a port @@ -595,7 +740,13 @@ static int mana_get_link_ksettings(struct net_device *ndev, } const struct ethtool_ops mana_ethtool_ops = { - .supported_coalesce_params = ETHTOOL_COALESCE_RX_CQE_FRAMES, + .supported_coalesce_params = ETHTOOL_COALESCE_RX_CQE_FRAMES | + ETHTOOL_COALESCE_RX_USECS | + ETHTOOL_COALESCE_RX_MAX_FRAMES | + ETHTOOL_COALESCE_TX_USECS | + ETHTOOL_COALESCE_TX_MAX_FRAMES | + ETHTOOL_COALESCE_USE_ADAPTIVE_RX | + ETHTOOL_COALESCE_USE_ADAPTIVE_TX, .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM | ETHTOOL_OP_NEEDS_RTNL_GLINK, diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 3a5c25026858..19c2b870feed 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -639,7 +639,7 @@ qed_dcbx_get_operational_params(struct qed_hwfn *p_hwfn, flags = p_hwfn->p_dcbx_info->operational.flags; /* If DCBx version is non zero, then negotiation - * was successfuly performed + * was successfully performed */ p_operational = ¶ms->operational; enabled = !!(QED_MFW_GET_FIELD(flags, DCBX_CONFIG_VERSION) != diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 1a968596ca45..aa28025a1da1 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -646,7 +646,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, } unprotected_len = skb->len; - eth = eth_hdr(skb); + eth = skb_eth_hdr(skb); sci_present = macsec_send_sci(secy); hh = skb_push(skb, macsec_extra_len(sci_present)); memmove(hh, eth, 2 * ETH_ALEN); diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index 93a7b0f6c4e7..583426d06381 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -49,8 +49,6 @@ struct asyncppp { unsigned long xmit_flags; u32 xaccm[8]; u32 raccm; - unsigned int bytes_sent; - unsigned int bytes_rcvd; struct sk_buff *tpkt; int tpkt_pos; diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index b7f243b416f8..0b1bd1635c39 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -59,8 +59,6 @@ struct syncppp { unsigned long xmit_flags; u32 xaccm[8]; u32 raccm; - unsigned int bytes_sent; - unsigned int bytes_rcvd; struct sk_buff *tpkt; unsigned long last_xmit; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index faac359aa9ac..b3f1b7186721 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -706,6 +706,7 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt, if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); + __skb_queue_purge(list); return -EIO; } } @@ -770,6 +771,7 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt, if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); + __skb_queue_purge(list); return -EIO; } } diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index f566d699d074..1e1dea485760 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -996,6 +996,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) ret = ath11k_ahb_setup_msa_resources(ab); if (ret) { ath11k_err(ab, "failed to setup msa resources\n"); + of_node_put(node); return ret; } diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 9e90d8e3f155..8e2abc7b8383 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4618,6 +4618,9 @@ static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar, msdu_details = &msdu_link->msdu_link[0]; for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (!i && FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu_details[i].buf_addr_info.info0) == 0) + break; if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR, msdu_details[i].buf_addr_info.info0) == 0) { msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 35bb9e7a63a2..a163168f3617 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -199,6 +199,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -208,6 +210,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index ae8a9acee3f1..ad74140e0fa5 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -189,6 +189,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -198,6 +200,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 6bcb90760bee..73be55cfa203 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -6,6 +6,7 @@ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com> */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/export.h> #include <linux/fs.h> @@ -13,6 +14,7 @@ #include <linux/init.h> #include <linux/kref.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> #include <linux/gpio/consumer.h> @@ -55,25 +57,44 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - if (nvmem->reg_read) - return nvmem->reg_read(nvmem->priv, offset, val, bytes); + struct nvmem_operations *ops; - return -EINVAL; + guard(srcu)(&nvmem->srcu); + ops = srcu_dereference(nvmem->ops, &nvmem->srcu); + if (!ops) + return -ENODEV; + + if (!ops->reg_read) + return -EOPNOTSUPP; + + return ops->reg_read(nvmem->priv, offset, val, bytes); } static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - int ret; + struct nvmem_operations *ops; + int ret, wr_ok; + + guard(srcu)(&nvmem->srcu); + ops = srcu_dereference(nvmem->ops, &nvmem->srcu); + if (!ops) + return -ENODEV; - if (nvmem->reg_write) { - gpiod_set_value_cansleep(nvmem->wp_gpio, 0); - ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); - gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + if (!ops->reg_write) + return -EOPNOTSUPP; + + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0); + if (ret) return ret; - } - return -EINVAL; + wr_ok = ops->reg_write(nvmem->priv, offset, val, bytes); + + ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1); + if (ret) + return ret; + + return wr_ok; } static int nvmem_access_with_keepouts(struct nvmem_device *nvmem, @@ -231,13 +252,12 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_read) - return -EPERM; - rc = nvmem_reg_read(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } @@ -264,19 +284,23 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_write || nvmem->read_only) + if (nvmem->read_only) return -EPERM; rc = nvmem_reg_write(nvmem, pos, buf, count); - - if (rc) + if (rc) { + if (rc == -EOPNOTSUPP) + return -EPERM; return rc; + } return count; } static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) { + struct nvmem_operations *ops = rcu_dereference_raw(nvmem->ops); + umode_t mode = 0400; if (!nvmem->root_only) @@ -285,10 +309,10 @@ static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) if (!nvmem->read_only) mode |= 0200; - if (!nvmem->reg_write) + if (!ops->reg_write) mode &= ~0200; - if (!nvmem->reg_read) + if (!ops->reg_read) mode &= ~0444; return mode; @@ -319,6 +343,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, { struct device *dev = kobj_to_dev(kobj); struct nvmem_device *nvmem = to_nvmem_device(dev); + struct nvmem_operations *ops = rcu_dereference_raw(nvmem->ops); /* * If the device has no .reg_write operation, do not allow @@ -327,7 +352,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, * can be forced into read-write mode using the 'force_ro' * attribute. */ - if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write) + if (attr == &dev_attr_force_ro.attr && !ops->reg_write) return 0; /* Attribute not visible */ return attr->mode; @@ -445,10 +470,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, return 0; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { - if (config->compat) + if (nvmem->flags & FLAG_COMPAT) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); } @@ -461,27 +485,23 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) const struct bin_attribute **pattrs; struct bin_attribute *attrs; unsigned int ncells = 0, i = 0; - int ret = 0; + int ret; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) - goto unlock_mutex; + return 0; /* Allocate an array of attributes with a sentinel */ ncells = list_count_nodes(&nvmem->cells); pattrs = devm_kcalloc(&nvmem->dev, ncells + 1, sizeof(struct bin_attribute *), GFP_KERNEL); - if (!pattrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!pattrs) + return -ENOMEM; attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); - if (!attrs) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs) + return -ENOMEM; /* Initialize each attribute to take the name and size of the cell */ list_for_each_entry(entry, &nvmem->cells, node) { @@ -494,10 +514,8 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) attrs[i].size = entry->bytes; attrs[i].read = &nvmem_cell_attr_read; attrs[i].private = entry; - if (!attrs[i].attr.name) { - ret = -ENOMEM; - goto unlock_mutex; - } + if (!attrs[i].attr.name) + return -ENOMEM; pattrs[i] = &attrs[i]; i++; @@ -507,13 +525,10 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ret = device_add_group(&nvmem->dev, &group); if (ret) - goto unlock_mutex; + return ret; nvmem->sysfs_cells_populated = true; -unlock_mutex: - mutex_unlock(&nvmem_mutex); - return ret; } @@ -524,36 +539,17 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, { return -ENOSYS; } -static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, - const struct nvmem_config *config) +static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) { } #endif /* CONFIG_NVMEM_SYSFS */ -static void nvmem_release(struct device *dev) -{ - struct nvmem_device *nvmem = to_nvmem_device(dev); - - ida_free(&nvmem_ida, nvmem->id); - gpiod_put(nvmem->wp_gpio); - kfree(nvmem); -} - -static const struct device_type nvmem_provider_type = { - .release = nvmem_release, -}; - -static const struct bus_type nvmem_bus_type = { - .name = "nvmem", -}; - static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell) { blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell); - mutex_lock(&nvmem_mutex); - list_del(&cell->node); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_del(&cell->node); of_node_put(cell->np); kfree_const(cell->name); kfree(cell); @@ -567,11 +563,29 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) nvmem_cell_entry_drop(cell); } +static void nvmem_release(struct device *dev) +{ + struct nvmem_device *nvmem = to_nvmem_device(dev); + + gpiod_put(nvmem->wp_gpio); + nvmem_device_remove_all_cells(nvmem); + ida_free(&nvmem_ida, nvmem->id); + cleanup_srcu_struct(&nvmem->srcu); + kfree(nvmem); +} + +static const struct device_type nvmem_provider_type = { + .release = nvmem_release, +}; + +static const struct bus_type nvmem_bus_type = { + .name = "nvmem", +}; + static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell) { - mutex_lock(&nvmem_mutex); - list_add_tail(&cell->node, &cell->nvmem->cells); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) + list_add_tail(&cell->node, &cell->nvmem->cells); blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell); } @@ -721,14 +735,14 @@ nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id) { struct nvmem_cell_entry *iter, *cell = NULL; - mutex_lock(&nvmem_mutex); + guard(mutex)(&nvmem_mutex); + list_for_each_entry(iter, &nvmem->cells, node) { if (strcmp(cell_id, iter->name) == 0) { cell = iter; break; } } - mutex_unlock(&nvmem_mutex); return cell; } @@ -899,6 +913,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregister); struct nvmem_device *nvmem_register(const struct nvmem_config *config) { + struct nvmem_operations *ops; struct nvmem_device *nvmem; int rval; @@ -912,8 +927,15 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (!nvmem) return ERR_PTR(-ENOMEM); + ops = kzalloc_obj(*ops); + if (!ops) { + kfree(nvmem); + return ERR_PTR(-ENOMEM); + } + rval = ida_alloc(&nvmem_ida, GFP_KERNEL); if (rval < 0) { + kfree(ops); kfree(nvmem); return ERR_PTR(rval); } @@ -923,6 +945,21 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->dev.type = &nvmem_provider_type; nvmem->dev.bus = &nvmem_bus_type; nvmem->dev.parent = config->dev; + INIT_LIST_HEAD(&nvmem->cells); + + /* + * Must happen before we assign the release() callback in + * device_initialize(). + */ + rval = init_srcu_struct(&nvmem->srcu); + if (rval) { + ida_free(&nvmem_ida, nvmem->id); + kfree(ops); + kfree(nvmem); + return ERR_PTR(rval); + } + + rcu_assign_pointer(nvmem->ops, ops); device_initialize(&nvmem->dev); @@ -935,10 +972,11 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; } - kref_init(&nvmem->refcnt); - INIT_LIST_HEAD(&nvmem->cells); nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + ops->reg_read = config->reg_read; + ops->reg_write = config->reg_write; + nvmem->owner = config->owner; if (!nvmem->owner && config->dev->driver) nvmem->owner = config->dev->driver->owner; @@ -948,8 +986,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->root_only = config->root_only; nvmem->priv = config->priv; nvmem->type = config->type; - nvmem->reg_read = config->reg_read; - nvmem->reg_write = config->reg_write; nvmem->keepout = config->keepout; nvmem->nkeepout = config->nkeepout; if (config->of_node) @@ -975,7 +1011,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; nvmem->read_only = device_property_present(config->dev, "read-only") || - config->read_only || !nvmem->reg_write; + config->read_only || !ops->reg_write; #ifdef CONFIG_NVMEM_SYSFS nvmem->dev.groups = nvmem_dev_groups; @@ -996,24 +1032,24 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (config->cells) { rval = nvmem_add_cells(nvmem, config->cells, config->ncells); if (rval) - goto err_remove_cells; + goto err_remove_compat; } if (config->add_legacy_fixed_of_cells) { rval = nvmem_add_cells_from_legacy_of(nvmem); if (rval) - goto err_remove_cells; + goto err_remove_compat; } rval = nvmem_add_cells_from_fixed_layout(nvmem); if (rval) - goto err_remove_cells; + goto err_remove_compat; dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); rval = device_add(&nvmem->dev); if (rval) - goto err_remove_cells; + goto err_remove_compat; rval = nvmem_populate_layout(nvmem); if (rval) @@ -1039,42 +1075,40 @@ err_destroy_layout: #endif err_remove_dev: device_del(&nvmem->dev); -err_remove_cells: - nvmem_device_remove_all_cells(nvmem); - if (config->compat) - nvmem_sysfs_remove_compat(nvmem, config); +err_remove_compat: + nvmem_sysfs_remove_compat(nvmem); err_put_device: + ops = rcu_replace_pointer(nvmem->ops, NULL, true); + synchronize_srcu(&nvmem->srcu); put_device(&nvmem->dev); + kfree(ops); return ERR_PTR(rval); } EXPORT_SYMBOL_GPL(nvmem_register); -static void nvmem_device_release(struct kref *kref) +/** + * nvmem_unregister() - Unregister previously registered nvmem device + * + * @nvmem: Pointer to previously registered nvmem device. + */ +void nvmem_unregister(struct nvmem_device *nvmem) { - struct nvmem_device *nvmem; + struct nvmem_operations *ops; - nvmem = container_of(kref, struct nvmem_device, refcnt); + if (!nvmem) + return; blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem); - if (nvmem->flags & FLAG_COMPAT) - device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + ops = rcu_replace_pointer(nvmem->ops, NULL, true); + synchronize_srcu(&nvmem->srcu); - nvmem_device_remove_all_cells(nvmem); + nvmem_sysfs_remove_compat(nvmem); nvmem_destroy_layout(nvmem); - device_unregister(&nvmem->dev); -} + kfree(ops); -/** - * nvmem_unregister() - Unregister previously registered nvmem device - * - * @nvmem: Pointer to previously registered nvmem device. - */ -void nvmem_unregister(struct nvmem_device *nvmem) -{ - if (nvmem) - kref_put(&nvmem->refcnt, nvmem_device_release); + device_unregister(&nvmem->dev); } EXPORT_SYMBOL_GPL(nvmem_unregister); @@ -1112,17 +1146,17 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_nvmem_register); -static struct nvmem_device *__nvmem_device_get(void *data, +static struct nvmem_device *nvmem_device_match(void *data, int (*match)(struct device *dev, const void *data)) { struct nvmem_device *nvmem = NULL; struct device *dev; - mutex_lock(&nvmem_mutex); - dev = bus_find_device(&nvmem_bus_type, NULL, data, match); - if (dev) - nvmem = to_nvmem_device(dev); - mutex_unlock(&nvmem_mutex); + scoped_guard(mutex, &nvmem_mutex) { + dev = bus_find_device(&nvmem_bus_type, NULL, data, match); + if (dev) + nvmem = to_nvmem_device(dev); + } if (!nvmem) return ERR_PTR(-EPROBE_DEFER); @@ -1135,18 +1169,9 @@ static struct nvmem_device *__nvmem_device_get(void *data, return ERR_PTR(-EINVAL); } - kref_get(&nvmem->refcnt); - return nvmem; } -static void __nvmem_device_put(struct nvmem_device *nvmem) -{ - put_device(&nvmem->dev); - module_put(nvmem->owner); - kref_put(&nvmem->refcnt, nvmem_device_release); -} - #if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id @@ -1171,7 +1196,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) if (!nvmem_np) return ERR_PTR(-ENOENT); - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); return nvmem; } @@ -1199,7 +1224,7 @@ struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name) } - return __nvmem_device_get((void *)dev_name, device_match_name); + return nvmem_device_match((void *)dev_name, device_match_name); } EXPORT_SYMBOL_GPL(nvmem_device_get); @@ -1215,7 +1240,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_get); struct nvmem_device *nvmem_device_find(void *data, int (*match)(struct device *dev, const void *data)) { - return __nvmem_device_get(data, match); + return nvmem_device_match(data, match); } EXPORT_SYMBOL_GPL(nvmem_device_find); @@ -1259,7 +1284,8 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put); */ void nvmem_device_put(struct nvmem_device *nvmem) { - __nvmem_device_put(nvmem); + module_put(nvmem->owner); + put_device(&nvmem->dev); } EXPORT_SYMBOL_GPL(nvmem_device_put); @@ -1332,35 +1358,32 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) dev_id = dev_name(dev); - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); list_for_each_entry(lookup, &nvmem_lookup_list, node) { if ((strcmp(lookup->dev_id, dev_id) == 0) && (strcmp(lookup->con_id, con_id) == 0)) { /* This is the right entry. */ - nvmem = __nvmem_device_get((void *)lookup->nvmem_name, + nvmem = nvmem_device_match((void *)lookup->nvmem_name, device_match_name); - if (IS_ERR(nvmem)) { + if (IS_ERR(nvmem)) /* Provider may not be registered yet. */ - cell = ERR_CAST(nvmem); - break; - } + return ERR_CAST(nvmem); cell_entry = nvmem_find_cell_entry_by_name(nvmem, lookup->cell_name); if (!cell_entry) { - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); cell = ERR_PTR(-ENOENT); } else { cell = nvmem_create_cell(cell_entry, con_id, 0); if (IS_ERR(cell)) - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } break; } } - mutex_unlock(&nvmem_lookup_mutex); return cell; } @@ -1374,18 +1397,16 @@ static void nvmem_layout_module_put(struct nvmem_device *nvmem) static struct nvmem_cell_entry * nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) { - struct nvmem_cell_entry *iter, *cell = NULL; + struct nvmem_cell_entry *cell; - mutex_lock(&nvmem_mutex); - list_for_each_entry(iter, &nvmem->cells, node) { - if (np == iter->np) { - cell = iter; - break; - } + guard(mutex)(&nvmem_mutex); + + list_for_each_entry(cell, &nvmem->cells, node) { + if (np == cell->np) + return cell; } - mutex_unlock(&nvmem_mutex); - return cell; + return NULL; } static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem) @@ -1455,7 +1476,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) } } - nvmem = __nvmem_device_get(nvmem_np, device_match_of_node); + nvmem = nvmem_device_match(nvmem_np, device_match_of_node); of_node_put(nvmem_np); if (IS_ERR(nvmem)) { of_node_put(cell_np); @@ -1465,7 +1486,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) ret = nvmem_layout_module_get_optional(nvmem); if (ret) { of_node_put(cell_np); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } @@ -1474,14 +1495,14 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) if (!cell_entry) { nvmem_layout_module_put(nvmem); ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT; - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); return ERR_PTR(ret); } cell = nvmem_create_cell(cell_entry, id, cell_index); if (IS_ERR(cell)) { nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } return cell; @@ -1596,7 +1617,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) kfree(cell); nvmem_layout_module_put(nvmem); - __nvmem_device_put(nvmem); + nvmem_device_put(nvmem); } EXPORT_SYMBOL_GPL(nvmem_cell_put); @@ -2118,10 +2139,10 @@ void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_add_tail(&entries[i].node, &nvmem_lookup_list); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups); @@ -2136,10 +2157,10 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) { int i; - mutex_lock(&nvmem_lookup_mutex); + guard(mutex)(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) list_del(&entries[i].node); - mutex_unlock(&nvmem_lookup_mutex); } EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups); diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h index 18fed57270e5..17418fd0dcc9 100644 --- a/drivers/nvmem/internals.h +++ b/drivers/nvmem/internals.h @@ -6,15 +6,21 @@ #include <linux/device.h> #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> +#include <linux/srcu.h> + +/* Hold pointers to callbacks owned by the nvmem provider module. */ +struct nvmem_operations { + nvmem_reg_read_t reg_read; + nvmem_reg_write_t reg_write; +}; struct nvmem_device { struct module *owner; struct device dev; - struct list_head node; + struct srcu_struct srcu; int stride; int word_size; int id; - struct kref refcnt; size_t size; bool read_only; bool root_only; @@ -27,10 +33,9 @@ struct nvmem_device { struct nvmem_cell_info *cell); const struct nvmem_keepout *keepout; unsigned int nkeepout; - nvmem_reg_read_t reg_read; - nvmem_reg_write_t reg_write; struct gpio_desc *wp_gpio; struct nvmem_layout *layout; + struct nvmem_operations __rcu *ops; void *priv; bool sysfs_cells_populated; }; diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 96efa00d9743..9cb23780093d 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -40,10 +40,13 @@ void pci_ats_init(struct pci_dev *dev) */ bool pci_ats_supported(struct pci_dev *dev) { - if (!dev->ats_cap) + if (!dev->ats_cap || dev->untrusted) return false; - return (dev->untrusted == 0); + if (dev->is_virtfn) + return pci_ats_supported(pci_physfn(dev)); + + return true; } EXPORT_SYMBOL_GPL(pci_ats_supported); @@ -70,8 +73,12 @@ int pci_prepare_ats(struct pci_dev *dev, int ps) if (ps < PCI_ATS_MIN_STU) return -EINVAL; - if (dev->is_virtfn) + if (dev->is_virtfn) { + if (pci_physfn(dev)->ats_stu != ps) + return -EINVAL; + return 0; + } dev->ats_stu = ps; ctrl = PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU); diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c index b067a3296dd3..a1ea24fc3b63 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-plat.c +++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c @@ -163,6 +163,7 @@ static const struct of_device_id cdns_plat_pcie_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, cdns_plat_pcie_of_match); static struct platform_driver cdns_plat_pcie_driver = { .driver = { diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 7cb020dd3423..9591672b0511 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -717,3 +717,4 @@ MODULE_ALIAS("platform:cpcap_usb"); MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_DESCRIPTION("CPCAP usb phy driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/pinctrl/intel/pinctrl-tigerlake.c b/drivers/pinctrl/intel/pinctrl-tigerlake.c index 3ac35a153e8c..93234f4a9bd0 100644 --- a/drivers/pinctrl/intel/pinctrl-tigerlake.c +++ b/drivers/pinctrl/intel/pinctrl-tigerlake.c @@ -329,6 +329,34 @@ static const struct pinctrl_pin_desc tgllp_pins[] = { PINCTRL_PIN(276, "SPI0_CLK_LOOPBK"), }; +static const unsigned int tgllp_i2c0_pins[] = { 5, 6 }; +static const unsigned int tgllp_i2c1_pins[] = { 7, 8 }; +static const unsigned int tgllp_pwm0_pins[] = { 99 }; +static const unsigned int tgllp_uart1_pins[] = { 85, 86, 87, 88 }; +static const unsigned int tgllp_ssp2_pins[] = { 108, 109, 110, 111 }; + +static const struct intel_pingroup tgllp_groups[] = { + PIN_GROUP("i2c0_grp", tgllp_i2c0_pins, 2), + PIN_GROUP("i2c1_grp", tgllp_i2c1_pins, 2), + PIN_GROUP("pwm0_grp", tgllp_pwm0_pins, 2), + PIN_GROUP("uart1_grp", tgllp_uart1_pins, 2), + PIN_GROUP("ssp2_grp", tgllp_ssp2_pins, 7), +}; + +static const char * const tgllp_i2c0_groups[] = { "i2c0_grp" }; +static const char * const tgllp_i2c1_groups[] = { "i2c1_grp" }; +static const char * const tgllp_pwm0_groups[] = { "pwm0_grp" }; +static const char * const tgllp_uart1_groups[] = { "uart1_grp" }; +static const char * const tgllp_ssp2_groups[] = { "ssp2_grp" }; + +static const struct intel_function tgllp_functions[] = { + FUNCTION("i2c0", tgllp_i2c0_groups), + FUNCTION("i2c1", tgllp_i2c1_groups), + FUNCTION("pwm0", tgllp_pwm0_groups), + FUNCTION("uart1", tgllp_uart1_groups), + FUNCTION("ssp2", tgllp_ssp2_groups), +}; + static const struct intel_padgroup tgllp_community0_gpps[] = { INTEL_GPP(0, 0, 25, 0), /* GPP_B */ INTEL_GPP(1, 26, 41, 32), /* GPP_T */ @@ -366,6 +394,10 @@ static const struct intel_community tgllp_communities[] = { static const struct intel_pinctrl_soc_data tgllp_soc_data = { .pins = tgllp_pins, .npins = ARRAY_SIZE(tgllp_pins), + .groups = tgllp_groups, + .ngroups = ARRAY_SIZE(tgllp_groups), + .functions = tgllp_functions, + .nfunctions = ARRAY_SIZE(tgllp_functions), .communities = tgllp_communities, .ncommunities = ARRAY_SIZE(tgllp_communities), }; diff --git a/drivers/pinctrl/pinctrl-upboard.c b/drivers/pinctrl/pinctrl-upboard.c index f8c8b9d84990..de1920a3387d 100644 --- a/drivers/pinctrl/pinctrl-upboard.c +++ b/drivers/pinctrl/pinctrl-upboard.c @@ -912,6 +912,19 @@ static const struct upboard_pinctrl_map upboard_pinctrl_map_apl01 = { .nmaps = ARRAY_SIZE(pinctrl_map_apl01), }; +static const struct pinctrl_map pinctrl_map_adl[] = { + PIN_MAP_MUX_GROUP_DEFAULT("upboard-pinctrl", "INTC1055:00", "i2c0_grp", "i2c0"), + PIN_MAP_MUX_GROUP_DEFAULT("upboard-pinctrl", "INTC1055:00", "i2c1_grp", "i2c1"), + PIN_MAP_MUX_GROUP_DEFAULT("upboard-pinctrl", "INTC1055:00", "pwm0_grp", "pwm0"), + PIN_MAP_MUX_GROUP_DEFAULT("upboard-pinctrl", "INTC1055:00", "uart1_grp", "uart1"), + PIN_MAP_MUX_GROUP_DEFAULT("upboard-pinctrl", "INTC1055:00", "ssp2_grp", "ssp2"), +}; + +static const struct upboard_pinctrl_map upboard_pinctrl_map_adl = { + .maps = &pinctrl_map_adl[0], + .nmaps = ARRAY_SIZE(pinctrl_map_adl), +}; + static const struct dmi_system_id dmi_platform_info[] = { { /* UP Squared */ @@ -921,6 +934,14 @@ static const struct dmi_system_id dmi_platform_info[] = { }, .driver_data = (void *)&upboard_pinctrl_map_apl01, }, + { + /* UP Xtreme i12 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPX-ADLP01"), + }, + .driver_data = (void *)&upboard_pinctrl_map_adl, + }, { } }; diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index fe94ce5f9b81..9c720f49465b 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -994,6 +994,16 @@ static void msm_gpio_irq_ack(struct irq_data *d) if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) { if (test_bit(d->hwirq, pctrl->dual_edge_irqs)) msm_gpio_update_dual_edge_parent(d); + + /* + * During early initialization of the IRQ hierarchy, + * irq_ack() is called by __irq_set_handler() before + * the parent IRQ chip has been set up. This is why + * we additionally need to check for d->parent_data->chip. + */ + + if (d->parent_data->chip && d->parent_data->chip->irq_ack) + irq_chip_ack_parent(d); return; } @@ -1064,7 +1074,10 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) { clear_bit(d->hwirq, pctrl->dual_edge_irqs); - irq_set_handler_locked(d, handle_fasteoi_irq); + if (type & IRQ_TYPE_LEVEL_MASK) + irq_set_handler_locked(d, handle_fasteoi_irq); + else + irq_set_handler_locked(d, handle_fasteoi_ack_irq); return 0; } diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c index 8d2b8246170b..e4c0abcd95b9 100644 --- a/drivers/pinctrl/qcom/pinctrl-x1e80100.c +++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c @@ -1836,9 +1836,7 @@ static const struct msm_pinctrl_soc_data x1e80100_pinctrl = { .ngroups = ARRAY_SIZE(x1e80100_groups), .ngpios = 239, .wakeirq_map = x1e80100_pdc_map, - /* TODO: Enabling PDC currently breaks GPIO interrupts */ - .nwakeirq_map = 0, - /* .nwakeirq_map = ARRAY_SIZE(x1e80100_pdc_map), */ + .nwakeirq_map = ARRAY_SIZE(x1e80100_pdc_map), .egpio_func = 9, }; diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 388a3e1a488c..b38aa010053e 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -20,7 +20,6 @@ #define SURFACE_PRO3_BUTTON_HID "MSHW0028" #define SURFACE_PRO4_BUTTON_HID "MSHW0040" #define SURFACE_BUTTON_OBJ_NAME "VGBI" -#define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" #define MSHW0040_DSM_REVISION 0x01 #define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision @@ -212,11 +211,10 @@ static int surface_button_probe(struct platform_device *pdev) goto err_free_button; } - strscpy(acpi_device_name(device), SURFACE_BUTTON_DEVICE_NAME); snprintf(button->phys, sizeof(button->phys), "%s/buttons", acpi_device_hid(device)); - input->name = acpi_device_name(device); + input->name = "Surface Pro 3/4 Buttons"; input->phys = button->phys; input->id.bustype = BUS_HOST; input->dev.parent = &pdev->dev; @@ -239,8 +237,8 @@ static int surface_button_probe(struct platform_device *pdev) goto err_free_button; } - dev_info(&pdev->dev, "%s [%s]\n", acpi_device_name(device), - acpi_device_bid(device)); + dev_info(&pdev->dev, "%s [%s]\n", input->name, acpi_device_bid(device)); + return 0; err_free_input: diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b54b5212b204..957034f39e4e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -802,7 +802,6 @@ config LG_LAPTOP tristate "LG Laptop Extras" depends on ACPI depends on ACPI_BATTERY - depends on ACPI_WMI depends on INPUT select INPUT_SPARSEKMAP select NEW_LEDS diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 347c3f6c5ae7..a37083cdb908 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -33,6 +33,28 @@ #include "pmc.h" +static const struct amd_pmc_bit_map soc15_ip_blk_v3[] = { + {"VDDCR", BIT(0)}, + {"VDDCR_LP", BIT(1)}, + {"LSOCV", BIT(2)}, + {"DISPLAY", BIT(3)}, + {"VCN", BIT(4)}, + {"JPEG", BIT(5)}, + {"UMSCH", BIT(6)}, + {"VPE", BIT(7)}, + {"MPM", BIT(8)}, + {"NPU", BIT(9)}, + {"USB_HC0", BIT(10)}, + {"eUSB_HC0", BIT(11)}, + {"RT0_ADP_HC1", BIT(12)}, + {"RT1_ADP_HC1", BIT(13)}, + {"RT2_ADP_HC2", BIT(14)}, + {"USB4_RT0", BIT(15)}, + {"USB4_RT1", BIT(16)}, + {"USB4-RT2", BIT(17)}, + {"LAPIC", BIT(18)}, +}; + static const struct amd_pmc_bit_map soc15_ip_blk_v2[] = { {"DISPLAY", BIT(0)}, {"CPU", BIT(1)}, @@ -159,9 +181,9 @@ static const struct amd_pmc_cpu_info amd_1ah_m80_cpu_info = { .smu_msg = AMD_PMC_REGISTER_MSG_1AH_80H, .smu_arg = AMD_PMC_REGISTER_ARG_1AH_80H, .smu_rsp = AMD_PMC_REGISTER_RSP_1AH_80H, - .num_ips = ARRAY_SIZE(soc15_ip_blk), + .num_ips = ARRAY_SIZE(soc15_ip_blk_v3), .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, - .ips_ptr = soc15_ip_blk, + .ips_ptr = soc15_ip_blk_v3, .os_hint = MSG_OS_HINT_RN, }; diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c index f2a880eb0cdf..93d9665717af 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -16,6 +16,7 @@ #include <linux/acpi.h> #include <linux/array_size.h> #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/dmi.h> #include <linux/err.h> @@ -1007,10 +1008,11 @@ fail_class_get: /* Init / exit ****************************************************************/ /* Set up the min/max and defaults for ROG tunables */ -static void init_rog_tunables(void) +static int init_rog_tunables(void) { const struct power_limits *ac_limits, *dc_limits; - struct rog_tunables *ac_rog_tunables = NULL, *dc_rog_tunables = NULL; + struct rog_tunables *ac_rog_tunables __free(kfree) = NULL; + struct rog_tunables *dc_rog_tunables __free(kfree) = NULL; const struct power_data *power_data; const struct dmi_system_id *dmi_id; @@ -1018,14 +1020,14 @@ static void init_rog_tunables(void) dmi_id = dmi_first_match(power_limits); if (!dmi_id) { pr_warn("No matching power limits found for this system\n"); - return; + return 0; } /* Get the power data for this system */ power_data = dmi_id->driver_data; if (!power_data) { pr_info("No power data available for this system\n"); - return; + return 0; } asus_armoury.requires_fan_curve = power_data->requires_fan_curve; @@ -1033,11 +1035,10 @@ static void init_rog_tunables(void) /* Initialize AC power tunables */ ac_limits = power_data->ac_data; if (ac_limits) { - ac_rog_tunables = kzalloc_obj(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]); + ac_rog_tunables = kzalloc_obj(*ac_rog_tunables); if (!ac_rog_tunables) - goto err_nomem; + return -ENOMEM; - asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC] = ac_rog_tunables; ac_rog_tunables->power_limits = ac_limits; /* Set initial AC values */ @@ -1080,13 +1081,10 @@ static void init_rog_tunables(void) /* Initialize DC power tunables */ dc_limits = power_data->dc_data; if (dc_limits) { - dc_rog_tunables = kzalloc_obj(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]); - if (!dc_rog_tunables) { - kfree(ac_rog_tunables); - goto err_nomem; - } + dc_rog_tunables = kzalloc_obj(*dc_rog_tunables); + if (!dc_rog_tunables) + return -ENOMEM; - asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC] = dc_rog_tunables; dc_rog_tunables->power_limits = dc_limits; /* Set initial DC values */ @@ -1126,15 +1124,16 @@ static void init_rog_tunables(void) pr_debug("No DC PPT limits defined\n"); } - return; + asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC] = no_free_ptr(ac_rog_tunables); + asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC] = no_free_ptr(dc_rog_tunables); -err_nomem: - pr_err("Failed to allocate memory for tunables\n"); + return 0; } static int __init asus_fw_init(void) { char *wmi_uid; + int err; wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); if (!wmi_uid) @@ -1147,10 +1146,21 @@ static int __init asus_fw_init(void) if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) return -ENODEV; - init_rog_tunables(); + err = init_rog_tunables(); + if (err) + return err; /* Must always be last step to ensure data is available */ - return asus_fw_attr_add(); + err = asus_fw_attr_add(); + if (err) + goto err_free_tunables; + + return 0; + +err_free_tunables: + kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]); + kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]); + return err; } static void __exit asus_fw_exit(void) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 65166b50a2c3..afcb517dc5bf 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -371,6 +371,38 @@ static const struct dmi_system_id power_limits[] = { }, { .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA401KM"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .nv_dynamic_boost_max = 15, + .nv_dynamic_boost_min = 5, + .nv_temp_target_max = 87, + .nv_temp_target_min = 75, + .nv_tgp_max = 95, + .nv_tgp_min = 55, + .ppt_pl1_spl_max = 80, + .ppt_pl1_spl_min = 15, + .ppt_pl2_sppt_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl3_fppt_max = 80, + .ppt_pl3_fppt_min = 35, + }, + .dc_data = &(struct power_limits) { + .nv_temp_target_max = 87, + .nv_temp_target_min = 75, + .ppt_pl1_spl_max = 35, + .ppt_pl1_spl_min = 25, + .ppt_pl2_sppt_max = 44, + .ppt_pl2_sppt_min = 31, + .ppt_pl3_fppt_max = 65, + .ppt_pl3_fppt_min = 45, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "FA401UM"), }, .driver_data = &(struct power_data) { @@ -756,6 +788,40 @@ static const struct dmi_system_id power_limits[] = { }, { .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA608WV"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .nv_dynamic_boost_max = 25, + .nv_dynamic_boost_min = 5, + .nv_temp_target_max = 87, + .nv_temp_target_min = 75, + .nv_tgp_max = 115, + .nv_tgp_min = 55, + .ppt_pl1_spl_max = 90, + .ppt_pl1_spl_min = 15, + .ppt_pl2_sppt_max = 90, + .ppt_pl2_sppt_min = 35, + .ppt_pl3_fppt_max = 90, + .ppt_pl3_fppt_min = 35, + }, + .dc_data = &(struct power_limits) { + .nv_temp_target_max = 87, + .nv_temp_target_min = 75, + .ppt_pl1_spl_def = 45, + .ppt_pl1_spl_max = 65, + .ppt_pl1_spl_min = 15, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 65, + .ppt_pl2_sppt_min = 35, + .ppt_pl3_fppt_max = 65, + .ppt_pl3_fppt_min = 35, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "FA617NS"), }, .driver_data = &(struct power_data) { @@ -1997,13 +2063,13 @@ static const struct dmi_system_id power_limits[] = { .driver_data = &(struct power_data) { .ac_data = &(struct power_limits) { .ppt_pl1_spl_min = 30, - .ppt_pl1_spl_max = 90, + .ppt_pl1_spl_max = 120, .ppt_pl2_sppt_min = 65, - .ppt_pl2_sppt_def = 110, - .ppt_pl2_sppt_max = 125, + .ppt_pl2_sppt_def = 140, + .ppt_pl2_sppt_max = 145, .ppt_pl3_fppt_min = 65, - .ppt_pl3_fppt_def = 110, - .ppt_pl3_fppt_max = 125, + .ppt_pl3_fppt_def = 140, + .ppt_pl3_fppt_max = 145, .nv_temp_target_min = 75, .nv_temp_target_max = 87, .nv_dynamic_boost_min = 5, @@ -2081,6 +2147,35 @@ static const struct dmi_system_id power_limits[] = { }, { .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G635LX"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_def = 140, + .ppt_pl1_spl_max = 175, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 175, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 80, + .nv_tgp_max = 150, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 55, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 70, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "G713PV"), }, .driver_data = &(struct power_data) { @@ -2324,6 +2419,35 @@ static const struct dmi_system_id power_limits[] = { }, { .matches = { + DMI_MATCH(DMI_BOARD_NAME, "HN7306EA"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 35, + .ppt_pl1_spl_def = 60, + .ppt_pl1_spl_max = 85, + .ppt_pl2_sppt_min = 40, + .ppt_pl2_sppt_def = 70, + .ppt_pl2_sppt_max = 95, + .ppt_pl3_fppt_min = 50, + .ppt_pl3_fppt_def = 85, + .ppt_pl3_fppt_max = 115, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 35, + .ppt_pl1_spl_def = 45, + .ppt_pl1_spl_max = 60, + .ppt_pl2_sppt_min = 40, + .ppt_pl2_sppt_def = 55, + .ppt_pl2_sppt_max = 70, + .ppt_pl3_fppt_min = 50, + .ppt_pl3_fppt_def = 65, + .ppt_pl3_fppt_max = 85, + }, + }, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "RC71"), }, .driver_data = &(struct power_data) { diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 140ac8a10537..449addd1ac7a 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -42,8 +42,6 @@ #define ASUS_LAPTOP_VERSION "0.42" #define ASUS_LAPTOP_NAME "Asus Laptop Support" -#define ASUS_LAPTOP_CLASS "hotkey" -#define ASUS_LAPTOP_DEVICE_NAME "Hotkey" #define ASUS_LAPTOP_FILE KBUILD_MODNAME #define ASUS_LAPTOP_PREFIX "\\_SB.ATKD." @@ -1524,9 +1522,8 @@ static void asus_acpi_notify(acpi_handle handle, u32 event, void *data) /* TODO Find a better way to handle events count. */ count = asus->event_count[event % 128]++; - acpi_bus_generate_netlink_event(asus->device->pnp.device_class, - dev_name(&asus->device->dev), event, - count); + acpi_bus_generate_netlink_event("hotkey", dev_name(&asus->device->dev), + event, count); if (event >= ATKD_BRNUP_MIN && event <= ATKD_BRNUP_MAX) event = ATKD_BRNUP; @@ -1840,8 +1837,6 @@ static int asus_acpi_probe(struct platform_device *pdev) if (!asus) return -ENOMEM; asus->handle = device->handle; - strscpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); - strscpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); asus->device = device; asus_dmi_check(); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index d18a80907611..8a640c25830a 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -34,8 +34,6 @@ #define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" #define EEEPC_LAPTOP_FILE "eeepc" -#define EEEPC_ACPI_CLASS "hotkey" -#define EEEPC_ACPI_DEVICE_NAME "Hotkey" #define EEEPC_ACPI_HID "ASUS010" MODULE_AUTHOR("Corentin Chary, Eric Cooper"); @@ -1214,8 +1212,7 @@ static void eeepc_acpi_notify(acpi_handle handle, u32 event, void *data) if (event > ACPI_MAX_SYS_NOTIFY) return; count = eeepc->event_count[event % 128]++; - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, + acpi_bus_generate_netlink_event("hotkey", dev_name(&device->dev), event, count); /* Brightness events are special */ @@ -1376,8 +1373,6 @@ static int eeepc_acpi_probe(struct platform_device *pdev) if (!eeepc) return -ENOMEM; eeepc->handle = device->handle; - strscpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); - strscpy(acpi_device_class(device), EEEPC_ACPI_CLASS); eeepc->device = device; platform_set_drvdata(pdev, eeepc); diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 54d0b9cec4d3..ea543deef68f 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -56,7 +56,6 @@ #define FUJITSU_LCD_N_LEVELS 8 -#define ACPI_FUJITSU_CLASS "fujitsu" #define ACPI_FUJITSU_BL_HID "FUJ02B1" #define ACPI_FUJITSU_BL_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver" #define ACPI_FUJITSU_BL_DEVICE_NAME "Fujitsu FUJ02B1" @@ -466,7 +465,7 @@ static int acpi_fujitsu_bl_input_setup(struct device *dev) snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0", acpi_device_hid(device)); - priv->input->name = acpi_device_name(device); + priv->input->name = ACPI_FUJITSU_BL_DEVICE_NAME; priv->input->phys = priv->phys; priv->input->id.bustype = BUS_HOST; priv->input->id.product = 0x06; @@ -546,13 +545,11 @@ static int acpi_fujitsu_bl_probe(struct platform_device *pdev) return -ENOMEM; fujitsu_bl = priv; - strscpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); - strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); platform_set_drvdata(pdev, priv); - pr_info("ACPI: %s [%s]\n", - acpi_device_name(device), acpi_device_bid(device)); + pr_info("ACPI: %s [%s]\n", ACPI_FUJITSU_BL_DEVICE_NAME, + acpi_device_bid(device)); if (get_max_brightness(&pdev->dev) <= 0) priv->max_brightness = FUJITSU_LCD_N_LEVELS; @@ -681,7 +678,7 @@ static int acpi_fujitsu_laptop_input_setup(struct device *dev) snprintf(priv->phys, sizeof(priv->phys), "%s/input0", acpi_device_hid(device)); - priv->input->name = acpi_device_name(device); + priv->input->name = ACPI_FUJITSU_LAPTOP_DEVICE_NAME; priv->input->phys = priv->phys; priv->input->id.bustype = BUS_HOST; @@ -1012,9 +1009,6 @@ static int acpi_fujitsu_laptop_probe(struct platform_device *pdev) WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended."); fext = &pdev->dev; - strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); - strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); - platform_set_drvdata(pdev, priv); /* kfifo */ @@ -1024,8 +1018,8 @@ static int acpi_fujitsu_laptop_probe(struct platform_device *pdev) if (ret) return ret; - pr_info("ACPI: %s [%s]\n", - acpi_device_name(device), acpi_device_bid(device)); + pr_info("ACPI: %s [%s]\n", ACPI_FUJITSU_LAPTOP_DEVICE_NAME, + acpi_device_bid(device)); while (call_fext_func(fext, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 && i++ < MAX_HOTKEY_RINGBUFFER_SIZE) diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 2f8c1b89cbca..e1f5dc86dc4d 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -22,8 +22,6 @@ #define MODULENAME "fujitsu-tablet" -#define ACPI_FUJITSU_CLASS "fujitsu" - #define INVERT_TABLET_MODE_BIT 0x01 #define INVERT_DOCK_STATE_BIT 0x02 #define FORCE_TABLET_MODE_IF_UNDOCK 0x04 @@ -160,6 +158,7 @@ static struct { struct fujitsu_config config; unsigned long prev_keymask; + char name[17]; char phys[21]; int irq; @@ -458,14 +457,10 @@ static int acpi_fujitsu_probe(struct platform_device *pdev) if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base) return -ENODEV; - sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev)); - sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS); - - snprintf(fujitsu.phys, sizeof(fujitsu.phys), - "%s/input0", acpi_device_hid(adev)); + scnprintf(fujitsu.name, sizeof(fujitsu.name), "Fujitsu %s", acpi_device_hid(adev)); + scnprintf(fujitsu.phys, sizeof(fujitsu.phys), "%s/input0", acpi_device_hid(adev)); - error = input_fujitsu_setup(&pdev->dev, - acpi_device_name(adev), fujitsu.phys); + error = input_fujitsu_setup(&pdev->dev, fujitsu.name, fujitsu.phys); if (error) return error; diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 0dcf2901259e..5353d997d272 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -311,6 +311,10 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .driver_data = (void *)&omen_v1_no_ec_board_params, }, { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8DD6") }, + .driver_data = (void *)&omen_v1_no_ec_thermal_params, + }, + { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, .driver_data = (void *)&omen_v1_legacy_board_params, }, @@ -545,9 +549,9 @@ struct hp_wmi_hwmon_priv { struct mutex lock; /* protects mode, pwm */ u8 min_rpm; u8 max_rpm; - int gpu_delta; u8 mode; - u8 pwm; + u8 cpu_pwm; + u8 gpu_pwm; struct delayed_work keep_alive_dwork; }; @@ -874,24 +878,20 @@ static int hp_wmi_fan_speed_max_set(int enabled) return enabled; } -static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed) +static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv) { u8 fan_speed[2]; - int gpu_speed, ret; + int ret; - fan_speed[CPU_FAN] = speed; - fan_speed[GPU_FAN] = speed; + if (priv->cpu_pwm == HP_FAN_SPEED_AUTOMATIC) + fan_speed[CPU_FAN] = HP_FAN_SPEED_AUTOMATIC; + else + fan_speed[CPU_FAN] = pwm_to_rpm(priv->cpu_pwm, priv); - /* - * GPU fan speed is always a little higher than CPU fan speed, we fetch - * this delta value from the fan table during hwmon init. - * Exception: Speed is set to HP_FAN_SPEED_AUTOMATIC, to revert to - * automatic mode. - */ - if (speed != HP_FAN_SPEED_AUTOMATIC) { - gpu_speed = speed + priv->gpu_delta; - fan_speed[GPU_FAN] = clamp_val(gpu_speed, 0, U8_MAX); - } + if (priv->gpu_pwm == HP_FAN_SPEED_AUTOMATIC) + fan_speed[GPU_FAN] = HP_FAN_SPEED_AUTOMATIC; + else + fan_speed[GPU_FAN] = pwm_to_rpm(priv->gpu_pwm, priv); ret = hp_wmi_get_fan_count_userdefine_trigger(); if (ret < 0) @@ -908,7 +908,9 @@ static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed) static int hp_wmi_fan_speed_reset(struct hp_wmi_hwmon_priv *priv) { - return hp_wmi_fan_speed_set(priv, HP_FAN_SPEED_AUTOMATIC); + priv->cpu_pwm = HP_FAN_SPEED_AUTOMATIC; + priv->gpu_pwm = HP_FAN_SPEED_AUTOMATIC; + return hp_wmi_fan_speed_set(priv); } static int hp_wmi_fan_speed_max_reset(struct hp_wmi_hwmon_priv *priv) @@ -2499,7 +2501,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) case PWM_MODE_MANUAL: if (!hp_wmi_fan_control_supported()) return -EOPNOTSUPP; - ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv)); + ret = hp_wmi_fan_speed_set(priv); if (ret < 0) return ret; mod_delayed_work(system_dfl_wq, &priv->keep_alive_dwork, @@ -2599,13 +2601,14 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct hp_wmi_hwmon_priv *priv; - int rpm; + int cpu_rpm, gpu_rpm; priv = dev_get_drvdata(dev); guard(mutex)(&priv->lock); switch (type) { case hwmon_pwm: if (attr == hwmon_pwm_input) { + int rpm; if (!hp_wmi_fan_control_supported()) return -EOPNOTSUPP; /* PWM input is invalid when not in manual mode */ @@ -2615,7 +2618,10 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, /* ensure PWM input is within valid fan speeds */ rpm = pwm_to_rpm(val, priv); rpm = clamp_val(rpm, priv->min_rpm, priv->max_rpm); - priv->pwm = rpm_to_pwm(rpm, priv); + if (channel == CPU_FAN) + priv->cpu_pwm = rpm_to_pwm(rpm, priv); + else if (channel == GPU_FAN) + priv->gpu_pwm = rpm_to_pwm(rpm, priv); return hp_wmi_apply_fan_settings(priv); } switch (val) { @@ -2629,10 +2635,14 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, * When switching to manual mode, set fan speed to * current RPM values to ensure a smooth transition. */ - rpm = hp_wmi_get_active_fan_speed(channel); - if (rpm < 0) - return rpm; - priv->pwm = rpm_to_pwm(rpm / 100, priv); + cpu_rpm = hp_wmi_get_active_fan_speed(CPU_FAN); + if (cpu_rpm < 0) + return cpu_rpm; + gpu_rpm = hp_wmi_get_active_fan_speed(GPU_FAN); + if (gpu_rpm < 0) + return gpu_rpm; + priv->cpu_pwm = rpm_to_pwm(cpu_rpm / 100, priv); + priv->gpu_pwm = rpm_to_pwm(gpu_rpm / 100, priv); priv->mode = PWM_MODE_MANUAL; return hp_wmi_apply_fan_settings(priv); case PWM_MODE_AUTO: @@ -2648,7 +2658,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, static const struct hwmon_channel_info * const info[] = { HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), - HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE | HWMON_PWM_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE | HWMON_PWM_INPUT, HWMON_PWM_INPUT), NULL }; @@ -2689,7 +2699,7 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) struct victus_s_fan_table *fan_table; u8 min_rpm, max_rpm; u8 cpu_rpm, gpu_rpm, noise_db; - int gpu_delta, i, num_entries, ret; + int i, num_entries, ret; size_t header_size, entry_size; /* Default behaviour on hwmon init is automatic mode */ @@ -2735,10 +2745,8 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) if (min_rpm == U8_MAX || max_rpm == 0) return -EINVAL; - gpu_delta = fan_table->entries[0].gpu_rpm - fan_table->entries[0].cpu_rpm; priv->min_rpm = min_rpm; priv->max_rpm = max_rpm; - priv->gpu_delta = gpu_delta; return 0; } diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 93cca17fdf58..d6aaf14d66a5 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -6,6 +6,7 @@ */ #include <linux/acpi.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/dmi.h> @@ -527,11 +528,104 @@ static void huawei_wmi_battery_exit(struct device *dev) /* Fn lock */ +/* GFRS byte[1] / SFRS byte[2] (FRSR) fn-lock state values */ +#define FN_LOCK_ACPI_OFF 1 +#define FN_LOCK_ACPI_ON 2 +#define FN_LOCK_ACPI_STAT_OK 0 + +/* + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control + * Fn-lock via EC registers 0x6B (read) and 0x6C (write). + * + * GFRS response buffer layout: + * byte[0] = STAT (FN_LOCK_ACPI_STAT_OK = success) + * byte[1] = FN_LOCK_ACPI_OFF (fn-lock off) or FN_LOCK_ACPI_ON (fn-lock on) + * + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)): + * Value is read from byte[2] of the integer argument, so it must be + * passed as (value << 16): + * (FN_LOCK_ACPI_OFF << 16) = fn-lock off (writes 0x55 to EC 0x6C) + * (FN_LOCK_ACPI_ON << 16) = fn-lock on (writes 0x5A to EC 0x6C) + */ + +static int huawei_acpi_fn_lock_get(int *on) +{ + union acpi_object acpi_arg; + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + + acpi_arg.type = ACPI_TYPE_INTEGER; + acpi_arg.integer.value = 0; + + status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output); + if (ACPI_FAILURE(status)) + return -EIO; + + union acpi_object *obj __free(kfree) = output.pointer; + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) + return -ENODATA; + + /* byte[0] = STAT, byte[1] = fn-lock state */ + if (obj->buffer.pointer[0] != FN_LOCK_ACPI_STAT_OK) + return -EIO; + + switch (obj->buffer.pointer[1]) { + case FN_LOCK_ACPI_OFF: + if (on) + *on = 0; + break; + case FN_LOCK_ACPI_ON: + if (on) + *on = 1; + break; + default: + return -ENODATA; + } + + return 0; +} + +static int huawei_acpi_fn_lock_set(int on) +{ + union acpi_object acpi_arg; + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + + /* + * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02). + * on=0 → FRSR=FN_LOCK_ACPI_OFF → EC gets 0x55 (fn-lock off) + * on=1 → FRSR=FN_LOCK_ACPI_ON → EC gets 0x5A (fn-lock on) + */ + acpi_arg.type = ACPI_TYPE_INTEGER; + acpi_arg.integer.value = (on ? FN_LOCK_ACPI_ON : FN_LOCK_ACPI_OFF) << 16; + + status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output); + if (ACPI_FAILURE(status)) + return -EIO; + + union acpi_object *obj __free(kfree) = output.pointer; + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 1) + return -ENODATA; + + if (obj->buffer.pointer[0] != FN_LOCK_ACPI_STAT_OK) + return -EIO; + + return 0; +} + static int huawei_wmi_fn_lock_get(int *on) { u8 ret[0x100] = { 0 }; int err, i; + /* Newer models: use direct ACPI \GFRS method */ + if (acpi_has_method(NULL, "\\GFRS")) + return huawei_acpi_fn_lock_get(on); + + /* WMI fallback */ err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100); if (err) return err; @@ -550,6 +644,11 @@ static int huawei_wmi_fn_lock_set(int on) { union hwmi_arg arg; + /* Newer models: use direct ACPI \SFRS method */ + if (acpi_has_method(NULL, "\\SFRS")) + return huawei_acpi_fn_lock_set(on); + + /* WMI fallback */ arg.cmd = FN_LOCK_SET; arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on. diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 115bb37577a1..adff564bf3fd 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = { .con_id = "dvdd", .enable_time_us = 45 * USEC_PER_MSEC, }, + { /* imx471 expects "vana" as con_id for power enable */ + .hid = "SONY471A", + .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, + .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, + .con_id = "vana", + .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, + }, + { + /* + * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id + * for power enable + */ + .hid = "TBE20A0", + .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, + .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, + .con_id = "vana", + .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, + }, }; static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type, diff --git a/drivers/platform/x86/intel/ishtp_eclite.c b/drivers/platform/x86/intel/ishtp_eclite.c index 93ac8b2dbf38..bca7e217878b 100644 --- a/drivers/platform/x86/intel/ishtp_eclite.c +++ b/drivers/platform/x86/intel/ishtp_eclite.c @@ -600,13 +600,16 @@ static int ecl_ishtp_cl_probe(struct ishtp_cl_device *cl_device) rv = acpi_opregion_init(opr_dev); if (rv) { dev_err(cl_data_to_dev(opr_dev), "ACPI opregion init failed\n"); - goto err_exit; + goto err_put; } /* Reprobe devices depending on ECLite - battery, fan, etc. */ acpi_dev_clear_dependencies(opr_dev->adev); return 0; + +err_put: + acpi_dev_put(opr_dev->adev); err_exit: ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING); ishtp_cl_disconnect(ecl_ishtp_cl); diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index 445e1403308e..6dd7c28fc0db 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -38,6 +38,7 @@ #include <linux/backlight.h> #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/dmi.h> @@ -67,6 +68,7 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/string_choices.h> #include <linux/string_helpers.h> #include <linux/sysfs.h> #include <linux/types.h> @@ -186,6 +188,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */ TP_HKEY_EV_CAMERASHUTTER_TOGGLE = 0x131b, /* Toggle Camera Shutter */ TP_HKEY_EV_DOUBLETAP_TOGGLE = 0x131c, /* Toggle trackpoint doubletap on/off */ + TP_HKEY_EV_USB_C_SECURITY = 0x131e, /* USB C Security (Fn+U, Fn+S) */ TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile in 2024 systems */ TP_HKEY_EV_PROFILE_TOGGLE2 = 0x1401, /* Toggle platform profile in 2025 + systems */ @@ -309,6 +312,7 @@ struct tp_acpi_drv_struct { struct ibm_struct { char *name; + acpi_device_class device_class; int (*read) (struct seq_file *); int (*write) (char *); @@ -374,6 +378,8 @@ static struct { u32 has_adaptive_kbd:1; u32 kbd_lang:1; u32 trackpoint_doubletap_enable:1; + u32 usbc_security_supported:1; + bool usbc_security_enabled; struct quirk_entry *quirks; } tp_features; @@ -838,9 +844,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) } ibm->acpi->device->driver_data = ibm; - scnprintf(acpi_device_class(ibm->acpi->device), - sizeof(acpi_device_class(ibm->acpi->device)), - "%s/%s", TPACPI_ACPI_EVENT_PREFIX, ibm->name); + scnprintf(ibm->device_class, sizeof(ibm->device_class), "%s/%s", + TPACPI_ACPI_EVENT_PREFIX, ibm->name); status = acpi_install_notify_handler(*ibm->acpi->handle, ibm->acpi->type, dispatch_acpi_notify, ibm); @@ -3869,7 +3874,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) pr_err("unknown HKEY notification event %d\n", event); /* forward it to userspace, maybe it knows how to handle it */ acpi_bus_generate_netlink_event( - ibm->acpi->device->pnp.device_class, + ibm->device_class, dev_name(&ibm->acpi->device->dev), event, 0); return; @@ -3949,7 +3954,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) /* netlink events */ if (send_acpi_ev) { acpi_bus_generate_netlink_event( - ibm->acpi->device->pnp.device_class, + ibm->device_class, dev_name(&ibm->acpi->device->dev), event, hkey); } @@ -11285,6 +11290,111 @@ static struct ibm_struct hwdd_driver_data = { .name = "hwdd", }; +/************************************************************************* + * USB-C Security subdriver + * + * HKEY.USCS(0) is a read-only ACPI method; its argument is ignored. + * It always returns: + * bit 16 - USB-C security capability present on this SKU or not + * bit 0 - USB-C Security state (enable or disable) + * + * Hotkey + * ------ + * 0x131e (Fn+U, Fn+S): firmware toggles USBS before firing the event. + * The driver reads back the new state and notifies the sysfs attribute. + */ + +/* USCS() return word bit layout */ +#define USCS_CAP_BIT BIT(16) /* capability: feature present on SKU */ +#define USCS_STATUS_BIT BIT(0) /* current security state */ + +/* Protects USCS() ACPI method calls in usbc_security_query() */ +static DEFINE_MUTEX(usbc_security_mutex); + +/** + * usbc_security_query - read current USB-C security state via USCS() + * @enabled: out - true when security is ON (data connections blocked) + * + * Returns: + * 0 success, @enabled contains the current state + * -EIO ACPI evaluation failed + * -ENODEV capability bit absent; feature not present on this SKU* + */ +static int usbc_security_query(bool *enabled) +{ + int status; + + guard(mutex)(&usbc_security_mutex); + if (!acpi_evalf(hkey_handle, &status, "USCS", "dd", 0)) + return -EIO; + + if (!(status & USCS_CAP_BIT)) { + pr_debug("USCS cap bit absent (raw=0x%x)\n", status); + return -ENODEV; + } + + *enabled = status & USCS_STATUS_BIT; + return 0; +} + +/* sysfs: /sys/devices/platform/thinkpad_acpi/usb_c_security ---------- */ +static ssize_t usb_c_security_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", + str_enabled_disabled(tp_features.usbc_security_enabled)); +} + +static DEVICE_ATTR_RO(usb_c_security); + +static struct attribute *usbc_security_attributes[] = { + &dev_attr_usb_c_security.attr, + NULL, +}; + +static umode_t usbc_security_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return tp_features.usbc_security_supported ? attr->mode : 0; +} + +static const struct attribute_group usbc_security_attr_group = { + .is_visible = usbc_security_attr_is_visible, + .attrs = usbc_security_attributes, +}; + +static int tpacpi_usbc_security_init(struct ibm_init_struct *iibm) +{ + int err; + + err = usbc_security_query(&tp_features.usbc_security_enabled); + if (err == -ENODEV) + return 0; + if (err) + return err; + + tp_features.usbc_security_supported = true; + return 0; +} + +/* tpacpi_usbc_security_hotkey - handle Fn+U Fn+S hotkey (0x131e) */ +static bool tpacpi_usbc_security_hotkey(void) +{ + if (!tp_features.usbc_security_supported) + return false; + + if (usbc_security_query(&tp_features.usbc_security_enabled)) + return false; + + sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "usb_c_security"); + return true; +} + +static struct ibm_struct usbc_security_driver_data = { + .name = "usbc_security", +}; + /* --------------------------------------------------------------------- */ static struct attribute *tpacpi_driver_attributes[] = { @@ -11345,6 +11455,7 @@ static const struct attribute_group *tpacpi_groups[] = { &dprc_attr_group, &auxmac_attr_group, &hwdd_attr_group, + &usbc_security_attr_group, NULL, }; @@ -11499,6 +11610,8 @@ static bool tpacpi_driver_event(const unsigned int hkey_event) case TP_HKEY_EV_PROFILE_TOGGLE2: platform_profile_cycle(); return true; + case TP_HKEY_EV_USB_C_SECURITY: + return tpacpi_usbc_security_hotkey(); } return false; @@ -11964,6 +12077,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = tpacpi_hwdd_init, .data = &hwdd_driver_data, }, + { + .init = tpacpi_usbc_security_init, + .data = &usbc_security_driver_data, + }, }; static int __init set_ibm_param(const char *val, const struct kernel_param *kp) diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index a8f2f465ef3f..36ad1e269443 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -8,8 +8,11 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> +#include <linux/array_size.h> #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/compiler_attributes.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/dev_printk.h> #include <linux/dmi.h> @@ -17,10 +20,12 @@ #include <linux/input/sparse-keymap.h> #include <linux/kernel.h> #include <linux/leds.h> +#include <linux/limits.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/string_choices.h> #include <linux/types.h> +#include <linux/unaligned.h> #include <acpi/battery.h> @@ -57,13 +62,18 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages"); #define LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR 0x3E8 #define LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR 0x5E8 -#define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248" -#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2" -#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6" -#define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B" -#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D" -#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210" -#define WMI_EVENT_GUID WMI_EVENT_GUID0 +#define LG_NOTIFY_TABLET_MODE_OFF 0x50 +#define LG_NOTIFY_TABLET_MODE_ON 0x51 +#define LG_NOTIFY_HOTKEY 0x80 +#define LG_NOTIFY_THERMAL 0x81 +#define LG_NOTIFY_MISC 0x82 + +#define LG_OREP_READ_EC 0 +#define LG_OREP_WRITE_EC 1 +#define LG_OREP_DEBUG 2 +#define LG_OREP_UPDATE_SYSTEM_STATE 3 +#define LG_OREP_INTERCEPT_WMI_EVENTS 4 +#define LG_OREP_WAKE_ON_LAN 6 #define SB_GGOV_METHOD "\\_SB.GGOV" #define GOV_TLED 0x2020008 @@ -78,42 +88,95 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages"); #define WMBB_USB_CHARGE 0x10B #define WMBB_BATT_LIMIT 0x10C +#define KBD_LED_BRIGHTNESS_MASK GENMASK(4, 0) +#define KBD_LED_BRIGHTNESS_OFF 0x0 +#define KBD_LED_BRIGHTNESS_HALF 0x2 +#define KBD_LED_BRIGHTNESS_FULL 0x4 +#define KBD_LED_MODE_MASK GENMASK(6, 5) +#define KBD_LED_MODE_OFF 0x0 +#define KBD_LED_MODE_ON 0x1 +#define KBD_LED_STATUS BIT(7) +#define KBD_LED_MAGIC_MASK GENMASK(15, 8) +/* Exact purpose is unknown, maybe some sort of brightness limit? */ +#define KBD_LED_MAGIC 0x05 + #define FAN_MODE_LOWER GENMASK(1, 0) #define FAN_MODE_UPPER GENMASK(5, 4) #define PLATFORM_NAME "lg-laptop" -MODULE_ALIAS("wmi:" WMI_EVENT_GUID0); -MODULE_ALIAS("wmi:" WMI_EVENT_GUID1); -MODULE_ALIAS("wmi:" WMI_EVENT_GUID2); -MODULE_ALIAS("wmi:" WMI_EVENT_GUID3); -MODULE_ALIAS("wmi:" WMI_METHOD_WMAB); -MODULE_ALIAS("wmi:" WMI_METHOD_WMBB); +struct lg_wmab_buffer_result { + __le32 value; + __le32 status; +} __packed; static struct platform_device *pf_device; -static struct input_dev *wmi_input_dev; - -static u32 inited; -#define INIT_INPUT_WMI_0 0x01 -#define INIT_INPUT_WMI_2 0x02 -#define INIT_INPUT_ACPI 0x04 -#define INIT_SPARSE_KEYMAP 0x80 static int battery_limit_use_wmbb; +static bool kbd_backlight_available; static struct led_classdev kbd_backlight; static enum led_brightness get_kbd_backlight_level(struct device *dev); static const struct key_entry wmi_keymap[] = { - {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */ - {KE_KEY, 0x74, {KEY_F21} }, /* Touchpad toggle (F5) */ - {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */ - {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing - * this key both sends an event and - * changes backlight level. - */ + /* Placeholder value send by multiple hotkeys handled by ACPI */ + { KE_IGNORE, 0x0, { KEY_UNKNOWN }}, + /* LG control panel */ + { KE_KEY, 0x70, { KEY_F15 }}, + /* Touchpad toggle */ + { KE_KEY, 0x74, { KEY_F21 }}, + /* Mute Audio, already handled by ACPI */ + { KE_IGNORE, 0x78, { KEY_MUTE }}, + /* Read mode */ + { KE_KEY, 0xf020000, { KEY_F14 }}, + /* Open settings */ + { KE_KEY, 0xf070002, { KEY_CONFIG }}, + /* Keyboard backlight - pressing this key both sends an event and changes backlight level */ + { KE_KEY, 0x10000000, { KEY_F16 }}, + /* Hotkey combination pressed */ + { KE_IGNORE, 0x30010000, { KEY_UNKNOWN }}, + /* Hotkey combination released */ + { KE_IGNORE, 0x30010001, { KEY_UNKNOWN }}, + /* Change fan mode */ + { KE_KEY, 0x30010051, { KEY_PERFORMANCE }}, + /* Disable camera */ + { KE_KEY, 0x40020000, { KEY_CAMERA_ACCESS_TOGGLE }}, + /* Mute microphone */ + { KE_KEY, 0x40020001, { KEY_MICMUTE }}, + /* Fn-Lock */ + { KE_KEY, 0x40030001, { KEY_FN_ESC }}, {KE_END, 0} }; +static int lg_laptop_execute_orep(acpi_handle handle, u64 command, u64 value, + unsigned long long *result) +{ + union acpi_object objs[] = { + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = command, + }, + }, + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = value, + }, + } + }; + struct acpi_object_list args = { + .count = ARRAY_SIZE(objs), + .pointer = objs, + }; + acpi_status status; + + status = acpi_evaluate_integer(handle, "OREP", &args, result); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} + static int ggov(u32 arg0) { union acpi_object args[1]; @@ -154,30 +217,97 @@ static int ggov(u32 arg0) return res; } -static union acpi_object *lg_wmab(struct device *dev, u32 method, u32 arg1, u32 arg2) +static int lg_wmab_get(struct device *dev, u32 method, u32 *result) { - union acpi_object args[3]; - acpi_status status; - struct acpi_object_list arg; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object in[] = { + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = method, + }, + }, + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = WM_GET, + }, + }, + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = 0, + }, + }, + }; + struct lg_wmab_buffer_result *buffer_result; + struct acpi_object_list input = { + .count = ARRAY_SIZE(in), + .pointer = in, + }; + acpi_status status; - args[0].type = ACPI_TYPE_INTEGER; - args[0].integer.value = method; - args[1].type = ACPI_TYPE_INTEGER; - args[1].integer.value = arg1; - args[2].type = ACPI_TYPE_INTEGER; - args[2].integer.value = arg2; + status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &input, &buffer); + if (ACPI_FAILURE(status)) + return -EIO; - arg.count = 3; - arg.pointer = args; + union acpi_object *obj __free(kfree) = buffer.pointer; - status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &arg, &buffer); - if (ACPI_FAILURE(status)) { - dev_err(dev, "WMAB: call failed.\n"); - return NULL; + if (!obj) + return -ENODATA; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + *result = obj->integer.value; + return 0; + case ACPI_TYPE_BUFFER: + if (obj->buffer.length != sizeof(*buffer_result)) + return -EPROTO; + + buffer_result = (struct lg_wmab_buffer_result *)obj->buffer.pointer; + if (get_unaligned_le32(&buffer_result->status)) + return -EIO; + + *result = get_unaligned_le32(&buffer_result->value); + return 0; + default: + return -EPROTO; } +} + +static int lg_wmab_set(struct device *dev, u32 method, u32 arg) +{ + union acpi_object in[] = { + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = method, + }, + }, + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = WM_SET, + }, + }, + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = arg, + }, + }, + }; + struct acpi_object_list input = { + .count = ARRAY_SIZE(in), + .pointer = in, + }; + acpi_status status; + + status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &input, NULL); + if (ACPI_FAILURE(status)) + return -EIO; - return buffer.pointer; + return 0; } static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u32 arg2) @@ -211,72 +341,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u return (union acpi_object *)buffer.pointer; } -static void wmi_notify(union acpi_object *obj, void *context) -{ - long data = (long)context; - - pr_debug("event guid %li\n", data); - if (!obj) - return; - - if (obj->type == ACPI_TYPE_INTEGER) { - int eventcode = obj->integer.value; - struct key_entry *key; - - if (eventcode == 0x10000000) { - led_classdev_notify_brightness_hw_changed( - &kbd_backlight, get_kbd_backlight_level(kbd_backlight.dev->parent)); - } else { - key = sparse_keymap_entry_from_scancode( - wmi_input_dev, eventcode); - if (key && key->type == KE_KEY) - sparse_keymap_report_entry(wmi_input_dev, - key, 1, true); - } - } - - pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type, - obj->integer.value); -} - -static void wmi_input_setup(void) -{ - acpi_status status; - - wmi_input_dev = input_allocate_device(); - if (wmi_input_dev) { - wmi_input_dev->name = "LG WMI hotkeys"; - wmi_input_dev->phys = "wmi/input0"; - wmi_input_dev->id.bustype = BUS_HOST; - - if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) || - input_register_device(wmi_input_dev)) { - pr_info("Cannot initialize input device"); - input_free_device(wmi_input_dev); - return; - } - - inited |= INIT_SPARSE_KEYMAP; - status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify, - (void *)0); - if (ACPI_SUCCESS(status)) - inited |= INIT_INPUT_WMI_0; - - status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify, - (void *)2); - if (ACPI_SUCCESS(status)) - inited |= INIT_INPUT_WMI_2; - } else { - pr_info("Cannot allocate input device"); - } -} - static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { unsigned long value; - union acpi_object *r; int ret; ret = kstrtoul(buffer, 10, &value); @@ -285,10 +354,10 @@ static ssize_t fan_mode_store(struct device *dev, if (value >= 3) return -EINVAL; - r = lg_wmab(dev, WM_FAN_MODE, WM_SET, - FIELD_PREP(FAN_MODE_LOWER, value) | - FIELD_PREP(FAN_MODE_UPPER, value)); - kfree(r); + ret = lg_wmab_set(dev, WM_FAN_MODE, FIELD_PREP(FAN_MODE_LOWER, value) | + FIELD_PREP(FAN_MODE_UPPER, value)); + if (ret < 0) + return ret; return count; } @@ -296,22 +365,14 @@ static ssize_t fan_mode_store(struct device *dev, static ssize_t fan_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { - unsigned int mode; - union acpi_object *r; - - r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0); - if (!r) - return -EIO; - - if (r->type != ACPI_TYPE_INTEGER) { - kfree(r); - return -EIO; - } + u32 mode; + int ret; - mode = FIELD_GET(FAN_MODE_LOWER, r->integer.value); - kfree(r); + ret = lg_wmab_get(dev, WM_FAN_MODE, &mode); + if (ret < 0) + return ret; - return sysfs_emit(buffer, "%d\n", mode); + return sysfs_emit(buffer, "%lu\n", FIELD_GET(FAN_MODE_LOWER, mode)); } static ssize_t usb_charge_store(struct device *dev, @@ -361,41 +422,30 @@ static ssize_t reader_mode_store(struct device *dev, const char *buffer, size_t count) { bool value; - union acpi_object *r; int ret; ret = kstrtobool(buffer, &value); if (ret) return ret; - r = lg_wmab(dev, WM_READER_MODE, WM_SET, value); - if (!r) - return -EIO; + ret = lg_wmab_set(dev, WM_READER_MODE, value); + if (ret < 0) + return ret; - kfree(r); return count; } static ssize_t reader_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { - unsigned int status; - union acpi_object *r; - - r = lg_wmab(dev, WM_READER_MODE, WM_GET, 0); - if (!r) - return -EIO; - - if (r->type != ACPI_TYPE_INTEGER) { - kfree(r); - return -EIO; - } - - status = !!r->integer.value; + u32 status; + int ret; - kfree(r); + ret = lg_wmab_get(dev, WM_READER_MODE, &status); + if (ret < 0) + return ret; - return sysfs_emit(buffer, "%d\n", status); + return sysfs_emit(buffer, "%d\n", !!status); } static ssize_t fn_lock_store(struct device *dev, @@ -403,40 +453,30 @@ static ssize_t fn_lock_store(struct device *dev, const char *buffer, size_t count) { bool value; - union acpi_object *r; int ret; ret = kstrtobool(buffer, &value); if (ret) return ret; - r = lg_wmab(dev, WM_FN_LOCK, WM_SET, value); - if (!r) - return -EIO; + ret = lg_wmab_set(dev, WM_FN_LOCK, value); + if (ret < 0) + return ret; - kfree(r); return count; } static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buffer) { - unsigned int status; - union acpi_object *r; - - r = lg_wmab(dev, WM_FN_LOCK, WM_GET, 0); - if (!r) - return -EIO; - - if (r->type != ACPI_TYPE_BUFFER) { - kfree(r); - return -EIO; - } + u32 status; + int ret; - status = !!r->buffer.pointer[0]; - kfree(r); + ret = lg_wmab_get(dev, WM_FN_LOCK, &status); + if (ret < 0) + return ret; - return sysfs_emit(buffer, "%d\n", status); + return sysfs_emit(buffer, "%d\n", !!status); } static ssize_t charge_control_end_threshold_store(struct device *dev, @@ -453,14 +493,18 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, if (value == 100 || value == 80) { union acpi_object *r; - if (battery_limit_use_wmbb) + if (battery_limit_use_wmbb) { r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_SET, value); - else - r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_SET, value); - if (!r) - return -EIO; + if (!r) + return -EIO; + + kfree(r); + } else { + ret = lg_wmab_set(&pf_device->dev, WM_BATT_LIMIT, value); + if (ret < 0) + return ret; + } - kfree(r); return count; } @@ -471,8 +515,9 @@ static ssize_t charge_control_end_threshold_show(struct device *device, struct device_attribute *attr, char *buf) { - unsigned int status; union acpi_object *r; + u32 status; + int ret; if (battery_limit_use_wmbb) { r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_GET, 0); @@ -485,19 +530,13 @@ static ssize_t charge_control_end_threshold_show(struct device *device, } status = r->buffer.pointer[0x10]; + kfree(r); } else { - r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_GET, 0); - if (!r) - return -EIO; - - if (r->type != ACPI_TYPE_INTEGER) { - kfree(r); - return -EIO; - } - - status = r->integer.value; + ret = lg_wmab_get(&pf_device->dev, WM_BATT_LIMIT, &status); + if (ret < 0) + return ret; } - kfree(r); + if (status != 80 && status != 100) status = 0; @@ -563,10 +602,7 @@ static const struct attribute_group dev_attribute_group = { static void tpad_led_set(struct led_classdev *cdev, enum led_brightness brightness) { - union acpi_object *r; - - r = lg_wmab(cdev->dev->parent, WM_TLED, WM_SET, brightness > LED_OFF); - kfree(r); + lg_wmab_set(cdev->dev->parent, WM_TLED, brightness > LED_OFF); } static enum led_brightness tpad_led_get(struct led_classdev *cdev) @@ -579,47 +615,50 @@ static LED_DEVICE(tpad_led, 1, 0); static void kbd_backlight_set(struct led_classdev *cdev, enum led_brightness brightness) { - u32 val; - union acpi_object *r; + /* Must always be written */ + u32 value = KBD_LED_STATUS; + u32 mode, bright; - val = 0x22; - if (brightness <= LED_OFF) - val = 0; - if (brightness >= LED_FULL) - val = 0x24; - r = lg_wmab(cdev->dev->parent, WM_KEY_LIGHT, WM_SET, val); - kfree(r); + if (brightness <= LED_OFF) { + mode = KBD_LED_MODE_OFF; + bright = KBD_LED_BRIGHTNESS_OFF; + } else { + mode = KBD_LED_MODE_ON; + if (brightness >= LED_FULL) + bright = KBD_LED_BRIGHTNESS_FULL; + else + bright = KBD_LED_BRIGHTNESS_HALF; + } + + value |= FIELD_PREP(KBD_LED_BRIGHTNESS_MASK, bright); + value |= FIELD_PREP(KBD_LED_MODE_MASK, mode); + + lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, value); } static enum led_brightness get_kbd_backlight_level(struct device *dev) { - union acpi_object *r; - int val; + u32 value; + int ret; - r = lg_wmab(dev, WM_KEY_LIGHT, WM_GET, 0); + ret = lg_wmab_get(dev, WM_KEY_LIGHT, &value); + if (ret < 0) + return LED_OFF; - if (!r) + if (FIELD_GET(KBD_LED_MAGIC_MASK, value) != KBD_LED_MAGIC) return LED_OFF; - if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) { - kfree(r); + if (FIELD_GET(KBD_LED_MODE_MASK, value) == KBD_LED_MODE_OFF) return LED_OFF; - } - switch (r->buffer.pointer[0] & 0x27) { - case 0x24: - val = LED_FULL; - break; - case 0x22: - val = LED_HALF; - break; + switch (FIELD_GET(KBD_LED_BRIGHTNESS_MASK, value)) { + case KBD_LED_BRIGHTNESS_FULL: + return LED_FULL; + case KBD_LED_BRIGHTNESS_HALF: + return LED_HALF; default: - val = LED_OFF; + return LED_OFF; } - - kfree(r); - - return val; } static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) @@ -629,26 +668,163 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED); -static void wmi_input_destroy(void) +static struct platform_driver pf_driver = { + .driver = { + .name = PLATFORM_NAME, + } +}; + +static int lg_laptop_get_event_data(acpi_handle handle, u32 value, u32 *data) { - if (inited & INIT_INPUT_WMI_2) - wmi_remove_notify_handler(WMI_EVENT_GUID2); + union acpi_object objs[] = { + { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = value, + }, + } + }; + struct acpi_object_list args = { + .count = ARRAY_SIZE(objs), + .pointer = objs, + }; + unsigned long long result; + acpi_status status; + + status = acpi_evaluate_integer(handle, "_WED", &args, &result); + if (ACPI_FAILURE(status)) + return -EIO; - if (inited & INIT_INPUT_WMI_0) - wmi_remove_notify_handler(WMI_EVENT_GUID0); + if (result > U32_MAX) + return -EPROTO; - if (inited & INIT_SPARSE_KEYMAP) - input_unregister_device(wmi_input_dev); + *data = result; - inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP); + return 0; } -static struct platform_driver pf_driver = { - .driver = { - .name = PLATFORM_NAME, +static void lg_laptop_handle_input_event(struct input_dev *input_dev, u32 value, u32 data) +{ + unsigned int kbd_brightness; + + switch (value) { + case LG_NOTIFY_HOTKEY: + sparse_keymap_report_event(input_dev, data, 1, true); + break; + case LG_NOTIFY_THERMAL: + /* Currently not supported */ + break; + case LG_NOTIFY_MISC: + switch (data) { + case 0x10000000: + if (!kbd_backlight_available) + break; + + kbd_brightness = get_kbd_backlight_level(kbd_backlight.dev->parent); + led_classdev_notify_brightness_hw_changed(&kbd_backlight, kbd_brightness); + break; + default: + sparse_keymap_report_event(input_dev, data, 1, true); + } + break; + default: + break; + } +} + +static void lg_laptop_notify_handler(acpi_handle handle, u32 value, void *context) +{ + struct input_dev *input_dev = context; + u32 data; + int ret; + + switch (value) { + case LG_NOTIFY_TABLET_MODE_OFF: + case LG_NOTIFY_TABLET_MODE_ON: + /* Already handled by intel-hid */ + return; + case LG_NOTIFY_HOTKEY: + case LG_NOTIFY_THERMAL: + case LG_NOTIFY_MISC: + ret = lg_laptop_get_event_data(handle, value, &data); + if (ret < 0) { + dev_notice(input_dev->dev.parent, "Failed to get event data: %d\n", ret); + return; + } + + dev_dbg(input_dev->dev.parent, "Received event %u (%u)\n", value, data); + + lg_laptop_handle_input_event(input_dev, value, data); + return; + default: + dev_notice(input_dev->dev.parent, "Received unknown event %u\n", value); } }; +static void lg_laptop_remove_notify_handler(void *context) +{ + acpi_handle handle = context; + + acpi_remove_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler); +} + +static void lg_laptop_reenable_wmi_events(void *context) +{ + acpi_handle handle = context; + unsigned long long dummy; + + lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 0, &dummy); +} + +static int lg_laptop_input_init(struct device *dev, acpi_handle handle) +{ + struct input_dev *input_dev; + unsigned long long result; + acpi_status status; + int ret; + + if (!acpi_has_method(handle, "_WED")) + return 0; + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "LG WMI hotkeys"; + input_dev->phys = "wmi/input0"; + input_dev->id.bustype = BUS_HOST; + ret = sparse_keymap_setup(input_dev, wmi_keymap, NULL); + if (ret < 0) + return ret; + + ret = input_register_device(input_dev); + if (ret < 0) + return ret; + + status = acpi_install_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler, + input_dev); + if (ACPI_FAILURE(status)) + return -EIO; + + ret = devm_add_action_or_reset(dev, lg_laptop_remove_notify_handler, handle); + if (ret < 0) + return ret; + + if (acpi_has_method(handle, "OREP")) { + ret = lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 1, &result); + if (ret < 0) + return ret; + if (result) + return -EIO; + + ret = devm_add_action_or_reset(dev, lg_laptop_reenable_wmi_events, handle); + if (ret < 0) + return ret; + } + + return 0; +} + static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address, size_t size, u64 value) { @@ -860,15 +1036,23 @@ static int acpi_probe(struct platform_device *pdev) if (year >= 2019) battery_limit_use_wmbb = 1; + /* LEDs are optional */ + ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight); + if (ret < 0) + kbd_backlight_available = false; + else + kbd_backlight_available = true; + + devm_led_classdev_register(&pdev->dev, &tpad_led); + + ret = lg_laptop_input_init(&pdev->dev, device->handle); + if (ret < 0) + goto out_platform_device; + ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); if (ret) goto out_platform_device; - /* LEDs are optional */ - led_classdev_register(&pf_device->dev, &kbd_backlight); - led_classdev_register(&pf_device->dev, &tpad_led); - - wmi_input_setup(); battery_hook_register(&battery_hook); return 0; @@ -884,11 +1068,7 @@ static void acpi_remove(struct platform_device *pdev) { sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); - led_classdev_unregister(&tpad_led); - led_classdev_unregister(&kbd_backlight); - battery_hook_unregister(&battery_hook); - wmi_input_destroy(); platform_device_unregister(pf_device); pf_device = NULL; platform_driver_unregister(&pf_driver); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 719add753cb3..3effb11c4153 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -159,8 +159,6 @@ MODULE_LICENSE("GPL"); #define ECO_MODE_ON 0x80 #define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support" -#define ACPI_PCC_DEVICE_NAME "Hotkey" -#define ACPI_PCC_CLASS "pcc" #define ACPI_PCC_INPUT_PHYS "panasonic/hkey0" @@ -1017,8 +1015,6 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev) pcc->device = device; pcc->handle = device->handle; device->driver_data = pcc; - strscpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); - strscpy(acpi_device_class(device), ACPI_PCC_CLASS); result = acpi_pcc_init_input(pcc); if (result) { diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 67370967df6f..0bf4d3054e37 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1264,7 +1264,7 @@ static void sony_nc_notify(acpi_handle ah, u32 event, void *data) ev_type = HOTKEY; sony_laptop_report_input_event(real_ev); } - acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class, + acpi_bus_generate_netlink_event("sony/hotkey", dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev); } @@ -3157,8 +3157,6 @@ static int sony_nc_probe(struct platform_device *pdev) return -ENODEV; sony_nc_acpi_device = device; - strscpy(acpi_device_class(device), "sony/hotkey"); - sony_nc_acpi_handle = device->handle; /* read device status */ @@ -4523,7 +4521,6 @@ static int sony_pic_probe(struct platform_device *pdev) return -ENODEV; spic_dev.acpi_dev = device; - strscpy(acpi_device_class(device), "sony/hotkey"); sony_pic_detect_device_type(&spic_dev); mutex_init(&spic_dev.lock); diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index e09d7f8ce45f..0e842c55dcc5 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -299,8 +299,6 @@ static int topstar_acpi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, topstar); - strscpy(acpi_device_name(device), "Topstar TPSACPI"); - strscpy(acpi_device_class(device), TOPSTAR_LAPTOP_CLASS); topstar->device = device; err = topstar_acpi_init(topstar); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 7cecb3a70b9c..a0b8060836d0 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -2505,8 +2505,7 @@ static void toshiba_acpi_kbd_bl_work(struct work_struct *work) LED_FULL : LED_OFF); /* Emulate the keyboard backlight event */ - acpi_bus_generate_netlink_event(toshiba_acpi->acpi_dev->pnp.device_class, - dev_name(&toshiba_acpi->acpi_dev->dev), + acpi_bus_generate_netlink_event("", dev_name(&toshiba_acpi->acpi_dev->dev), 0x92, 0); } @@ -3250,8 +3249,7 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *data) break; } - acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class, - dev_name(&acpi_dev->dev), + acpi_bus_generate_netlink_event("", dev_name(&acpi_dev->dev), event, (event == 0x80) ? dev->last_key_event : 0); } diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index 8d12241924df..c6633b74029f 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -136,9 +136,7 @@ static void toshiba_haps_notify(acpi_handle handle, u32 event, void *data) pr_debug("Received event: 0x%x\n", event); - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), - event, 0); + acpi_bus_generate_netlink_event("", dev_name(&device->dev), event, 0); } static void toshiba_haps_remove(struct platform_device *pdev) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index e40e385c52bd..4c3185e2ceec 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -19,13 +19,10 @@ #define MODULE_NAME "xo15-ebook" -#define XO15_EBOOK_CLASS MODULE_NAME #define XO15_EBOOK_TYPE_UNKNOWN 0x00 #define XO15_EBOOK_NOTIFY_STATUS 0x80 -#define XO15_EBOOK_SUBCLASS "ebook" #define XO15_EBOOK_HID "XO15EBK" -#define XO15_EBOOK_DEVICE_NAME "EBook Switch" MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver"); MODULE_LICENSE("GPL"); @@ -105,12 +102,9 @@ static int ebook_switch_probe(struct platform_device *pdev) if (!id) return dev_err_probe(dev, -ENODEV, "Unsupported hid\n"); - strscpy(acpi_device_name(device), XO15_EBOOK_DEVICE_NAME); - strscpy(acpi_device_class(device), XO15_EBOOK_CLASS "/" XO15_EBOOK_SUBCLASS); - snprintf(button->phys, sizeof(button->phys), "%s/button/input0", id->id); - input->name = acpi_device_name(device); + input->name = "EBook Switch"; input->phys = button->phys; input->id.bustype = BUS_HOST; diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 19e992d2ee3b..99d100e1d923 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -54,6 +54,15 @@ struct imx8m_blk_ctrl_domain_data { * register. */ u32 mipi_phy_rst_mask; + + /* + * VC8000E reset de-assertion edge and AXI clock may have a timing issue. + * Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off + * both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to + * VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is + * de-asserted by HW) + */ + bool is_errata_err050531; }; #define DOMAIN_MAX_CLKS 4 @@ -108,7 +117,11 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) dev_err(bc->dev, "failed to enable clocks\n"); goto bus_put; } - regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + if (data->is_errata_err050531) + regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + else + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); /* power up upstream GPC domain */ ret = pm_runtime_get_sync(domain->power_dev); @@ -117,6 +130,9 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) goto clk_disable; } + if (data->is_errata_err050531) + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + /* wait for reset to propagate */ udelay(5); @@ -511,12 +527,38 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] .clk_mask = BIT(2), .path_names = (const char *[]){"vc8000e"}, .num_paths = 1, + .is_errata_err050531 = true, }, }; +static int imx8mp_vpu_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action == GENPD_NOTIFY_ON) { + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + + udelay(5); + + /* set "fuse" bits to enable the VPUs */ + regmap_set_bits(bc->regmap, 0x8, 0xffffffff); + regmap_set_bits(bc->regmap, 0xc, 0xffffffff); + regmap_set_bits(bc->regmap, 0x10, 0xffffffff); + regmap_set_bits(bc->regmap, 0x14, 0xffffffff); + } + + return NOTIFY_OK; +} + static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = { .max_reg = 0x18, - .power_notifier_fn = imx8mm_vpu_power_notifier, + .power_notifier_fn = imx8mp_vpu_power_notifier, .domains = imx8mp_vpu_blk_ctl_domain_data, .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data), }; diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c index 1afc78b034fa..243ce939ba68 100644 --- a/drivers/pmdomain/imx/imx93-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c @@ -48,6 +48,8 @@ #define PRIO(X) (X) +#define BLK_CTRL_NO_PARENT UINT_MAX + struct imx93_blk_ctrl_domain; struct imx93_blk_ctrl { @@ -68,12 +70,18 @@ struct imx93_blk_ctrl_qos { u32 cfg_prio; }; +struct imx93_blk_ctrl_subdomain_link { + struct generic_pm_domain *parent; + struct generic_pm_domain *subdomain; +}; + struct imx93_blk_ctrl_domain_data { const char *name; const char * const *clk_names; int num_clks; u32 rst_mask; u32 clk_mask; + u32 parent; int num_qos; struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS]; }; @@ -203,6 +211,13 @@ static void imx93_release_pm_genpd(void *data) pm_genpd_remove(genpd); } +static void imx93_release_subdomain(void *data) +{ + struct imx93_blk_ctrl_subdomain_link *link = data; + + pm_genpd_remove_subdomain(link->parent, link->subdomain); +} + static struct lock_class_key blk_ctrl_genpd_lock_class; static int imx93_blk_ctrl_probe(struct platform_device *pdev) @@ -302,6 +317,34 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev) bc->onecell_data.domains[i] = &domain->genpd; } + for (i = 0; i < bc_data->num_domains; i++) { + struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; + const struct imx93_blk_ctrl_domain_data *data = domain->data; + struct imx93_blk_ctrl_subdomain_link *link; + + if (bc_data->skip_mask & BIT(i) || + data->parent == BLK_CTRL_NO_PARENT) + continue; + + link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + link->parent = &bc->domains[data->parent].genpd; + link->subdomain = &domain->genpd; + + ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd, + &domain->genpd); + if (ret) + return dev_err_probe(dev, ret, "failed to add subdomain %s\n", + domain->genpd.name); + + ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link); + if (ret) + return dev_err_probe(dev, ret, + "failed to add subdomain release callback\n"); + } + ret = devm_pm_runtime_enable(dev); if (ret) return dev_err_probe(dev, ret, "failed to enable pm-runtime\n"); @@ -326,8 +369,9 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .name = "mediablk-mipi-dsi", .clk_names = (const char *[]){ "dsi" }, .num_clks = 1, - .rst_mask = BIT(11) | BIT(12), - .clk_mask = BIT(11) | BIT(12), + .rst_mask = BIT(11), + .clk_mask = BIT(11), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_MIPI_CSI] = { .name = "mediablk-mipi-csi", @@ -335,6 +379,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(9) | BIT(10), .clk_mask = BIT(9) | BIT(10), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_PXP] = { .name = "mediablk-pxp", @@ -342,6 +387,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(7) | BIT(8), .clk_mask = BIT(7) | BIT(8), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 2, .qos = { { @@ -363,6 +409,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(4) | BIT(5) | BIT(6), .clk_mask = BIT(4) | BIT(5) | BIT(6), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 1, .qos = { { @@ -379,6 +426,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(2) | BIT(3), .clk_mask = BIT(2) | BIT(3), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 4, .qos = { { @@ -404,6 +452,14 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] } } }, + [IMX93_MEDIABLK_PD_MIPI_PHY] = { + .name = "mediablk-mipi-phy", + .clk_names = NULL, + .num_clks = 0, + .rst_mask = BIT(12), + .clk_mask = BIT(12), + .parent = BLK_CTRL_NO_PARENT, + }, }; static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = { diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 63120e703923..c12127d8e8ae 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -305,10 +305,10 @@ static struct rpmhpd *sa8775p_rpmhpds[] = { [SA8775P_LMX] = &lmx, [SA8775P_MMCX] = &mmcx, [SA8775P_MMCX_AO] = &mmcx_ao, - [SA8775P_MXC] = &mxc, - [SA8775P_MXC_AO] = &mxc_ao, [SA8775P_MX] = &mx, [SA8775P_MX_AO] = &mx_ao, + [SA8775P_MXC] = &mxc, + [SA8775P_MXC_AO] = &mxc_ao, [SA8775P_NSP0] = &nsp0, [SA8775P_NSP1] = &nsp1, }; @@ -325,10 +325,10 @@ static struct rpmhpd *nord_rpmhpds[] = { [RPMHPD_EBI] = &ebi, [RPMHPD_GFX] = &gfx, [RPMHPD_GFX1] = &gfx1, - [RPMHPD_MX] = &mx, - [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MXC] = &mxc, [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP0] = &nsp0, @@ -480,9 +480,9 @@ static struct rpmhpd *sm7150_rpmhpds[] = { [RPMHPD_GFX] = &gfx, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, + [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MSS] = &mss, }; static const struct rpmhpd_desc sm7150_desc = { @@ -698,11 +698,11 @@ static struct rpmhpd *hawi_rpmhpds[] = { [RPMHPD_LMX] = &lmx, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MXC] = &mxc, [RPMHPD_MXC_AO] = &mxc_ao, - [RPMHPD_MSS] = &mss, [RPMHPD_NSP] = &nsp, [RPMHPD_NSP2] = &nsp2, }; @@ -809,18 +809,18 @@ static struct rpmhpd *glymur_rpmhpds[] = { [RPMHPD_CX_AO] = &cx_ao, [RPMHPD_EBI] = &ebi, [RPMHPD_GFX] = &gfx, + [RPMHPD_GMXC] = &gmxc, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, [RPMHPD_MXC] = &mxc, [RPMHPD_MXC_AO] = &mxc_ao, - [RPMHPD_MSS] = &mss, [RPMHPD_NSP] = &nsp, [RPMHPD_NSP2] = &nsp2, - [RPMHPD_GMXC] = &gmxc, }; static const struct rpmhpd_desc glymur_desc = { @@ -834,15 +834,15 @@ static struct rpmhpd *x1e80100_rpmhpds[] = { [RPMHPD_CX_AO] = &cx_ao, [RPMHPD_EBI] = &ebi, [RPMHPD_GFX] = &gfx, + [RPMHPD_GMXC] = &gmxc, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, [RPMHPD_MMCX] = &mmcx, [RPMHPD_MMCX_AO] = &mmcx_ao, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_NSP] = &nsp, [RPMHPD_MXC] = &mxc, - [RPMHPD_GMXC] = &gmxc, + [RPMHPD_NSP] = &nsp, }; static const struct rpmhpd_desc x1e80100_desc = { @@ -860,10 +860,10 @@ static struct rpmhpd *qcs8300_rpmhpds[] = { [RPMHPD_LMX] = &lmx, [RPMHPD_MMCX] = &mmcx_w_cx_parent, [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, - [RPMHPD_MXC] = &mxc, - [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP0] = &nsp0, [RPMHPD_NSP1] = &nsp1, }; diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index e5202a7b6209..36b0c52a4b8b 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -829,3 +829,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-btemp"); MODULE_DESCRIPTION("AB8500 battery temperature driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 1813fbdfa1c1..5c3af3571013 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3751,3 +3751,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-charger"); MODULE_DESCRIPTION("AB8500 charger management driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index eb5c1ae68e44..3ab33cf465c6 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3252,3 +3252,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); MODULE_ALIAS("platform:ab8500-fg"); MODULE_DESCRIPTION("AB8500 Fuel Gauge driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 5f6ea416fa30..e9049d6229df 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -421,3 +421,4 @@ module_platform_driver(axp20x_ac_power_driver); MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 50ca8e110085..ee8701a6e907 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -1155,3 +1155,4 @@ module_platform_driver(axp20x_batt_driver); MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index e75d1e377ac1..599adcf84968 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -1080,3 +1080,4 @@ module_platform_driver(axp20x_usb_power_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 5af334c0a980..05a850ec0131 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -817,3 +817,4 @@ MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>"); MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>"); MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 59c741993ef8..4d7e77d7375b 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1206,3 +1206,4 @@ module_platform_driver(cpcap_battery_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_DESCRIPTION("CPCAP PMIC Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index ec8d2a9245d9..744ee12edd1e 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -977,3 +977,4 @@ MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_DESCRIPTION("CPCAP Battery Charger Interface driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cpcap-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c index 27f36ef5b88d..58449df6068c 100644 --- a/drivers/power/supply/da9150-charger.c +++ b/drivers/power/supply/da9150-charger.c @@ -644,3 +644,4 @@ module_platform_driver(da9150_charger_driver); MODULE_DESCRIPTION("Charger Driver for DA9150"); MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index f5f2566b3a32..d18c8ee40405 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -298,3 +298,4 @@ module_platform_driver(gab_driver); MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>"); MODULE_DESCRIPTION("generic battery driver using IIO"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index b111c7ce2be3..5be269f17bff 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -190,3 +190,4 @@ module_platform_driver(ingenic_battery_driver); MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs"); MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/intel_dc_ti_battery.c b/drivers/power/supply/intel_dc_ti_battery.c index 67a75281b0ac..cb6fa8d88b43 100644 --- a/drivers/power/supply/intel_dc_ti_battery.c +++ b/drivers/power/supply/intel_dc_ti_battery.c @@ -389,3 +389,4 @@ MODULE_ALIAS("platform:" DEV_NAME); MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c index 582644f4e8b3..1e53f11a8e2b 100644 --- a/drivers/power/supply/lego_ev3_battery.c +++ b/drivers/power/supply/lego_ev3_battery.c @@ -230,3 +230,4 @@ module_platform_driver(lego_ev3_battery_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Lechner <david@lechnology.com>"); MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index f0a680c155c4..8c6ec98362d0 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -727,3 +727,4 @@ MODULE_DESCRIPTION("TI LP8788 Charger Driver"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:lp8788-charger"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index e94d53b36aa4..19a9b1bed29e 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -639,3 +639,4 @@ module_i2c_driver(max17040_i2c_driver); MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mp2629_charger.c b/drivers/power/supply/mp2629_charger.c index f758d6a7bc8c..c06743d2abff 100644 --- a/drivers/power/supply/mp2629_charger.c +++ b/drivers/power/supply/mp2629_charger.c @@ -659,3 +659,4 @@ module_platform_driver(mp2629_charger_driver); MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>"); MODULE_DESCRIPTION("MP2629 Charger driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index 916556baa854..8ac5f4d59f39 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -930,3 +930,4 @@ module_platform_driver(mt6370_chg_driver); MODULE_AUTHOR("ChiaEn Wu <chiaen_wu@richtek.com>"); MODULE_DESCRIPTION("MediaTek MT6370 Charger Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c index bf2e2ccc454a..7010374b302a 100644 --- a/drivers/power/supply/qcom_smbx.c +++ b/drivers/power/supply/qcom_smbx.c @@ -1057,3 +1057,4 @@ module_platform_driver(qcom_spmi_smb); MODULE_AUTHOR("Casey Connolly <casey.connolly@linaro.org>"); MODULE_DESCRIPTION("Qualcomm SMB2 Charger Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c index 40dec55a9f73..a3f30e390c11 100644 --- a/drivers/power/supply/rn5t618_power.c +++ b/drivers/power/supply/rn5t618_power.c @@ -821,3 +821,4 @@ module_platform_driver(rn5t618_power_driver); MODULE_ALIAS("platform:rn5t618-power"); MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c index b0220ec2d926..57266921dc8e 100644 --- a/drivers/power/supply/rx51_battery.c +++ b/drivers/power/supply/rx51_battery.c @@ -246,3 +246,4 @@ MODULE_ALIAS("platform:rx51-battery"); MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); MODULE_DESCRIPTION("Nokia RX-51 battery driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index a7ed9de8a289..1719ec4173e6 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -1350,3 +1350,4 @@ module_platform_driver(sc27xx_fgu_driver); MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index 04216b2bfb6c..151f7b24e9b9 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -1144,3 +1144,4 @@ MODULE_AUTHOR("Gražvydas Ignotas"); MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_bci"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c index a99b3ff26929..5e04b4f0a135 100644 --- a/drivers/power/supply/twl4030_madc_battery.c +++ b/drivers/power/supply/twl4030_madc_battery.c @@ -235,3 +235,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>"); MODULE_DESCRIPTION("twl4030_madc battery driver"); MODULE_ALIAS("platform:twl4030_madc_battery"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/power/supply/twl6030_charger.c b/drivers/power/supply/twl6030_charger.c index b4ec26ff257c..82911a811f4e 100644 --- a/drivers/power/supply/twl6030_charger.c +++ b/drivers/power/supply/twl6030_charger.c @@ -579,3 +579,4 @@ module_platform_driver(twl6030_charger_driver); MODULE_DESCRIPTION("TWL6030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e8886a9b64d9..729776086879 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -569,7 +569,7 @@ config PWM_PCA9685 config PWM_PXA tristate "PXA PWM support" depends on ARCH_PXA || ARCH_MMP || ARCH_SPACEMIT || COMPILE_TEST - depends on HAS_IOMEM + depends on HAS_IOMEM && OF help Generic PWM framework driver for PXA. diff --git a/drivers/pwm/pwm-apple.c b/drivers/pwm/pwm-apple.c index aa49ca1c30e8..dac8c9a76b2d 100644 --- a/drivers/pwm/pwm-apple.c +++ b/drivers/pwm/pwm-apple.c @@ -141,7 +141,7 @@ static int apple_pwm_probe(struct platform_device *pdev) static const struct of_device_id apple_pwm_of_match[] = { { .compatible = "apple,s5l-fpwm" }, - {} + { } }; MODULE_DEVICE_TABLE(of, apple_pwm_of_match); diff --git a/drivers/pwm/pwm-argon-fan-hat.c b/drivers/pwm/pwm-argon-fan-hat.c index 2c59bd142d40..b480663e87d5 100644 --- a/drivers/pwm/pwm-argon-fan-hat.c +++ b/drivers/pwm/pwm-argon-fan-hat.c @@ -89,7 +89,7 @@ static int argon_fan_hat_i2c_probe(struct i2c_client *i2c) static const struct of_device_id argon_fan_hat_dt_ids[] = { { .compatible = "argon40,fan-hat" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, argon_fan_hat_dt_ids); diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index 387a0d1fa4f2..1ba2c9a049b9 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -217,23 +217,20 @@ static const struct of_device_id atmel_hlcdc_dt_ids[] = { .compatible = "atmel,at91sam9n12-hlcdc", /* 9n12 has same errata as 9x5 HLCDC PWM */ .data = &atmel_hlcdc_pwm_at91sam9x5_errata, - }, - { + }, { .compatible = "atmel,at91sam9x5-hlcdc", .data = &atmel_hlcdc_pwm_at91sam9x5_errata, - }, - { + }, { .compatible = "atmel,sama5d2-hlcdc", - }, - { + }, { .compatible = "atmel,sama5d3-hlcdc", .data = &atmel_hlcdc_pwm_sama5d3_errata, - }, - { + }, { .compatible = "atmel,sama5d4-hlcdc", .data = &atmel_hlcdc_pwm_sama5d3_errata, + }, { + .compatible = "microchip,sam9x60-hlcdc", }, - { .compatible = "microchip,sam9x60-hlcdc", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids); diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index a765ef279b51..f74a42d75b81 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -374,9 +374,9 @@ static struct atmel_tcb_config tcb_sama5d2_config = { }; static const struct of_device_id atmel_tcb_of_match[] = { - { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, }, - { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, }, - { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, }, + { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config }, + { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config }, + { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config }, { /* sentinel */ } }; diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 06d22d0f7b26..86918523d821 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -441,9 +441,8 @@ static const struct of_device_id atmel_pwm_dt_ids[] = { }, { .compatible = "microchip,sam9x60-pwm", .data = &mchp_sam9x60_pwm_data, - }, { - /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids); diff --git a/drivers/pwm/pwm-bcm-iproc.c b/drivers/pwm/pwm-bcm-iproc.c index f4c9f10e490e..dc1e792b1e08 100644 --- a/drivers/pwm/pwm-bcm-iproc.c +++ b/drivers/pwm/pwm-bcm-iproc.c @@ -230,7 +230,7 @@ static int iproc_pwmc_probe(struct platform_device *pdev) static const struct of_device_id bcm_iproc_pwmc_dt[] = { { .compatible = "brcm,iproc-pwm" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, bcm_iproc_pwmc_dt); diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 022c078aae84..ecfa3e3707f4 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -319,7 +319,7 @@ static int kona_pwmc_probe(struct platform_device *pdev) static const struct of_device_id bcm_kona_pwmc_dt[] = { { .compatible = "brcm,kona-pwm" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, bcm_kona_pwmc_dt); diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index 532903da521f..a4fbf9cfc6f7 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -165,7 +165,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835_pwm_pm_ops, bcm2835_pwm_suspend, bcm2835_pwm_resume); static const struct of_device_id bcm2835_pwm_of_match[] = { - { .compatible = "brcm,bcm2835-pwm", }, + { .compatible = "brcm,bcm2835-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, bcm2835_pwm_of_match); diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index da9954818302..d36a245b6b4f 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -190,7 +190,7 @@ static const struct pwm_ops berlin_pwm_ops = { static const struct of_device_id berlin_pwm_match[] = { { .compatible = "marvell,berlin-pwm" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, berlin_pwm_match); diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c index 82d27d07ba91..790ef7ffbbe5 100644 --- a/drivers/pwm/pwm-brcmstb.c +++ b/drivers/pwm/pwm-brcmstb.c @@ -222,7 +222,7 @@ static const struct pwm_ops brcmstb_pwm_ops = { }; static const struct of_device_id brcmstb_pwm_of_match[] = { - { .compatible = "brcm,bcm7038-pwm", }, + { .compatible = "brcm,bcm7038-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, brcmstb_pwm_of_match); diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c index f8f5af57acba..1907f2f10e5c 100644 --- a/drivers/pwm/pwm-clk.c +++ b/drivers/pwm/pwm-clk.c @@ -119,7 +119,7 @@ static void pwm_clk_remove(struct platform_device *pdev) } static const struct of_device_id pwm_clk_dt_ids[] = { - { .compatible = "clk-pwm", }, + { .compatible = "clk-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pwm_clk_dt_ids); diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index 2c92ce754872..1ce12ee0329b 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -91,7 +91,7 @@ static int clps711x_pwm_probe(struct platform_device *pdev) } static const struct of_device_id clps711x_pwm_dt_ids[] = { - { .compatible = "cirrus,ep7209-pwm", }, + { .compatible = "cirrus,ep7209-pwm" }, { } }; MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids); diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c index 67cfa17f58e0..08a24a45c76d 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -267,7 +267,7 @@ static int cros_ec_pwm_probe(struct platform_device *pdev) static const struct of_device_id cros_ec_pwm_of_match[] = { { .compatible = "google,cros-ec-pwm" }, { .compatible = "google,cros-ec-pwm-type" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match); #endif diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index e02ee6383dbc..29ad8d0b5536 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -266,7 +266,7 @@ static const struct of_device_id hibvt_pwm_of_match[] = { .data = &hi3559v100_shub_soc_info }, { .compatible = "hisilicon,hi3559v100-pwm", .data = &hi3559v100_soc_info }, - { } + { } }; MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match); diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index 80fdb3303400..c20753a36d6d 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -442,7 +442,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm, pwm_imx_tpm_suspend, pwm_imx_tpm_resume); static const struct of_device_id imx_tpm_pwm_dt_ids[] = { - { .compatible = "fsl,imx7ulp-pwm", }, + { .compatible = "fsl,imx7ulp-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_tpm_pwm_dt_ids); diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index d5535d208005..075d46180dff 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -151,7 +151,7 @@ static const struct pwm_ops pwm_imx1_ops = { }; static const struct of_device_id pwm_imx1_dt_ids[] = { - { .compatible = "fsl,imx1-pwm", }, + { .compatible = "fsl,imx1-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index c8b801fcb525..7c1ad15008d2 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -378,7 +378,7 @@ static const struct pwm_ops pwm_imx27_ops = { }; static const struct of_device_id pwm_imx27_dt_ids[] = { - { .compatible = "fsl,imx27-pwm", }, + { .compatible = "fsl,imx27-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); diff --git a/drivers/pwm/pwm-ipq.c b/drivers/pwm/pwm-ipq.c index c53373948136..6b7aec566f7e 100644 --- a/drivers/pwm/pwm-ipq.c +++ b/drivers/pwm/pwm-ipq.c @@ -245,8 +245,8 @@ static int ipq_pwm_probe(struct platform_device *pdev) } static const struct of_device_id pwm_ipq_dt_match[] = { - { .compatible = "qcom,ipq6018-pwm", }, - {} + { .compatible = "qcom,ipq6018-pwm" }, + { } }; MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match); diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index e0b5966fc7fe..0db1474c7c8c 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -261,7 +261,7 @@ static const struct of_device_id jz4740_pwm_dt_ids[] = { { .compatible = "ingenic,jz4740-pwm", .data = &jz4740_soc_info }, { .compatible = "ingenic,jz4725b-pwm", .data = &jz4725b_soc_info }, { .compatible = "ingenic,x1000-pwm", .data = &x1000_soc_info }, - {}, + { } }; MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids); diff --git a/drivers/pwm/pwm-loongson.c b/drivers/pwm/pwm-loongson.c index 31a57edecfd0..f2fb35b7af2b 100644 --- a/drivers/pwm/pwm-loongson.c +++ b/drivers/pwm/pwm-loongson.c @@ -269,7 +269,7 @@ static const struct of_device_id pwm_loongson_of_ids[] = { MODULE_DEVICE_TABLE(of, pwm_loongson_of_ids); static const struct acpi_device_id pwm_loongson_acpi_ids[] = { - { "LOON0006" }, + { .id = "LOON0006" }, { } }; MODULE_DEVICE_TABLE(acpi, pwm_loongson_acpi_ids); diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c index 90b0733c00c1..10537e74be28 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -296,7 +296,7 @@ static int lp3943_pwm_probe(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id lp3943_pwm_of_match[] = { - { .compatible = "ti,lp3943-pwm", }, + { .compatible = "ti,lp3943-pwm" }, { } }; MODULE_DEVICE_TABLE(of, lp3943_pwm_of_match); diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 01d471725106..59e9b58a31bc 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -329,7 +329,7 @@ static const struct pwm_ops lpc18xx_pwm_ops = { static const struct of_device_id lpc18xx_pwm_of_match[] = { { .compatible = "nxp,lpc1850-sct-pwm" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lpc18xx_pwm_of_match); diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index c748537e57d1..19ec53648f40 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -155,7 +155,7 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) } static const struct of_device_id lpc32xx_pwm_dt_ids[] = { - { .compatible = "nxp,lpc3220-pwm", }, + { .compatible = "nxp,lpc3220-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids); diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index 3a0fd6593520..bcbfda1a6624 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -57,7 +57,7 @@ static const struct pci_device_id pwm_lpss_pci_ids[] = { { 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-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c index 8b95064b8703..489ca0c09c04 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -58,11 +58,11 @@ static int pwm_lpss_probe_platform(struct platform_device *pdev) } static const struct acpi_device_id pwm_lpss_acpi_match[] = { - { "80860F09", (unsigned long)&pwm_lpss_byt_info }, - { "80862288", (unsigned long)&pwm_lpss_bsw_info }, - { "80862289", (unsigned long)&pwm_lpss_bsw_info }, - { "80865AC8", (unsigned long)&pwm_lpss_bxt_info }, - { }, + { .id = "80860F09", .driver_data = (unsigned long)&pwm_lpss_byt_info }, + { .id = "80862288", .driver_data = (unsigned long)&pwm_lpss_bsw_info }, + { .id = "80862289", .driver_data = (unsigned long)&pwm_lpss_bsw_info }, + { .id = "80865AC8", .driver_data = (unsigned long)&pwm_lpss_bxt_info }, + { } }; MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 992137a27750..0bb49be657b7 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -627,7 +627,7 @@ static const struct of_device_id pwm_mediatek_of_match[] = { { .compatible = "mediatek,mt8183-pwm", .data = &mt8183_pwm_data }, { .compatible = "mediatek,mt8365-pwm", .data = &mt8365_pwm_data }, { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data }, - { }, + { } }; MODULE_DEVICE_TABLE(of, pwm_mediatek_of_match); diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 8c6bf3d49753..20e6cd07127c 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -596,15 +596,14 @@ static const struct of_device_id meson_pwm_matches[] = { { .compatible = "amlogic,meson8-pwm-v2", .data = &pwm_meson8_v2_data - }, - { + }, { .compatible = "amlogic,meson-axg-pwm-v2", .data = &pwm_meson_axg_v2_data - }, - { + }, { .compatible = "amlogic,meson-g12-pwm-v2", .data = &pwm_meson_axg_v2_data }, + /* The following compatibles are obsolete */ { .compatible = "amlogic,meson8b-pwm", @@ -642,7 +641,7 @@ static const struct of_device_id meson_pwm_matches[] = { .compatible = "amlogic,meson-s4-pwm", .data = &pwm_s4_data }, - {}, + { } }; MODULE_DEVICE_TABLE(of, meson_pwm_matches); diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index bafd6b6195f6..b5409e15dac8 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -295,10 +295,10 @@ static const struct mtk_pwm_data mt8183_pwm_data = { }; static const struct of_device_id mtk_disp_pwm_of_match[] = { - { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data}, - { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data}, - { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data}, - { .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data}, + { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data }, + { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data }, + { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data }, + { .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data }, { } }; MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match); diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 8cad214b1c29..e1a3df6549fc 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -163,7 +163,7 @@ static int mxs_pwm_probe(struct platform_device *pdev) } static const struct of_device_id mxs_pwm_dt_ids[] = { - { .compatible = "fsl,imx23-pwm", }, + { .compatible = "fsl,imx23-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids); diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 1858a77401f8..51830a87275c 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -444,8 +444,8 @@ static void pwm_omap_dmtimer_remove(struct platform_device *pdev) } static const struct of_device_id pwm_omap_dmtimer_of_match[] = { - {.compatible = "ti,omap-dmtimer-pwm"}, - {} + { .compatible = "ti,omap-dmtimer-pwm" }, + { } }; MODULE_DEVICE_TABLE(of, pwm_omap_dmtimer_of_match); diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index a02255a64ea8..a2792b803643 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -544,13 +544,13 @@ static const struct i2c_device_id pca9685_id[] = { MODULE_DEVICE_TABLE(i2c, pca9685_id); static const struct acpi_device_id pca9685_acpi_ids[] = { - { "INT3492", 0 }, - { /* sentinel */ }, + { .id = "INT3492" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(acpi, pca9685_acpi_ids); static const struct of_device_id pca9685_dt_ids[] = { - { .compatible = "nxp,pca9685-pwm", }, + { .compatible = "nxp,pca9685-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pca9685_dt_ids); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index c206dbe9000e..5f06088f82d4 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -136,7 +136,6 @@ static const struct pwm_ops pxa_pwm_ops = { .apply = pxa_pwm_apply, }; -#ifdef CONFIG_OF /* * Device tree users must create one device instance for each PWM channel. * Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver @@ -144,16 +143,13 @@ static const struct pwm_ops pxa_pwm_ops = { * supported identically. */ static const struct of_device_id pwm_of_match[] = { - { .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]}, - { .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]}, - { .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]}, - { .compatible = "marvell,pxa910-pwm", .data = &pwm_id_table[0]}, + { .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0] }, + { .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0] }, + { .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0] }, + { .compatible = "marvell,pxa910-pwm", .data = &pwm_id_table[0] }, { } }; MODULE_DEVICE_TABLE(of, pwm_of_match); -#else -#define pwm_of_match NULL -#endif static int pwm_probe(struct platform_device *pdev) { @@ -165,7 +161,7 @@ static int pwm_probe(struct platform_device *pdev) struct reset_control *rst; int ret = 0; - if (IS_ENABLED(CONFIG_OF) && id == NULL) + if (id == NULL) id = of_device_get_match_data(dev); if (id == NULL) @@ -191,9 +187,7 @@ static int pwm_probe(struct platform_device *pdev) return PTR_ERR(rst); chip->ops = &pxa_pwm_ops; - - if (IS_ENABLED(CONFIG_OF)) - chip->of_xlate = of_pwm_single_xlate; + chip->of_xlate = of_pwm_single_xlate; pc->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pc->mmio_base)) diff --git a/drivers/pwm/pwm-raspberrypi-poe.c b/drivers/pwm/pwm-raspberrypi-poe.c index 8921e7ea2cea..7cea6561c730 100644 --- a/drivers/pwm/pwm-raspberrypi-poe.c +++ b/drivers/pwm/pwm-raspberrypi-poe.c @@ -177,7 +177,7 @@ static int raspberrypi_pwm_probe(struct platform_device *pdev) } static const struct of_device_id raspberrypi_pwm_of_match[] = { - { .compatible = "raspberrypi,firmware-poe-pwm", }, + { .compatible = "raspberrypi,firmware-poe-pwm" }, { } }; MODULE_DEVICE_TABLE(of, raspberrypi_pwm_of_match); diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 578dbdd2d5a7..1733cd700693 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -248,8 +248,8 @@ static void rcar_pwm_remove(struct platform_device *pdev) } static const struct of_device_id rcar_pwm_of_table[] = { - { .compatible = "renesas,pwm-rcar", }, - { }, + { .compatible = "renesas,pwm-rcar" }, + { } }; MODULE_DEVICE_TABLE(of, rcar_pwm_of_table); diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 2196080b4177..140fd0bcb93f 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -479,11 +479,11 @@ static int tpu_probe(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id tpu_of_table[] = { - { .compatible = "renesas,tpu-r8a73a4", }, - { .compatible = "renesas,tpu-r8a7740", }, - { .compatible = "renesas,tpu-r8a7790", }, - { .compatible = "renesas,tpu", }, - { }, + { .compatible = "renesas,tpu-r8a73a4" }, + { .compatible = "renesas,tpu-r8a7740" }, + { .compatible = "renesas,tpu-r8a7790" }, + { .compatible = "renesas,tpu" }, + { } }; MODULE_DEVICE_TABLE(of, tpu_of_table); diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 67b85bdb491b..050ca8aad0ca 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -292,10 +292,10 @@ static const struct rockchip_pwm_data pwm_data_v3 = { }; static const struct of_device_id rockchip_pwm_dt_ids[] = { - { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, - { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, - { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, - { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3}, + { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1 }, + { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2 }, + { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop }, + { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index dfa1d11a48a8..ac8399b96ad1 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -437,7 +437,7 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) } static const struct of_device_id rzg2l_gpt_of_table[] = { - { .compatible = "renesas,rzg2l-gpt", }, + { .compatible = "renesas,rzg2l-gpt" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, rzg2l_gpt_of_table); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 951b38ff5f8e..331e81f1594a 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -501,7 +501,7 @@ static const struct of_device_id samsung_pwm_matches[] = { { .compatible = "samsung,s5p6440-pwm", .data = &s5p64x0_variant }, { .compatible = "samsung,s5pc100-pwm", .data = &s5pc100_variant }, { .compatible = "samsung,exynos4210-pwm", .data = &s5p64x0_variant }, - {}, + { } }; MODULE_DEVICE_TABLE(of, samsung_pwm_matches); diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index e11ecf1fa0f9..4d0e4512d8a5 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -355,7 +355,7 @@ static void pwm_sifive_remove(struct platform_device *dev) static const struct of_device_id pwm_sifive_of_match[] = { { .compatible = "sifive,pwm0" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, pwm_sifive_of_match); diff --git a/drivers/pwm/pwm-sl28cpld.c b/drivers/pwm/pwm-sl28cpld.c index 0dc2e3f809c3..0e25a05ea538 100644 --- a/drivers/pwm/pwm-sl28cpld.c +++ b/drivers/pwm/pwm-sl28cpld.c @@ -244,7 +244,7 @@ static int sl28cpld_pwm_probe(struct platform_device *pdev) static const struct of_device_id sl28cpld_pwm_of_match[] = { { .compatible = "kontron,sl28cpld-pwm" }, - {} + { } }; MODULE_DEVICE_TABLE(of, sl28cpld_pwm_of_match); diff --git a/drivers/pwm/pwm-sophgo-sg2042.c b/drivers/pwm/pwm-sophgo-sg2042.c index 7d07b0ca7d29..0b6461172e6a 100644 --- a/drivers/pwm/pwm-sophgo-sg2042.c +++ b/drivers/pwm/pwm-sophgo-sg2042.c @@ -225,11 +225,10 @@ static const struct sg2042_chip_data sg2044_chip_data = { static const struct of_device_id sg2042_pwm_ids[] = { { .compatible = "sophgo,sg2042-pwm", - .data = &sg2042_chip_data - }, - { + .data = &sg2042_chip_data, + }, { .compatible = "sophgo,sg2044-pwm", - .data = &sg2044_chip_data + .data = &sg2044_chip_data, }, { } }; diff --git a/drivers/pwm/pwm-sprd.c b/drivers/pwm/pwm-sprd.c index 438dbaa3a98f..51cab6a54fe2 100644 --- a/drivers/pwm/pwm-sprd.c +++ b/drivers/pwm/pwm-sprd.c @@ -276,8 +276,8 @@ static int sprd_pwm_probe(struct platform_device *pdev) } static const struct of_device_id sprd_pwm_of_match[] = { - { .compatible = "sprd,ums512-pwm", }, - { }, + { .compatible = "sprd,ums512-pwm" }, + { } }; MODULE_DEVICE_TABLE(of, sprd_pwm_of_match); diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 3b702b8f0c7f..e5c3cf594571 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -625,7 +625,7 @@ static int sti_pwm_probe(struct platform_device *pdev) } static const struct of_device_id sti_pwm_of_match[] = { - { .compatible = "st,sti-pwm", }, + { .compatible = "st,sti-pwm" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sti_pwm_of_match); diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c index 4789eafb8bac..2aa88f4985f5 100644 --- a/drivers/pwm/pwm-stm32-lp.c +++ b/drivers/pwm/pwm-stm32-lp.c @@ -412,8 +412,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend, stm32_pwm_lp_resume); static const struct of_device_id stm32_pwm_lp_of_match[] = { - { .compatible = "st,stm32-pwm-lp", }, - {}, + { .compatible = "st,stm32-pwm-lp" }, + { } }; MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index c708e4a7ad70..ea5a51a2800f 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -918,9 +918,9 @@ static int stm32_pwm_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume); static const struct of_device_id stm32_pwm_of_match[] = { - { .compatible = "st,stm32-pwm", }, - { .compatible = "st,stm32mp25-pwm", }, - { /* end node */ }, + { .compatible = "st,stm32-pwm" }, + { .compatible = "st,stm32mp25-pwm" }, + { /* end node */ } }; MODULE_DEVICE_TABLE(of, stm32_pwm_of_match); diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 6c5591ca868b..0752152b1470 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -369,7 +369,7 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = { .data = &sun50i_h6_pwm_data, }, { /* sentinel */ - }, + } }; MODULE_DEVICE_TABLE(of, sun4i_pwm_dt_ids); diff --git a/drivers/pwm/pwm-sunplus.c b/drivers/pwm/pwm-sunplus.c index cc8137f108df..4c4a4b0f5f18 100644 --- a/drivers/pwm/pwm-sunplus.c +++ b/drivers/pwm/pwm-sunplus.c @@ -213,8 +213,8 @@ static int sunplus_pwm_probe(struct platform_device *pdev) } static const struct of_device_id sunplus_pwm_of_match[] = { - { .compatible = "sunplus,sp7021-pwm", }, - {} + { .compatible = "sunplus,sp7021-pwm" }, + { } }; MODULE_DEVICE_TABLE(of, sunplus_pwm_of_match); diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 67cc5e8bdb0e..3751737ead43 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -211,9 +211,9 @@ static const struct pwm_ops ecap_pwm_ops = { }; static const struct of_device_id ecap_of_match[] = { - { .compatible = "ti,am3352-ecap" }, - { .compatible = "ti,am33xx-ecap" }, - {}, + { .compatible = "ti,am3352-ecap" }, + { .compatible = "ti,am33xx-ecap" }, + { } }; MODULE_DEVICE_TABLE(of, ecap_of_match); diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 2533c95b0ba9..3a53489f0b98 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -407,7 +407,7 @@ static const struct pwm_ops ehrpwm_pwm_ops = { static const struct of_device_id ehrpwm_of_match[] = { { .compatible = "ti,am3352-ehrpwm" }, { .compatible = "ti,am33xx-ehrpwm" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ehrpwm_of_match); diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index a555cc3be4b3..e1b6871af81e 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -338,7 +338,7 @@ static int twl_pwmled_probe(struct platform_device *pdev) static const struct of_device_id twl_pwmled_of_match[] = { { .compatible = "ti,twl4030-pwmled" }, { .compatible = "ti,twl6030-pwmled" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, twl_pwmled_of_match); #endif diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index 8f981ffff4b4..99c1caf4f19c 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -362,7 +362,7 @@ static int twl_pwm_probe(struct platform_device *pdev) static const struct of_device_id twl_pwm_of_match[] = { { .compatible = "ti,twl4030-pwm" }, { .compatible = "ti,twl6030-pwm" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, twl_pwm_of_match); #endif diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c index 28fae4979e3f..830aef483bd0 100644 --- a/drivers/pwm/pwm-visconti.c +++ b/drivers/pwm/pwm-visconti.c @@ -156,7 +156,7 @@ static int visconti_pwm_probe(struct platform_device *pdev) } static const struct of_device_id visconti_pwm_of_match[] = { - { .compatible = "toshiba,visconti-pwm", }, + { .compatible = "toshiba,visconti-pwm" }, { } }; MODULE_DEVICE_TABLE(of, visconti_pwm_of_match); diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 149d9e35b78c..8013e31b1147 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -226,7 +226,7 @@ static const struct pwm_ops vt8500_pwm_ops = { }; static const struct of_device_id vt8500_pwm_dt_ids[] = { - { .compatible = "via,vt8500-pwm", }, + { .compatible = "via,vt8500-pwm" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids); diff --git a/drivers/pwm/pwm-xilinx.c b/drivers/pwm/pwm-xilinx.c index 52c241982807..fa2d10e658dd 100644 --- a/drivers/pwm/pwm-xilinx.c +++ b/drivers/pwm/pwm-xilinx.c @@ -281,8 +281,8 @@ static int xilinx_pwm_probe(struct platform_device *pdev) } static const struct of_device_id xilinx_pwm_of_match[] = { - { .compatible = "xlnx,xps-timer-1.00.a", }, - {}, + { .compatible = "xlnx,xps-timer-1.00.a" }, + { } }; MODULE_DEVICE_TABLE(of, xilinx_pwm_of_match); diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index dcd6619a4b02..3cc25d053451 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -874,6 +874,7 @@ static struct rio_net *rio_scan_alloc_net(struct rio_mport *mport, net->dev.release = rio_scan_release_dev; if (rio_add_net(net)) { put_device(&net->dev); + mport->net = NULL; net = NULL; } } diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index dc5d67767336..1797929dfe56 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -248,7 +248,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, ret = regulator_lock_nested(rdev1, ww_ctx); WARN_ON(ret); ret = regulator_lock_nested(rdev2, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); goto exit; } @@ -264,7 +264,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, swap(held, contended); ret = regulator_lock_nested(contended, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); break; } diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 1ec2e1348891..751fe36580fa 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -13,7 +13,7 @@ #include <linux/err.h> #include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/db8500-prcmu.h> @@ -93,13 +93,13 @@ static int enable_epod(u16 epod_id, bool ramret) if (ramret) { if (!epod_on[epod_id]) { - ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET); + ret = db8500_prcmu_set_epod(epod_id, EPOD_STATE_RAMRET); if (ret < 0) return ret; } epod_ramret[epod_id] = true; } else { - ret = prcmu_set_epod(epod_id, EPOD_STATE_ON); + ret = db8500_prcmu_set_epod(epod_id, EPOD_STATE_ON); if (ret < 0) return ret; epod_on[epod_id] = true; @@ -114,18 +114,18 @@ static int disable_epod(u16 epod_id, bool ramret) if (ramret) { if (!epod_on[epod_id]) { - ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF); + ret = db8500_prcmu_set_epod(epod_id, EPOD_STATE_OFF); if (ret < 0) return ret; } epod_ramret[epod_id] = false; } else { if (epod_ramret[epod_id]) { - ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET); + ret = db8500_prcmu_set_epod(epod_id, EPOD_STATE_RAMRET); if (ret < 0) return ret; } else { - ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF); + ret = db8500_prcmu_set_epod(epod_id, EPOD_STATE_OFF); if (ret < 0) return ret; } diff --git a/drivers/regulator/mt6316-regulator.c b/drivers/regulator/mt6316-regulator.c index 952852bbe923..b170506eec57 100644 --- a/drivers/regulator/mt6316-regulator.c +++ b/drivers/regulator/mt6316-regulator.c @@ -329,6 +329,7 @@ static const struct of_device_id mt6316_regulator_match[] = { { .compatible = "mediatek,mt6316d-regulator", .data = (void *)MT6316_TYPE_4PHASE }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mt6316_regulator_match); static struct spmi_driver mt6316_regulator_driver = { .driver = { diff --git a/drivers/regulator/mt6363-regulator.c b/drivers/regulator/mt6363-regulator.c index 0aebcbda0a19..aa6a8eb7ac4b 100644 --- a/drivers/regulator/mt6363-regulator.c +++ b/drivers/regulator/mt6363-regulator.c @@ -927,6 +927,7 @@ static const struct of_device_id mt6363_regulator_match[] = { { .compatible = "mediatek,mt6363-regulator" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mt6363_regulator_match); static struct platform_driver mt6363_regulator_driver = { .driver = { diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index c521c744e7db..65befdbfa5f7 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -210,6 +210,7 @@ config QCOM_Q6V5_MSS select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM + select QCOM_PAS help Say y here to support the Qualcomm self-authenticating modem subsystem based on Hexagon V5. The TrustZone based system is @@ -230,6 +231,7 @@ config QCOM_Q6V5_PAS select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM + select QCOM_PAS help Say y here to support the TrustZone based Peripheral Image Loader for the Qualcomm remote processors. This is commonly used to control @@ -282,7 +284,7 @@ config QCOM_WCNSS_PIL select QCOM_MDT_LOADER select QCOM_PIL_INFO select QCOM_RPROC_COMMON - select QCOM_SCM + select QCOM_PAS help Say y here to support the Peripheral Image Loader for loading WCNSS firmware and boot the core on e.g. MSM8974, MSM8916. The firmware is diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 85a74c9ec521..436656bdfa8b 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -36,6 +36,7 @@ struct mtk_scp *scp_get(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *scp_node; struct platform_device *scp_pdev; + struct mtk_scp *scp; scp_node = of_parse_phandle(dev->of_node, "mediatek,scp", 0); if (!scp_node) { @@ -51,7 +52,13 @@ struct mtk_scp *scp_get(struct platform_device *pdev) return NULL; } - return platform_get_drvdata(scp_pdev); + scp = platform_get_drvdata(scp_pdev); + if (!scp) { + put_device(&scp_pdev->dev); + return NULL; + } + + return scp; } EXPORT_SYMBOL_GPL(scp_get); diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index ae78f5c7c1b6..96888007faa8 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -34,6 +34,7 @@ #include "qcom_pil_info.h" #include "qcom_q6v5.h" +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/firmware/qcom/qcom_scm.h> #define MPSS_CRASH_REASON_SMEM 421 @@ -1480,7 +1481,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) } if (qproc->need_pas_mem_setup) { - ret = qcom_scm_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size); + ret = qcom_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size); if (ret) { dev_err(qproc->dev, "setting up mpss memory failed: %d\n", ret); @@ -2077,7 +2078,7 @@ static int q6v5_probe(struct platform_device *pdev) if (!desc) return -EINVAL; - if (desc->need_mem_protection && !qcom_scm_is_available()) + if (desc->need_mem_protection && !qcom_pas_is_available()) return -EPROBE_DEFER; mba_image = desc->hexagon_mba_image; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 60a4337d9e51..4862c679cbe5 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -20,6 +20,7 @@ #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/firmware/qcom/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> @@ -116,8 +117,8 @@ struct qcom_pas { struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; - struct qcom_scm_pas_context *pas_ctx; - struct qcom_scm_pas_context *dtb_pas_ctx; + struct qcom_pas_context *pas_ctx; + struct qcom_pas_context *dtb_pas_ctx; }; static void qcom_pas_segment_dump(struct rproc *rproc, @@ -194,7 +195,7 @@ static int qcom_pas_shutdown_poll_decrypt(struct qcom_pas *pas) do { msleep(QCOM_PAS_DECRYPT_SHUTDOWN_DELAY_MS); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_pas_shutdown(pas->pas_id); } while (ret == -EINVAL && --retry_num); return ret; @@ -210,9 +211,9 @@ static int qcom_pas_unprepare(struct rproc *rproc) * auth_and_reset() was successful, but in other cases clean it up * here. */ - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); return 0; } @@ -226,9 +227,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) pas->firmware = fw; if (pas->lite_pas_id) - qcom_scm_pas_shutdown(pas->lite_pas_id); + qcom_pas_shutdown(pas->lite_pas_id); if (pas->lite_dtb_pas_id) - qcom_scm_pas_shutdown(pas->lite_dtb_pas_id); + qcom_pas_shutdown(pas->lite_dtb_pas_id); if (pas->dtb_pas_id) { ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name, pas->dev); @@ -248,7 +249,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) return 0; release_dtb_metadata: - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); release_firmware(pas->dtb_firmware); return ret; @@ -308,7 +309,7 @@ static int qcom_pas_start(struct rproc *rproc) if (ret) goto disable_px_supply; - ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx); + ret = qcom_pas_prepare_and_auth_reset(pas->dtb_pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate dtb image and release reset\n"); @@ -327,7 +328,7 @@ static int qcom_pas_start(struct rproc *rproc) if (ret) goto release_pas_metadata; - ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx); + ret = qcom_pas_prepare_and_auth_reset(pas->pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate image and release reset\n"); @@ -337,13 +338,13 @@ static int qcom_pas_start(struct rproc *rproc) ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000)); if (ret == -ETIMEDOUT) { dev_err(pas->dev, "start timed out\n"); - qcom_scm_pas_shutdown(pas->pas_id); + qcom_pas_shutdown(pas->pas_id); goto unmap_carveout; } - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); /* firmware is used to pass reference from qcom_pas_start(), drop it now */ pas->firmware = NULL; @@ -353,9 +354,9 @@ static int qcom_pas_start(struct rproc *rproc) unmap_carveout: qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size); release_pas_metadata: - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); unmap_dtb_carveout: if (pas->dtb_pas_id) @@ -404,7 +405,7 @@ static int qcom_pas_stop(struct rproc *rproc) if (ret == -ETIMEDOUT) dev_err(pas->dev, "timed out on wait\n"); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_pas_shutdown(pas->pas_id); if (ret && pas->decrypt_shutdown) ret = qcom_pas_shutdown_poll_decrypt(pas); @@ -412,7 +413,7 @@ static int qcom_pas_stop(struct rproc *rproc) dev_err(pas->dev, "failed to shutdown: %d\n", ret); if (pas->dtb_pas_id) { - ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); + ret = qcom_pas_shutdown(pas->dtb_pas_id); if (ret) dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); @@ -482,11 +483,11 @@ static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *f * * Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources * or not and if it is not having then we pass NULL and zero as input resource - * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table() + * table pointer and size respectively to the argument of qcom_pas_get_rsc_table() * and this is even true for Qualcomm remote processor who does follow remoteproc * framework. */ - output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size); + output_rt = qcom_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size); ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0; if (ret) { dev_err(pas->dev, "Error in getting resource table: %d\n", ret); @@ -807,7 +808,7 @@ static int qcom_pas_probe(struct platform_device *pdev) if (!desc) return -EINVAL; - if (!qcom_scm_is_available()) + if (!qcom_pas_is_available()) return -EPROBE_DEFER; fw_name = desc->firmware_name; @@ -899,16 +900,16 @@ static int qcom_pas_probe(struct platform_device *pdev) qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name); - pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id, - pas->mem_phys, pas->mem_size); + pas->pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->pas_id, + pas->mem_phys, pas->mem_size); if (IS_ERR(pas->pas_ctx)) { ret = PTR_ERR(pas->pas_ctx); goto remove_ssr_sysmon; } - pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id, - pas->dtb_mem_phys, - pas->dtb_mem_size); + pas->dtb_pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->dtb_pas_id, + pas->dtb_mem_phys, + pas->dtb_mem_size); if (IS_ERR(pas->dtb_pas_ctx)) { ret = PTR_ERR(pas->dtb_pas_ctx); goto remove_ssr_sysmon; diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 4add9037dbd5..0dbdd18ab3dd 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -19,7 +19,7 @@ #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> -#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> #include <linux/soc/qcom/mdt_loader.h> @@ -257,7 +257,7 @@ static int wcnss_start(struct rproc *rproc) wcnss_indicate_nv_download(wcnss); wcnss_configure_iris(wcnss); - ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID); + ret = qcom_pas_auth_and_reset(WCNSS_PAS_ID); if (ret) { dev_err(wcnss->dev, "failed to authenticate image and release reset\n"); @@ -269,7 +269,7 @@ static int wcnss_start(struct rproc *rproc) if (wcnss->ready_irq > 0 && ret == 0) { /* We have a ready_irq, but it didn't fire in time. */ dev_err(wcnss->dev, "start timed out\n"); - qcom_scm_pas_shutdown(WCNSS_PAS_ID); + qcom_pas_shutdown(WCNSS_PAS_ID); ret = -ETIMEDOUT; goto disable_iris; } @@ -311,7 +311,7 @@ static int wcnss_stop(struct rproc *rproc) 0); } - ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID); + ret = qcom_pas_shutdown(WCNSS_PAS_ID); if (ret) dev_err(wcnss->dev, "failed to shutdown: %d\n", ret); @@ -557,10 +557,10 @@ static int wcnss_probe(struct platform_device *pdev) data = of_device_get_match_data(&pdev->dev); - if (!qcom_scm_is_available()) + if (!qcom_pas_is_available()) return -EPROBE_DEFER; - if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) { + if (!qcom_pas_supported(WCNSS_PAS_ID)) { dev_err(&pdev->dev, "PAS is not available for WCNSS\n"); return -ENXIO; } diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 04f23295ffc1..b1d04d082e44 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -1074,11 +1074,9 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev) } kproc->reset = devm_reset_control_get_exclusive(cdev, NULL); - if (IS_ERR_OR_NULL(kproc->reset)) { - ret = PTR_ERR_OR_ZERO(kproc->reset); - if (!ret) - ret = -ENODEV; - dev_err_probe(cdev, ret, "failed to get reset handle\n"); + if (IS_ERR(kproc->reset)) { + ret = dev_err_probe(cdev, PTR_ERR(kproc->reset), + "failed to get reset handle\n"); goto out; } diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ /dev/null diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/drivers/s390/crypto/zcrypt_cex2a.h +++ /dev/null diff --git a/drivers/s390/crypto/zcrypt_cex2c.c b/drivers/s390/crypto/zcrypt_cex2c.c deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/drivers/s390/crypto/zcrypt_cex2c.c +++ /dev/null diff --git a/drivers/s390/crypto/zcrypt_cex2c.h b/drivers/s390/crypto/zcrypt_cex2c.h deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/drivers/s390/crypto/zcrypt_cex2c.h +++ /dev/null diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 8948b5fd42d2..22c8099cf6bb 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -2305,6 +2305,20 @@ static const struct llcc_slice_config sdm845_data[] = {{ }, }; +static const struct llcc_slice_config shikra_data[] = { + { + .usecase_id = LLCC_ECC, + .slice_id = 26, + .max_cap = 256, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0x3, + .cache_mode = 0, + .activate_on_init = true, + .vict_prio = true, + }, +}; + static const struct llcc_slice_config sm6350_data[] = { { .usecase_id = LLCC_CPUSS, @@ -4575,6 +4589,15 @@ static const struct qcom_llcc_config sdm845_cfg[] = { }, }; +static const struct qcom_llcc_config shikra_cfg[] = { + { + .sct_data = shikra_data, + .size = ARRAY_SIZE(shikra_data), + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, +}; + static const struct qcom_llcc_config sm6350_cfg[] = { { .sct_data = sm6350_data, @@ -4752,6 +4775,11 @@ static const struct qcom_sct_config sdm845_cfgs = { .num_config = ARRAY_SIZE(sdm845_cfg), }; +static const struct qcom_sct_config shikra_cfgs = { + .llcc_config = shikra_cfg, + .num_config = ARRAY_SIZE(shikra_cfg), +}; + static const struct qcom_sct_config sm6350_cfgs = { .llcc_config = sm6350_cfg, .num_config = ARRAY_SIZE(sm6350_cfg), @@ -5632,6 +5660,7 @@ static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfgs }, { .compatible = "qcom,sdm670-llcc", .data = &sdm670_cfgs }, { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfgs }, + { .compatible = "qcom,shikra-llcc", .data = &shikra_cfgs }, { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfgs }, { .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfgs }, { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfgs }, diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index c004d444d698..137992456b71 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -13,7 +13,7 @@ #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/soc/qcom/mdt_loader.h> @@ -229,7 +229,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *ctx) + struct qcom_pas_context *ctx) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -271,7 +271,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, goto out; } - ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len, ctx); + ret = qcom_pas_init_image(pas_id, metadata, metadata_len, ctx); kfree(metadata); if (ret) { /* Invalid firmware metadata */ @@ -280,7 +280,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, } if (relocate) { - ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); + ret = qcom_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); if (ret) { /* Unable to set up relocation */ dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); @@ -472,7 +472,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load); * firmware segments (e.g., .bXX files). Authentication of the segments done * by a separate call. * - * The PAS context must be initialized using qcom_scm_pas_context_init() + * The PAS context must be initialized using devm_qcom_pas_context_alloc() * prior to invoking this function. * * @ctx: Pointer to the PAS (Peripheral Authentication Service) context @@ -483,7 +483,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load); * * Return: 0 on success or a negative error code on failure. */ -int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, +int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw, const char *firmware, void *mem_region, phys_addr_t *reloc_base) { int ret; diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c index 0dc1a7946050..64ca99d7fad9 100644 --- a/drivers/soc/qcom/qcom_pd_mapper.c +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -310,6 +310,24 @@ static const struct qcom_pdm_domain_data cdsp_root_pd = { .services = { NULL }, }; +static const struct qcom_pdm_domain_data cdsp1_root_pd = { + .domain = "msm/cdsp1/root_pd", + .instance_id = 125, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data gpdsp_root_pd = { + .domain = "msm/gpdsp/root_pd", + .instance_id = 192, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data gpdsp1_root_pd = { + .domain = "msm/gpdsp1/root_pd", + .instance_id = 241, + .services = { NULL }, +}; + static const struct qcom_pdm_domain_data slpi_root_pd = { .domain = "msm/slpi/root_pd", .instance_id = 90, @@ -425,6 +443,24 @@ static const struct qcom_pdm_domain_data *qcs615_domains[] = { NULL, }; +static const struct qcom_pdm_domain_data *qcs8300_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &gpdsp_root_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sa8775p_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &cdsp1_root_pd, + &gpdsp_root_pd, + &gpdsp1_root_pd, + NULL, +}; + static const struct qcom_pdm_domain_data *sc7180_domains[] = { &adsp_audio_pd, &adsp_root_pd_pdr, @@ -602,6 +638,8 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { { .compatible = "qcom,qcm6490", .data = sc7280_domains, }, { .compatible = "qcom,qcs404", .data = qcs404_domains, }, { .compatible = "qcom,qcs615", .data = qcs615_domains, }, + { .compatible = "qcom,qcs8300", .data = qcs8300_domains, }, + { .compatible = "qcom,sa8775p", .data = sa8775p_domains, }, { .compatible = "qcom,sc7180", .data = sc7180_domains, }, { .compatible = "qcom,sc7280", .data = sc7280_domains, }, { .compatible = "qcom,sc8180x", .data = sc8180x_domains, }, diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 2e380faf9080..e7122444f12a 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -54,6 +54,8 @@ static const struct subsystem_data subsystems[] = { { "cdsp1", 607, 12 }, { "gpdsp0", 607, 17 }, { "gpdsp1", 607, 18 }, + { "soccp", 607, 19 }, + { "dcp", 607, 22 }, { "slpi", 608, 3 }, { "gpu", 609, 0 }, { "display", 610, 0 }, diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index a3cb6d61bb65..0209a02c390f 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -145,6 +145,7 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { .compatible = "qcom,sdm660", .data = &ubwc_1_0_hbb14 }, { .compatible = "qcom,sdm670", .data = &ubwc_2_0_hbb14, }, { .compatible = "qcom,sdm845", .data = &ubwc_2_0_hbb15, }, + { .compatible = "qcom,shikra", .data = &ubwc_0_0_hbb15, }, { .compatible = "qcom,sm4250", .data = &ubwc_1_0_hbb14, }, { .compatible = "qcom,sm6115", .data = &ubwc_1_0_hbb14, }, { .compatible = "qcom,sm6125", .data = &ubwc_1_0_hbb14, }, diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 88ac0833351c..2914d781dbf5 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -622,25 +622,30 @@ static int spi_geni_init(struct spi_geni_master *mas) u32 spi_tx_cfg, fifo_disable; int ret = -ENXIO; - pm_runtime_get_sync(mas->dev); + PM_RUNTIME_ACQUIRE_IF_ENABLED(mas->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret < 0) { + dev_err(mas->dev, "Failed to resume and get %d\n", ret); + return ret; + } proto = geni_se_read_proto(se); if (spi->target) { if (proto != GENI_SE_SPI_SLAVE) { dev_err(mas->dev, "Invalid proto %d\n", proto); - goto out_pm; + return ret; } spi_slv_setup(mas); } else if (proto == GENI_SE_INVALID_PROTO) { ret = geni_load_se_firmware(se, GENI_SE_SPI); if (ret) { dev_err(mas->dev, "spi master firmware load failed ret: %d\n", ret); - goto out_pm; + return ret; } } else if (proto != GENI_SE_SPI) { dev_err(mas->dev, "Invalid proto %d\n", proto); - goto out_pm; + return ret; } mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se); @@ -673,7 +678,7 @@ static int spi_geni_init(struct spi_geni_master *mas) dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n"); break; } else if (ret == -EPROBE_DEFER) { - goto out_pm; + return ret; } /* * in case of failure to get gpi dma channel, we can still do the @@ -702,8 +707,6 @@ static int spi_geni_init(struct spi_geni_master *mas) writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG); } -out_pm: - pm_runtime_put(mas->dev); return ret; } diff --git a/drivers/staging/axis-fifo/axis-fifo.c b/drivers/staging/axis-fifo/axis-fifo.c index 3aa2aa870ea9..1c34de020cf8 100644 --- a/drivers/staging/axis-fifo/axis-fifo.c +++ b/drivers/staging/axis-fifo/axis-fifo.c @@ -246,7 +246,8 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, mutex_lock(&fifo->write_lock); ret = wait_event_interruptible(fifo->write_queue, - ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= words_to_write); + ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= + words_to_write); if (ret) goto end_unlock; } diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index e2f39617e458..a78249812b26 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -252,22 +252,22 @@ error_ret: * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9832_write, AD9832_FREQ0HM); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9832_write, AD9832_FREQ1HM); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9832_write, AD9832_FREQ_SYM); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9832_write, AD9832_FREQ0HM); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9832_write, AD9832_FREQ1HM); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9832_write, AD9832_FREQ_SYM); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9832_write, AD9832_PHASE0H); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9832_write, AD9832_PHASE1H); -static IIO_DEV_ATTR_PHASE(0, 2, 0200, NULL, ad9832_write, AD9832_PHASE2H); -static IIO_DEV_ATTR_PHASE(0, 3, 0200, NULL, ad9832_write, AD9832_PHASE3H); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9832_write, AD9832_PHASE0H); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9832_write, AD9832_PHASE1H); +static IIO_DEV_ATTR_PHASE(0200, 0, 2, NULL, ad9832_write, AD9832_PHASE2H); +static IIO_DEV_ATTR_PHASE(0200, 0, 3, NULL, ad9832_write, AD9832_PHASE3H); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9832_write, AD9832_PHASE_SYM); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9832_write, AD9832_PINCTRL_EN); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9832_write, AD9832_OUTPUT_EN); static struct attribute *ad9832_attributes[] = { diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index b039f3c074cd..0d20f796e0f0 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -314,21 +314,21 @@ static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, 0444, * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9834_write, AD9834_REG_FREQ0); -static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9834_write, AD9834_REG_FREQ1); -static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9834_write, AD9834_FSEL); +static IIO_DEV_ATTR_FREQ(0200, 0, 0, NULL, ad9834_write, AD9834_REG_FREQ0); +static IIO_DEV_ATTR_FREQ(0200, 0, 1, NULL, ad9834_write, AD9834_REG_FREQ1); +static IIO_DEV_ATTR_FREQSYMBOL(0200, 0, NULL, ad9834_write, AD9834_FSEL); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9834_write, AD9834_REG_PHASE0); -static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9834_write, AD9834_REG_PHASE1); -static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, ad9834_write, AD9834_PSEL); +static IIO_DEV_ATTR_PHASE(0200, 0, 0, NULL, ad9834_write, AD9834_REG_PHASE0); +static IIO_DEV_ATTR_PHASE(0200, 0, 1, NULL, ad9834_write, AD9834_REG_PHASE1); +static IIO_DEV_ATTR_PHASESYMBOL(0200, 0, NULL, ad9834_write, AD9834_PSEL); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, ad9834_write, AD9834_PIN_SW); -static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, ad9834_write, AD9834_RESET); -static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, 0200, NULL, ad9834_write, AD9834_OPBITEN); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); -static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); +static IIO_DEV_ATTR_PINCONTROL_EN(0200, 0, NULL, ad9834_write, AD9834_PIN_SW); +static IIO_DEV_ATTR_OUT_ENABLE(0200, 0, NULL, ad9834_write, AD9834_RESET); +static IIO_DEV_ATTR_OUTY_ENABLE(0200, 0, 1, NULL, ad9834_write, AD9834_OPBITEN); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 0, ad9834_store_wavetype, 0); +static IIO_DEV_ATTR_OUT_WAVETYPE(0200, 0, 1, ad9834_store_wavetype, 1); static struct attribute *ad9834_attributes[] = { &iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr, diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h index 2ebe68eb7398..b2ca8bb971e8 100644 --- a/drivers/staging/iio/frequency/dds.h +++ b/drivers/staging/iio/frequency/dds.h @@ -11,7 +11,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY */ -#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQ(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num, \ _mode, _show, _store, _addr) @@ -26,7 +26,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol */ -#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_FREQSYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol, \ _mode, _show, _store, _addr) @@ -34,7 +34,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_phaseY */ -#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASE(_mode, _channel, _num, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num, \ _mode, _show, _store, _addr) @@ -49,7 +49,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol */ -#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_PHASESYMBOL(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol, \ _mode, _show, _store, _addr) @@ -57,7 +57,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en */ -#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en, \ _mode, _show, _store, _addr) @@ -65,7 +65,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en */ -#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\ _mode, _show, _store, _addr) @@ -73,7 +73,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en */ -#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\ +#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_mode, _channel, _show, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en, \ _mode, _show, _store, _addr) @@ -81,7 +81,7 @@ * /sys/bus/iio/devices/.../out_altvoltageX_out_enable */ -#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_ENABLE(_mode, _channel, _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable, \ _mode, _show, _store, _addr) @@ -89,8 +89,8 @@ * /sys/bus/iio/devices/.../out_altvoltageX_outY_enable */ -#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \ - _mode, _show, _store, _addr) \ +#define IIO_DEV_ATTR_OUTY_ENABLE(_mode, _channel, _output, \ + _show, _store, _addr) \ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\ _mode, _show, _store, _addr) @@ -98,9 +98,9 @@ * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype */ -#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \ +#define IIO_DEV_ATTR_OUT_WAVETYPE(_mode, _channel, _output, _store, _addr)\ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\ - 0200, NULL, _store, _addr) + _mode, NULL, _store, _addr) /** * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index 310e3f24e571..48a35bda4237 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2343,7 +2343,7 @@ static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys, return ret; } - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { dev_err(dev, "Failed to get runtime PM\n"); return ret; @@ -2702,7 +2702,7 @@ static int ipu7_resume(struct device *dev) if (ret) dev_err(dev, "IPC reset protocol failed!\n"); - ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); + ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev); if (ret < 0) { dev_err(dev, "Failed to get runtime PM\n"); return 0; diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index e1122d3f325d..1d429f16e01e 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -1165,7 +1165,7 @@ u8 rtw_ap_set_pairwise_key(struct adapter *padapter, struct sta_info *psta) goto exit; } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_); + init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, SET_STA_KEY_CMD); psetstakey_para->algorithm = (u8)psta->dot118021XPrivacy; @@ -1228,7 +1228,7 @@ static int rtw_ap_set_key(struct adapter *padapter, memcpy(&psetkeyparm->key[0], key, keylen); - pcmd->cmdcode = _SetKey_CMD_; + pcmd->cmdcode = SET_KEY_CMD; pcmd->parmbuf = (u8 *)psetkeyparm; pcmd->cmdsz = (sizeof(struct setkey_parm)); pcmd->rsp = NULL; diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 818226be783a..623ce064d395 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -11,78 +11,78 @@ #include <linux/delay.h> static struct _cmd_callback rtw_cmd_callback[] = { - {GEN_CMD_CODE(_Read_MACREG), NULL}, /*0*/ - {GEN_CMD_CODE(_Write_MACREG), NULL}, - {GEN_CMD_CODE(_Read_BBREG), &rtw_getbbrfreg_cmdrsp_callback}, - {GEN_CMD_CODE(_Write_BBREG), NULL}, - {GEN_CMD_CODE(_Read_RFREG), &rtw_getbbrfreg_cmdrsp_callback}, - {GEN_CMD_CODE(_Write_RFREG), NULL}, /*5*/ - {GEN_CMD_CODE(_Read_EEPROM), NULL}, - {GEN_CMD_CODE(_Write_EEPROM), NULL}, - {GEN_CMD_CODE(_Read_EFUSE), NULL}, - {GEN_CMD_CODE(_Write_EFUSE), NULL}, - - {GEN_CMD_CODE(_Read_CAM), NULL}, /*10*/ - {GEN_CMD_CODE(_Write_CAM), NULL}, - {GEN_CMD_CODE(_setBCNITV), NULL}, - {GEN_CMD_CODE(_setMBIDCFG), NULL}, - {GEN_CMD_CODE(_JoinBss), &rtw_joinbss_cmd_callback}, /*14*/ - {GEN_CMD_CODE(_DisConnect), &rtw_disassoc_cmd_callback}, /*15*/ - {GEN_CMD_CODE(_CreateBss), &rtw_createbss_cmd_callback}, - {GEN_CMD_CODE(_SetOpMode), NULL}, - {GEN_CMD_CODE(_SiteSurvey), &rtw_survey_cmd_callback}, /*18*/ - {GEN_CMD_CODE(_SetAuth), NULL}, - - {GEN_CMD_CODE(_SetKey), NULL}, /*20*/ - {GEN_CMD_CODE(_SetStaKey), &rtw_setstaKey_cmdrsp_callback}, - {GEN_CMD_CODE(_SetAssocSta), &rtw_setassocsta_cmdrsp_callback}, - {GEN_CMD_CODE(_DelAssocSta), NULL}, - {GEN_CMD_CODE(_SetStaPwrState), NULL}, - {GEN_CMD_CODE(_SetBasicRate), NULL}, /*25*/ - {GEN_CMD_CODE(_GetBasicRate), NULL}, - {GEN_CMD_CODE(_SetDataRate), NULL}, - {GEN_CMD_CODE(_GetDataRate), NULL}, - {GEN_CMD_CODE(_SetPhyInfo), NULL}, - - {GEN_CMD_CODE(_GetPhyInfo), NULL}, /*30*/ - {GEN_CMD_CODE(_SetPhy), NULL}, - {GEN_CMD_CODE(_GetPhy), NULL}, - {GEN_CMD_CODE(_readRssi), NULL}, - {GEN_CMD_CODE(_readGain), NULL}, - {GEN_CMD_CODE(_SetAtim), NULL}, /*35*/ - {GEN_CMD_CODE(_SetPwrMode), NULL}, - {GEN_CMD_CODE(_JoinbssRpt), NULL}, - {GEN_CMD_CODE(_SetRaTable), NULL}, - {GEN_CMD_CODE(_GetRaTable), NULL}, - - {GEN_CMD_CODE(_GetCCXReport), NULL}, /*40*/ - {GEN_CMD_CODE(_GetDTMReport), NULL}, - {GEN_CMD_CODE(_GetTXRateStatistics), NULL}, - {GEN_CMD_CODE(_SetUsbSuspend), NULL}, - {GEN_CMD_CODE(_SetH2cLbk), NULL}, - {GEN_CMD_CODE(_AddBAReq), NULL}, /*45*/ - {GEN_CMD_CODE(_SetChannel), NULL}, /*46*/ - {GEN_CMD_CODE(_SetTxPower), NULL}, - {GEN_CMD_CODE(_SwitchAntenna), NULL}, - {GEN_CMD_CODE(_SetCrystalCap), NULL}, - {GEN_CMD_CODE(_SetSingleCarrierTx), NULL}, /*50*/ - - {GEN_CMD_CODE(_SetSingleToneTx), NULL}, /*51*/ - {GEN_CMD_CODE(_SetCarrierSuppressionTx), NULL}, - {GEN_CMD_CODE(_SetContinuousTx), NULL}, - {GEN_CMD_CODE(_SwitchBandwidth), NULL}, /*54*/ - {GEN_CMD_CODE(_TX_Beacon), NULL},/*55*/ - - {GEN_CMD_CODE(_Set_MLME_EVT), NULL},/*56*/ - {GEN_CMD_CODE(_Set_Drv_Extra), NULL},/*57*/ - {GEN_CMD_CODE(_Set_H2C_MSG), NULL},/*58*/ - {GEN_CMD_CODE(_SetChannelPlan), NULL},/*59*/ - - {GEN_CMD_CODE(_SetChannelSwitch), NULL},/*60*/ - {GEN_CMD_CODE(_TDLS), NULL},/*61*/ - {GEN_CMD_CODE(_ChkBMCSleepq), NULL}, /*62*/ - - {GEN_CMD_CODE(_RunInThreadCMD), NULL},/*63*/ + {READ_MACREG_CMD, NULL}, /*0*/ + {WRITE_MACREG_CMD, NULL}, + {READ_BBREG_CMD, &rtw_getbbrfreg_cmdrsp_callback}, + {WRITE_BBREG_CMD, NULL}, + {READ_RFREG_CMD, &rtw_getbbrfreg_cmdrsp_callback}, + {WRITE_RFREG_CMD, NULL}, /*5*/ + {READ_EEPROM_CMD, NULL}, + {WRITE_EEPROM_CMD, NULL}, + {READ_EFUSE_CMD, NULL}, + {WRITE_EFUSE_CMD, NULL}, + + {READ_CAM_CMD, NULL}, /*10*/ + {WRITE_CAM_CMD, NULL}, + {SET_BCNITV_CMD, NULL}, + {SET_MBIDCFG_CMD, NULL}, + {JOIN_BSS_CMD, &rtw_joinbss_cmd_callback}, /*14*/ + {DISCONNECT_CMD, &rtw_disassoc_cmd_callback}, /*15*/ + {CREATE_BSS_CMD, &rtw_createbss_cmd_callback}, + {SET_OP_MODE_CMD, NULL}, + {SITE_SURVEY_CMD, &rtw_survey_cmd_callback}, /*18*/ + {SET_AUTH_CMD, NULL}, + + {SET_KEY_CMD, NULL}, /*20*/ + {SET_STA_KEY_CMD, &rtw_setstaKey_cmdrsp_callback}, + {SET_ASSOC_STA_CMD, &rtw_setassocsta_cmdrsp_callback}, + {DEL_ASSOC_STA_CMD, NULL}, + {SET_STA_PWR_STATE_CMD, NULL}, + {SET_BASIC_RATE_CMD, NULL}, /*25*/ + {GET_BASIC_RATE_CMD, NULL}, + {SET_DATA_RATE_CMD, NULL}, + {GET_DATA_RATE_CMD, NULL}, + {SET_PHY_INFO_CMD, NULL}, + + {GET_PHY_INFO_CMD, NULL}, /*30*/ + {SET_PHY_CMD, NULL}, + {GET_PHY_CMD, NULL}, + {READ_RSSI_CMD, NULL}, + {READ_GAIN_CMD, NULL}, + {SET_ATIM_CMD, NULL}, /*35*/ + {SET_PWR_MODE_CMD, NULL}, + {JOIN_BSS_RPT_CMD, NULL}, + {SET_RA_TABLE_CMD, NULL}, + {GET_RA_TABLE_CMD, NULL}, + + {GET_CCX_REPORT_CMD, NULL}, /*40*/ + {GET_DTM_REPORT_CMD, NULL}, + {GET_TX_RATE_STATISTICS_CMD, NULL}, + {SET_USB_SUSPEND_CMD, NULL}, + {SET_H2C_LBK_CMD, NULL}, + {ADD_BA_REQ_CMD, NULL}, /*45*/ + {SET_CHANNEL_CMD, NULL}, /*46*/ + {SET_TX_POWER_CMD, NULL}, + {SWITCH_ANTENNA_CMD, NULL}, + {SET_CRYSTAL_CAP_CMD, NULL}, + {SET_SINGLE_CARRIER_TX_CMD, NULL}, /*50*/ + + {SET_SINGLE_TONE_TX_CMD, NULL}, /*51*/ + {SET_CARRIER_SUPPRESSION_TX_CMD, NULL}, + {SET_CONTINUOUS_TX_CMD, NULL}, + {SWITCH_BANDWIDTH_CMD, NULL}, /*54*/ + {TX_BEACON_CMD, NULL},/*55*/ + + {SET_MLME_EVT_CMD, NULL},/*56*/ + {SET_DRV_EXTRA_CMD, NULL},/*57*/ + {SET_H2C_MSG_CMD, NULL},/*58*/ + {SET_CHANNEL_PLAN_CMD, NULL},/*59*/ + + {SET_CHANNEL_SWITCH_CMD, NULL},/*60*/ + {TDLS_CMD, NULL},/*61*/ + {CHK_BMC_SLEEPQ_CMD, NULL}, /*62*/ + + {RUN_IN_THREAD_CMD, NULL},/*63*/ }; static struct cmd_hdl wlancmds[] = { @@ -296,12 +296,12 @@ void rtw_free_cmd_priv(struct cmd_priv *pcmdpriv) int rtw_cmd_filter(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj); int rtw_cmd_filter(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj) { - u8 bAllow = false; /* set to true to allow enqueuing cmd when hw_init_completed is false */ + bool allow = false; /* set to true to allow enqueuing cmd when hw_init_completed is false */ - if (cmd_obj->cmdcode == GEN_CMD_CODE(_SetChannelPlan)) - bAllow = true; + if (cmd_obj->cmdcode == SET_CHANNEL_PLAN_CMD) + allow = true; - if ((!pcmdpriv->padapter->hw_init_completed && !bAllow) || + if ((!pcmdpriv->padapter->hw_init_completed && !allow) || !atomic_read(&pcmdpriv->cmdthd_running)) /* com_thread not running */ return _FAIL; @@ -341,8 +341,8 @@ struct cmd_obj *rtw_dequeue_cmd(struct cmd_priv *pcmdpriv) void rtw_free_cmd_obj(struct cmd_obj *pcmd) { - if ((pcmd->cmdcode != _JoinBss_CMD_) && - (pcmd->cmdcode != _CreateBss_CMD_)) { + if ((pcmd->cmdcode != JOIN_BSS_CMD) && + (pcmd->cmdcode != CREATE_BSS_CMD)) { /* free parmbuf in cmd_obj */ kfree(pcmd->parmbuf); } @@ -499,7 +499,7 @@ post_process: break; } - if (pcmd->cmdcode == GEN_CMD_CODE(_Set_Drv_Extra)) { + if (pcmd->cmdcode == SET_DRV_EXTRA_CMD) { extra_parm = (struct drvextra_cmd_parm *)pcmd->parmbuf; if (extra_parm->pbuf && extra_parm->size > 0) kfree(extra_parm->pbuf); @@ -544,7 +544,7 @@ u8 rtw_sitesurvey_cmd(struct adapter *padapter, struct ndis_802_11_ssid *ssid, rtw_free_network_queue(padapter, false); - init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, GEN_CMD_CODE(_SiteSurvey)); + init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, SITE_SURVEY_CMD); /* psurveyPara->bsslimit = 48; */ psurveyPara->scan_mode = pmlmepriv->scan_mode; @@ -607,7 +607,7 @@ u8 rtw_createbss_cmd(struct adapter *padapter) } INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = _CreateBss_CMD_; + pcmd->cmdcode = CREATE_BSS_CMD; pcmd->parmbuf = (unsigned char *)pdev_network; pcmd->cmdsz = get_wlan_bssid_ex_sz((struct wlan_bssid_ex *)pdev_network); pcmd->rsp = NULL; @@ -640,7 +640,7 @@ int rtw_startbss_cmd(struct adapter *padapter, int flags) } INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = GEN_CMD_CODE(_CreateBss); + pcmd->cmdcode = CREATE_BSS_CMD; pcmd->parmbuf = NULL; pcmd->cmdsz = 0; pcmd->rsp = NULL; @@ -780,7 +780,7 @@ u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork) pcmd->cmdsz = get_wlan_bssid_ex_sz(psecnetwork);/* get cmdsz before endian conversion */ INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = _JoinBss_CMD_;/* GEN_CMD_CODE(_JoinBss) */ + pcmd->cmdcode = JOIN_BSS_CMD; pcmd->parmbuf = (unsigned char *)psecnetwork; pcmd->rsp = NULL; pcmd->rspsz = 0; @@ -814,7 +814,7 @@ u8 rtw_disassoc_cmd(struct adapter *padapter, u32 deauth_timeout_ms, bool enqueu kfree(param); goto exit; } - init_h2fwcmd_w_parm_no_rsp(cmdobj, param, _DisConnect_CMD_); + init_h2fwcmd_w_parm_no_rsp(cmdobj, param, DISCONNECT_CMD); res = rtw_enqueue_cmd(cmdpriv, cmdobj); } else { /* no need to enqueue, do the cmd hdl directly and free cmd parameter */ @@ -850,7 +850,7 @@ u8 rtw_setopmode_cmd(struct adapter *padapter, enum nl80211_iftype networktype, goto exit; } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, _SetOpMode_CMD_); + init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, SET_OP_MODE_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); } else { setopmode_hdl(padapter, (u8 *)psetop); @@ -907,7 +907,7 @@ u8 rtw_setstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 unicast_ goto exit; } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_); + init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, SET_STA_KEY_CMD); ph2c->rsp = (u8 *)psetstakey_rsp; ph2c->rspsz = sizeof(struct set_stakey_rsp); res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -958,7 +958,7 @@ u8 rtw_clearstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 enqueu goto exit; } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_); + init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, SET_STA_KEY_CMD); ph2c->rsp = (u8 *)psetstakey_rsp; ph2c->rspsz = sizeof(struct set_stakey_rsp); @@ -996,7 +996,7 @@ u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr) paddbareq_parm->tid = tid; memcpy(paddbareq_parm->addr, addr, ETH_ALEN); - init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, GEN_CMD_CODE(_AddBAReq)); + init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, ADD_BA_REQ_CMD); /* rtw_enqueue_cmd(pcmdpriv, ph2c); */ res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1030,7 +1030,7 @@ u8 rtw_reset_securitypriv_cmd(struct adapter *padapter) pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); /* rtw_enqueue_cmd(pcmdpriv, ph2c); */ res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1063,7 +1063,7 @@ u8 rtw_free_assoc_resources_cmd(struct adapter *padapter) pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); /* rtw_enqueue_cmd(pcmdpriv, ph2c); */ res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1096,7 +1096,7 @@ u8 rtw_dynamic_chk_wk_cmd(struct adapter *padapter) pdrvextra_cmd_parm->type = 0; pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); /* rtw_enqueue_cmd(pcmdpriv, ph2c); */ res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1345,7 +1345,7 @@ u8 rtw_lps_ctrl_wk_cmd(struct adapter *padapter, u8 lps_ctrl_type, u8 enqueue) pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); } else { @@ -1386,7 +1386,7 @@ u8 rtw_dm_in_lps_wk_cmd(struct adapter *padapter) pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1448,7 +1448,7 @@ u8 rtw_dm_ra_mask_wk_cmd(struct adapter *padapter, u8 *psta) pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = psta; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1481,7 +1481,7 @@ u8 rtw_ps_cmd(struct adapter *padapter) pdrvextra_cmd_parm->type = 0; pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); res = rtw_enqueue_cmd(pcmdpriv, ppscmd); @@ -1552,7 +1552,7 @@ u8 rtw_chk_hi_queue_cmd(struct adapter *padapter) pdrvextra_cmd_parm->size = 0; pdrvextra_cmd_parm->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1644,7 +1644,7 @@ u8 rtw_c2h_packet_wk_cmd(struct adapter *padapter, u8 *pbuf, u16 length) pdrvextra_cmd_parm->size = length; pdrvextra_cmd_parm->pbuf = pbuf; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -1679,7 +1679,7 @@ u8 rtw_c2h_wk_cmd(struct adapter *padapter, u8 *c2h_evt) pdrvextra_cmd_parm->size = c2h_evt ? 16 : 0; pdrvextra_cmd_parm->pbuf = c2h_evt; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); + init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 776ee3a90cca..849280edaacc 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1859,7 +1859,7 @@ signed int rtw_set_auth(struct adapter *adapter, struct security_priv *psecurity psetauthparm->mode = (unsigned char)psecuritypriv->dot11_auth_algrthm; - pcmd->cmdcode = _SetAuth_CMD_; + pcmd->cmdcode = SET_AUTH_CMD; pcmd->parmbuf = (unsigned char *)psetauthparm; pcmd->cmdsz = (sizeof(struct setauth_parm)); pcmd->rsp = NULL; @@ -1930,7 +1930,7 @@ signed int rtw_set_key(struct adapter *adapter, struct security_priv *psecurityp goto exit; } - pcmd->cmdcode = _SetKey_CMD_; + pcmd->cmdcode = SET_KEY_CMD; pcmd->parmbuf = (u8 *)psetkeyparm; pcmd->cmdsz = (sizeof(struct setkey_parm)); pcmd->rsp = NULL; diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 76fc252177da..f3497b5871eb 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -4298,7 +4298,7 @@ void report_survey_event(struct adapter *padapter, union recv_frame *precv_frame INIT_LIST_HEAD(&pcmd_obj->list); - pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT); + pcmd_obj->cmdcode = SET_MLME_EVT_CMD; pcmd_obj->cmdsz = cmdsz; pcmd_obj->parmbuf = pevtcmd; @@ -4307,7 +4307,7 @@ void report_survey_event(struct adapter *padapter, union recv_frame *precv_frame pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd); pc2h_evt_hdr->len = sizeof(struct survey_event); - pc2h_evt_hdr->ID = GEN_EVT_CODE(_Survey); + pc2h_evt_hdr->ID = SURVEY_EVENT; pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq); psurvey_evt = (struct survey_event *)(pevtcmd + sizeof(struct C2HEvent_Header)); @@ -4348,7 +4348,7 @@ void report_surveydone_event(struct adapter *padapter) INIT_LIST_HEAD(&pcmd_obj->list); - pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT); + pcmd_obj->cmdcode = SET_MLME_EVT_CMD; pcmd_obj->cmdsz = cmdsz; pcmd_obj->parmbuf = pevtcmd; @@ -4357,7 +4357,7 @@ void report_surveydone_event(struct adapter *padapter) pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd); pc2h_evt_hdr->len = sizeof(struct surveydone_event); - pc2h_evt_hdr->ID = GEN_EVT_CODE(_SurveyDone); + pc2h_evt_hdr->ID = SURVEY_DONE_EVENT; pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq); psurveydone_evt = (struct surveydone_event *)(pevtcmd + sizeof(struct C2HEvent_Header)); @@ -4390,7 +4390,7 @@ void report_join_res(struct adapter *padapter, int res) INIT_LIST_HEAD(&pcmd_obj->list); - pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT); + pcmd_obj->cmdcode = SET_MLME_EVT_CMD; pcmd_obj->cmdsz = cmdsz; pcmd_obj->parmbuf = pevtcmd; @@ -4399,7 +4399,7 @@ void report_join_res(struct adapter *padapter, int res) pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd); pc2h_evt_hdr->len = sizeof(struct joinbss_event); - pc2h_evt_hdr->ID = GEN_EVT_CODE(_JoinBss); + pc2h_evt_hdr->ID = JOIN_BSS_EVENT; pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq); pjoinbss_evt = (struct joinbss_event *)(pevtcmd + sizeof(struct C2HEvent_Header)); @@ -4434,7 +4434,7 @@ void report_wmm_edca_update(struct adapter *padapter) INIT_LIST_HEAD(&pcmd_obj->list); - pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT); + pcmd_obj->cmdcode = SET_MLME_EVT_CMD; pcmd_obj->cmdsz = cmdsz; pcmd_obj->parmbuf = pevtcmd; @@ -4443,7 +4443,7 @@ void report_wmm_edca_update(struct adapter *padapter) pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd); pc2h_evt_hdr->len = sizeof(struct wmm_event); - pc2h_evt_hdr->ID = GEN_EVT_CODE(_WMM); + pc2h_evt_hdr->ID = WMM_EVENT; pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq); pwmm_event = (struct wmm_event *)(pevtcmd + sizeof(struct C2HEvent_Header)); @@ -4477,7 +4477,7 @@ void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsi INIT_LIST_HEAD(&pcmd_obj->list); - pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT); + pcmd_obj->cmdcode = SET_MLME_EVT_CMD; pcmd_obj->cmdsz = cmdsz; pcmd_obj->parmbuf = pevtcmd; @@ -4486,7 +4486,7 @@ void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsi pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd); pc2h_evt_hdr->len = sizeof(struct stadel_event); - pc2h_evt_hdr->ID = GEN_EVT_CODE(_DelSTA); + pc2h_evt_hdr->ID = DEL_STA_EVENT; pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq); pdel_sta_evt = (struct stadel_event *)(pevtcmd + sizeof(struct C2HEvent_Header)); @@ -4527,7 +4527,7 @@ void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr, int INIT_LIST_HEAD(&pcmd_obj->list); - pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT); + pcmd_obj->cmdcode = SET_MLME_EVT_CMD; pcmd_obj->cmdsz = cmdsz; pcmd_obj->parmbuf = pevtcmd; @@ -4536,7 +4536,7 @@ void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr, int pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd); pc2h_evt_hdr->len = sizeof(struct stassoc_event); - pc2h_evt_hdr->ID = GEN_EVT_CODE(_AddSTA); + pc2h_evt_hdr->ID = ADD_STA_EVENT; pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq); padd_sta_evt = (struct stassoc_event *)(pevtcmd + sizeof(struct C2HEvent_Header)); @@ -4971,7 +4971,7 @@ void survey_timer_hdl(struct timer_list *t) return; } - init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, GEN_CMD_CODE(_SiteSurvey)); + init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, SITE_SURVEY_CMD); rtw_enqueue_cmd(pcmdpriv, ph2c); } } @@ -5578,7 +5578,7 @@ u8 chk_bmc_sleepq_cmd(struct adapter *padapter) goto exit; } - init_h2fwcmd_w_parm_no_parm_rsp(ph2c, GEN_CMD_CODE(_ChkBMCSleepq)); + init_h2fwcmd_w_parm_no_parm_rsp(ph2c, CHK_BMC_SLEEPQ_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); @@ -5616,7 +5616,7 @@ u8 set_tx_beacon_cmd(struct adapter *padapter) pmlmeinfo->hidden_ssid_mode); ptxBeacon_parm->network.ie_length += len_diff; - init_h2fwcmd_w_parm_no_rsp(ph2c, ptxBeacon_parm, GEN_CMD_CODE(_TX_Beacon)); + init_h2fwcmd_w_parm_no_rsp(ph2c, ptxBeacon_parm, TX_BEACON_CMD); res = rtw_enqueue_cmd(pcmdpriv, ph2c); diff --git a/drivers/staging/rtl8723bs/include/rtw_cmd.h b/drivers/staging/rtl8723bs/include/rtw_cmd.h index 1f822c1784eb..2e791da7e815 100644 --- a/drivers/staging/rtl8723bs/include/rtw_cmd.h +++ b/drivers/staging/rtl8723bs/include/rtw_cmd.h @@ -550,9 +550,6 @@ struct RunInThread_param { }; -#define GEN_CMD_CODE(cmd) cmd ## _CMD_ - - /* Result: @@ -628,86 +625,81 @@ struct _cmd_callback { }; enum { - GEN_CMD_CODE(_Read_MACREG), /*0*/ - GEN_CMD_CODE(_Write_MACREG), - GEN_CMD_CODE(_Read_BBREG), - GEN_CMD_CODE(_Write_BBREG), - GEN_CMD_CODE(_Read_RFREG), - GEN_CMD_CODE(_Write_RFREG), /*5*/ - GEN_CMD_CODE(_Read_EEPROM), - GEN_CMD_CODE(_Write_EEPROM), - GEN_CMD_CODE(_Read_EFUSE), - GEN_CMD_CODE(_Write_EFUSE), - - GEN_CMD_CODE(_Read_CAM), /*10*/ - GEN_CMD_CODE(_Write_CAM), - GEN_CMD_CODE(_setBCNITV), - GEN_CMD_CODE(_setMBIDCFG), - GEN_CMD_CODE(_JoinBss), /*14*/ - GEN_CMD_CODE(_DisConnect), /*15*/ - GEN_CMD_CODE(_CreateBss), - GEN_CMD_CODE(_SetOpMode), - GEN_CMD_CODE(_SiteSurvey), /*18*/ - GEN_CMD_CODE(_SetAuth), - - GEN_CMD_CODE(_SetKey), /*20*/ - GEN_CMD_CODE(_SetStaKey), - GEN_CMD_CODE(_SetAssocSta), - GEN_CMD_CODE(_DelAssocSta), - GEN_CMD_CODE(_SetStaPwrState), - GEN_CMD_CODE(_SetBasicRate), /*25*/ - GEN_CMD_CODE(_GetBasicRate), - GEN_CMD_CODE(_SetDataRate), - GEN_CMD_CODE(_GetDataRate), - GEN_CMD_CODE(_SetPhyInfo), - - GEN_CMD_CODE(_GetPhyInfo), /*30*/ - GEN_CMD_CODE(_SetPhy), - GEN_CMD_CODE(_GetPhy), - GEN_CMD_CODE(_readRssi), - GEN_CMD_CODE(_readGain), - GEN_CMD_CODE(_SetAtim), /*35*/ - GEN_CMD_CODE(_SetPwrMode), - GEN_CMD_CODE(_JoinbssRpt), - GEN_CMD_CODE(_SetRaTable), - GEN_CMD_CODE(_GetRaTable), - - GEN_CMD_CODE(_GetCCXReport), /*40*/ - GEN_CMD_CODE(_GetDTMReport), - GEN_CMD_CODE(_GetTXRateStatistics), - GEN_CMD_CODE(_SetUsbSuspend), - GEN_CMD_CODE(_SetH2cLbk), - GEN_CMD_CODE(_AddBAReq), /*45*/ - GEN_CMD_CODE(_SetChannel), /*46*/ - GEN_CMD_CODE(_SetTxPower), - GEN_CMD_CODE(_SwitchAntenna), - GEN_CMD_CODE(_SetCrystalCap), - GEN_CMD_CODE(_SetSingleCarrierTx), /*50*/ - - GEN_CMD_CODE(_SetSingleToneTx),/*51*/ - GEN_CMD_CODE(_SetCarrierSuppressionTx), - GEN_CMD_CODE(_SetContinuousTx), - GEN_CMD_CODE(_SwitchBandwidth), /*54*/ - GEN_CMD_CODE(_TX_Beacon), /*55*/ - - GEN_CMD_CODE(_Set_MLME_EVT), /*56*/ - GEN_CMD_CODE(_Set_Drv_Extra), /*57*/ - GEN_CMD_CODE(_Set_H2C_MSG), /*58*/ - - GEN_CMD_CODE(_SetChannelPlan), /*59*/ - - GEN_CMD_CODE(_SetChannelSwitch), /*60*/ - GEN_CMD_CODE(_TDLS), /*61*/ - GEN_CMD_CODE(_ChkBMCSleepq), /*62*/ - - GEN_CMD_CODE(_RunInThreadCMD), /*63*/ + READ_MACREG_CMD, /*0*/ + WRITE_MACREG_CMD, + READ_BBREG_CMD, + WRITE_BBREG_CMD, + READ_RFREG_CMD, + WRITE_RFREG_CMD, /*5*/ + READ_EEPROM_CMD, + WRITE_EEPROM_CMD, + READ_EFUSE_CMD, + WRITE_EFUSE_CMD, + + READ_CAM_CMD, /*10*/ + WRITE_CAM_CMD, + SET_BCNITV_CMD, + SET_MBIDCFG_CMD, + JOIN_BSS_CMD, /*14*/ + DISCONNECT_CMD, /*15*/ + CREATE_BSS_CMD, + SET_OP_MODE_CMD, + SITE_SURVEY_CMD, /*18*/ + SET_AUTH_CMD, + + SET_KEY_CMD, /*20*/ + SET_STA_KEY_CMD, + SET_ASSOC_STA_CMD, + DEL_ASSOC_STA_CMD, + SET_STA_PWR_STATE_CMD, + SET_BASIC_RATE_CMD, /*25*/ + GET_BASIC_RATE_CMD, + SET_DATA_RATE_CMD, + GET_DATA_RATE_CMD, + SET_PHY_INFO_CMD, + + GET_PHY_INFO_CMD, /*30*/ + SET_PHY_CMD, + GET_PHY_CMD, + READ_RSSI_CMD, + READ_GAIN_CMD, + SET_ATIM_CMD, /*35*/ + SET_PWR_MODE_CMD, + JOIN_BSS_RPT_CMD, + SET_RA_TABLE_CMD, + GET_RA_TABLE_CMD, + + GET_CCX_REPORT_CMD, /*40*/ + GET_DTM_REPORT_CMD, + GET_TX_RATE_STATISTICS_CMD, + SET_USB_SUSPEND_CMD, + SET_H2C_LBK_CMD, + ADD_BA_REQ_CMD, /*45*/ + SET_CHANNEL_CMD, /*46*/ + SET_TX_POWER_CMD, + SWITCH_ANTENNA_CMD, + SET_CRYSTAL_CAP_CMD, + SET_SINGLE_CARRIER_TX_CMD, /*50*/ + + SET_SINGLE_TONE_TX_CMD, /*51*/ + SET_CARRIER_SUPPRESSION_TX_CMD, + SET_CONTINUOUS_TX_CMD, + SWITCH_BANDWIDTH_CMD, /*54*/ + TX_BEACON_CMD, /*55*/ + + SET_MLME_EVT_CMD, /*56*/ + SET_DRV_EXTRA_CMD, /*57*/ + SET_H2C_MSG_CMD, /*58*/ + + SET_CHANNEL_PLAN_CMD, /*59*/ + + SET_CHANNEL_SWITCH_CMD, /*60*/ + TDLS_CMD, /*61*/ + CHK_BMC_SLEEPQ_CMD, /*62*/ + + RUN_IN_THREAD_CMD, /*63*/ MAX_H2CCMD }; -#define _GetBBReg_CMD_ _Read_BBREG_CMD_ -#define _SetBBReg_CMD_ _Write_BBREG_CMD_ -#define _GetRFReg_CMD_ _Read_RFREG_CMD_ -#define _SetRFReg_CMD_ _Write_RFREG_CMD_ - #endif /* __RTW_CMD_H_ */ diff --git a/drivers/staging/rtl8723bs/include/rtw_event.h b/drivers/staging/rtl8723bs/include/rtw_event.h index 62e0dec249ad..e5cb46c2a731 100644 --- a/drivers/staging/rtl8723bs/include/rtw_event.h +++ b/drivers/staging/rtl8723bs/include/rtw_event.h @@ -64,10 +64,6 @@ struct wmm_event { unsigned char wmm; }; -#define GEN_EVT_CODE(event) event ## _EVT_ - - - struct fwevent { u32 parmsize; void (*event_callback)(struct adapter *dev, u8 *pbuf); diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index 786dc805516e..bf5a7e9dbe0e 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -675,33 +675,33 @@ void rtw_dummy_event_callback(struct adapter *adapter, u8 *pbuf); void rtw_fwdbg_event_callback(struct adapter *adapter, u8 *pbuf); enum { - GEN_EVT_CODE(_Read_MACREG) = 0, /*0*/ - GEN_EVT_CODE(_Read_BBREG), - GEN_EVT_CODE(_Read_RFREG), - GEN_EVT_CODE(_Read_EEPROM), - GEN_EVT_CODE(_Read_EFUSE), - GEN_EVT_CODE(_Read_CAM), /*5*/ - GEN_EVT_CODE(_Get_BasicRate), - GEN_EVT_CODE(_Get_DataRate), - GEN_EVT_CODE(_Survey), /*8*/ - GEN_EVT_CODE(_SurveyDone), /*9*/ - - GEN_EVT_CODE(_JoinBss), /*10*/ - GEN_EVT_CODE(_AddSTA), - GEN_EVT_CODE(_DelSTA), - GEN_EVT_CODE(_AtimDone), - GEN_EVT_CODE(_TX_Report), - GEN_EVT_CODE(_CCX_Report), /*15*/ - GEN_EVT_CODE(_DTM_Report), - GEN_EVT_CODE(_TX_Rate_Statistics), - GEN_EVT_CODE(_C2HLBK), - GEN_EVT_CODE(_FWDBG), - GEN_EVT_CODE(_C2HFEEDBACK), /*20*/ - GEN_EVT_CODE(_ADDBA), - GEN_EVT_CODE(_C2HBCN), - GEN_EVT_CODE(_ReportPwrState), /* filen: only for PCIE, USB */ - GEN_EVT_CODE(_CloseRF), /* filen: only for PCIE, work around ASPM */ - GEN_EVT_CODE(_WMM), /*25*/ + READ_MACREG_EVENT = 0, /*0*/ + READ_BBREG_EVENT, + READ_RFREG_EVENT, + READ_EEPROM_EVENT, + READ_EFUSE_EVENT, + READ_CAM_EVENT, /*5*/ + GET_BASICRATE_EVENT, + GET_DATARATE_EVENT, + SURVEY_EVENT, /*8*/ + SURVEY_DONE_EVENT, /*9*/ + + JOIN_BSS_EVENT, /*10*/ + ADD_STA_EVENT, + DEL_STA_EVENT, + ATIM_DONE_EVENT, + TX_REPORT_EVENT, + CCX_REPORT_EVENT, /*15*/ + DTM_REPORT_EVENT, + TX_RATE_STATISTICS_EVENT, + C2HLBK_EVENT, + FWDBG_EVENT, + C2HFEEDBACK_EVENT, /*20*/ + ADDBA_EVENT, + C2HBCN_EVENT, + REPORT_PWR_STATE_EVENT, /* filen: only for PCIE, USB */ + CLOSE_RF_EVENT, /* filen: only for PCIE, work around ASPM */ + WMM_EVENT, /*25*/ MAX_C2HEVT }; diff --git a/drivers/staging/rtl8723bs/include/wifi.h b/drivers/staging/rtl8723bs/include/wifi.h index c8844e2bf7b4..d5132bc4280a 100644 --- a/drivers/staging/rtl8723bs/include/wifi.h +++ b/drivers/staging/rtl8723bs/include/wifi.h @@ -329,21 +329,21 @@ static inline int IsFrameTypeCtrl(unsigned char *pframe) */ struct ieee80211_ht_addt_info { unsigned char control_chan; - unsigned char ht_param; - __le16 operation_mode; - __le16 stbc_param; - unsigned char basic_set[16]; + unsigned char ht_param; + __le16 operation_mode; + __le16 stbc_param; + unsigned char basic_set[16]; } __packed; struct HT_caps_element { union { struct { - __le16 HT_caps_info; + __le16 HT_caps_info; unsigned char AMPDU_para; unsigned char MCS_rate[16]; - __le16 HT_ext_caps; - __le16 Beamforming_caps; + __le16 HT_ext_caps; + __le16 Beamforming_caps; unsigned char ASEL_caps; } HT_cap_element; unsigned char HT_cap[26]; @@ -357,22 +357,22 @@ struct HT_info_element { } __packed; struct AC_param { - unsigned char ACI_AIFSN; - unsigned char CW; - __le16 TXOP_limit; + unsigned char ACI_AIFSN; + unsigned char CW; + __le16 TXOP_limit; } __packed; struct WMM_para_element { - unsigned char QoS_info; - unsigned char reserved; - struct AC_param ac_param[4]; + unsigned char QoS_info; + unsigned char reserved; + struct AC_param ac_param[4]; } __packed; struct ADDBA_request { - unsigned char dialog_token; - __le16 BA_para_set; - __le16 BA_timeout_value; - __le16 BA_starting_seqctrl; + unsigned char dialog_token; + __le16 BA_para_set; + __le16 BA_timeout_value; + __le16 BA_starting_seqctrl; } __packed; /* 802.11n HT capabilities masks */ diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index a2e49b38ab5a..7c8d912dd462 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -334,9 +334,8 @@ static void rtw_sdio_if1_deinit(struct adapter *if1) * notes: drv_init() is called when the bus driver has located a card for us to support. * We accept the new device by returning 0. */ -static int rtw_drv_init( - struct sdio_func *func, - const struct sdio_device_id *id) +static int rtw_drv_init(struct sdio_func *func, + const struct sdio_device_id *id) { int status = _FAIL; struct adapter *if1 = NULL; diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index 576f88b6a1b3..cf1706569e6d 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -10,7 +10,7 @@ #include <linux/cpu_cooling.h> #include <linux/interrupt.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -82,7 +82,7 @@ static void db8500_thermal_update_config(struct db8500_thermal_zone *th, unsigned long next_low, unsigned long next_high) { - prcmu_stop_temp_sense(); + db8500_prcmu_stop_temp_sense(); th->cur_index = idx; th->interpolated_temp = (next_low + next_high)/2; @@ -91,8 +91,8 @@ static void db8500_thermal_update_config(struct db8500_thermal_zone *th, * The PRCMU accept absolute temperatures in celsius so divide * down the millicelsius with 1000 */ - prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000)); - prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME); + db8500_prcmu_config_hotmon((u8)(next_low / 1000), (u8)(next_high / 1000)); + db8500_prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME); } static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data) @@ -204,7 +204,7 @@ static int db8500_thermal_probe(struct platform_device *pdev) static int db8500_thermal_suspend(struct platform_device *pdev, pm_message_t state) { - prcmu_stop_temp_sense(); + db8500_prcmu_stop_temp_sense(); return 0; } diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c index d7f2e6ca92c2..bb6222c8cc5f 100644 --- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -1069,3 +1069,4 @@ module_platform_driver(adc_tm5_driver); MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index f39ca0ddd17b..fb003ca96454 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -904,3 +904,4 @@ module_platform_driver(qpnp_tm_driver); MODULE_ALIAS("platform:spmi-temp-alarm"); MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c index e25e36c99a88..7ced8f76a0ec 100644 --- a/drivers/thermal/renesas/rzg3s_thermal.c +++ b/drivers/thermal/renesas/rzg3s_thermal.c @@ -270,3 +270,4 @@ module_platform_driver(rzg3s_thermal_driver); MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver"); MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 7c844589b153..cfdb8e674dd2 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -228,3 +228,4 @@ module_platform_driver(gadc_thermal_driver); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 81eaca751541..28af0fd98181 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -443,23 +443,23 @@ static int rs_startup(struct tty_struct *tty, struct serial_state *info) struct tty_port *port = &info->tport; unsigned long flags; int retval=0; - unsigned long page; + void *buffer; - page = get_zeroed_page(GFP_KERNEL); - if (!page) + buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buffer) return -ENOMEM; local_irq_save(flags); if (tty_port_initialized(port)) { - free_page(page); + kfree(buffer); goto errout; } if (info->xmit.buf) - free_page(page); + kfree(buffer); else - info->xmit.buf = (unsigned char *) page; + info->xmit.buf = buffer; #ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d ...", info->line); @@ -537,7 +537,7 @@ static void rs_shutdown(struct tty_struct *tty, struct serial_state *info) */ free_irq(IRQ_AMIGA_VERTB, info); - free_page((unsigned long)info->xmit.buf); + kfree(info->xmit.buf); info->xmit.buf = NULL; info->IER = 0; diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index c682c0d0dffa..f9a14eaa13cb 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -1642,14 +1642,14 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { .exit = pci_xr17v35x_exit, }; -#define CTI_EXAR_DEVICE(devid, bd) { \ - PCI_DEVICE_SUB( \ - PCI_VENDOR_ID_EXAR, \ - PCI_DEVICE_ID_EXAR_##devid, \ - PCI_SUBVENDOR_ID_CONNECT_TECH, \ - PCI_ANY_ID), 0, 0, \ - (kernel_ulong_t)&bd \ - } +#define CTI_EXAR_DEVICE(devid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_CONNECT_TECH, \ + PCI_ANY_ID), \ + .driver_data = (kernel_ulong_t)&bd \ +} #define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } @@ -1658,18 +1658,18 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { PCI_VENDOR_ID_EXAR, \ PCI_DEVICE_ID_EXAR_##devid, \ PCI_SUBVENDOR_ID_IBM, \ - PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ - } + PCI_SUBDEVICE_ID_IBM_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} #define USR_DEVICE(devid, sdevid, bd) { \ PCI_DEVICE_SUB( \ PCI_VENDOR_ID_USR, \ PCI_DEVICE_ID_EXAR_##devid, \ PCI_VENDOR_ID_EXAR, \ - PCI_SUBDEVICE_ID_USR_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ - } + PCI_SUBDEVICE_ID_USR_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_2S, pbn_exar_XR17C15x), @@ -1726,7 +1726,7 @@ static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(COMMTECH, 4224PCI335, pbn_fastcom335_4), EXAR_DEVICE(COMMTECH, 2324PCI335, pbn_fastcom335_4), EXAR_DEVICE(COMMTECH, 2328PCI335, pbn_fastcom335_8), - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, exar_pci_tbl); diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 5fe12577ce8b..6bda58f74acf 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1400,17 +1400,12 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); + /* + * Set up each port's state before registering the gpiochip, + * since the gpiochip callbacks will read s->p[i].regmap as + * soon as gpiolib exposes the controller. + */ for (i = 0; i < devtype->nr; i++) { - unsigned int line; - - line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); - if (line == MAX310X_UART_NRMAX) { - ret = -ERANGE; - goto out_uart; - } - - /* Initialize port data */ - s->p[i].port.line = line; s->p[i].port.dev = dev; s->p[i].port.irq = irq; s->p[i].port.type = PORT_MAX310X; @@ -1440,20 +1435,16 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); - - /* Register port */ - ret = uart_add_one_port(&max310x_uart, &s->p[i].port); - if (ret) - goto out_uart; - - set_bit(line, max310x_lines); - - /* Go to suspend mode */ - max310x_power(&s->p[i].port, 0); } #ifdef CONFIG_GPIOLIB - /* Setup GPIO controller */ + /* + * Register the GPIO controller before adding the UART ports so + * that consumers referencing the chip's own GPIOs from device + * tree (for example rs485-term-gpios = <&max310x ...>) can + * resolve them at uart_add_one_port() time instead of receiving + * -EPROBE_DEFER from their own provider. + */ s->gpio.owner = THIS_MODULE; s->gpio.parent = dev; s->gpio.label = devtype->name; @@ -1471,6 +1462,64 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty goto out_uart; #endif + for (i = 0; i < devtype->nr; i++) { + struct fwnode_handle *saved_fwnode = dev_fwnode(dev); + struct device_node *port_np = NULL; + struct device_node *child; + unsigned int line; + + line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); + if (line == MAX310X_UART_NRMAX) { + ret = -ERANGE; + goto out_uart; + } + s->p[i].port.line = line; + + /* Locate the matching "serial@i" DT subnode, if any. */ + for_each_available_child_of_node(dev->of_node, child) { + u32 reg; + + if (!of_node_name_eq(child, "serial")) + continue; + if (of_property_read_u32(child, "reg", ®)) + continue; + if (reg == i) { + port_np = child; + break; + } + } + + /* + * Temporarily retarget dev's fwnode to the per-port subnode + * so uart_get_rs485_mode() picks up the per-port properties. + * For single-port variants, fall back to the chip's own + * fwnode so legacy DTs that declare rs485 properties at the + * top level keep working. + */ + if (port_np) { + device_set_node(dev, of_fwnode_handle(port_np)); + ret = uart_get_rs485_mode(&s->p[i].port); + device_set_node(dev, saved_fwnode); + of_node_put(port_np); + if (ret) + goto out_uart; + } else if (devtype->nr == 1) { + ret = uart_get_rs485_mode(&s->p[i].port); + if (ret) + goto out_uart; + } + + /* Register port */ + ret = uart_add_one_port(&max310x_uart, &s->p[i].port); + if (ret) + goto out_uart; + + set_bit(line, max310x_lines); + + /* Go to suspend mode */ + max310x_power(&s->p[i].port, 0); + } + /* Setup interrupt */ ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist, IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), s); diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 6fad57fee912..9138fa29d301 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -16,6 +16,7 @@ #include <linux/tty_flip.h> #include <linux/bitops.h> #include <linux/mcb.h> +#include <linux/slab.h> #define MEN_Z135_MAX_PORTS 12 #define MEN_Z135_BASECLK 29491200 @@ -811,7 +812,7 @@ static int men_z135_probe(struct mcb_device *mdev, if (!uart) return -ENOMEM; - uart->rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + uart->rxbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!uart->rxbuf) return -ENOMEM; @@ -841,7 +842,7 @@ static int men_z135_probe(struct mcb_device *mdev, return 0; err: - free_page((unsigned long) uart->rxbuf); + kfree(uart->rxbuf); dev_err(dev, "Failed to add UART: %d\n", err); return err; @@ -858,7 +859,7 @@ static void men_z135_remove(struct mcb_device *mdev) line--; uart_remove_one_port(&men_z135_driver, &uart->port); - free_page((unsigned long) uart->rxbuf); + kfree(uart->rxbuf); } static const struct mcb_device_id men_z135_ids[] = { diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 80e31c4d9536..5f7f073f285e 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1662,7 +1662,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, if (priv == NULL) goto init_port_alloc_err; - rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + rxbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!rxbuf) goto init_port_free_txbuf; @@ -1735,7 +1735,7 @@ init_port_hal_free: #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE pch_uart_ports[board->line_no] = NULL; #endif - free_page((unsigned long)rxbuf); + kfree(rxbuf); init_port_free_txbuf: kfree(priv); init_port_alloc_err: @@ -1750,7 +1750,7 @@ static void pch_uart_exit_port(struct eg20t_port *priv) snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); debugfs_lookup_and_remove(name, NULL); uart_remove_one_port(&pch_uart_driver, &priv->port); - free_page((unsigned long)priv->rxbuf.buf); + kfree(priv->rxbuf.buf); } static void pch_uart_pci_remove(struct pci_dev *pdev) diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 6d99a02dd439..51e81ec0ffdb 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -197,7 +197,7 @@ struct rp2_card { }; #define RP_ID(prod) PCI_VDEVICE(RP, (prod)) -#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0)) +#define RP_CAP(ports, smpte) .driver_data = (((ports) << 8) | ((smpte) << 0)) static inline void rp2_decode_cap(const struct pci_device_id *id, int *ports, int *smpte) diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index ef8741c3e662..37929fb56174 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -506,29 +506,23 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t if (pgrp_nr < 0) return -EINVAL; - spin_lock_irq(&real_tty->ctrl.lock); + guard(spinlock_irq)(&real_tty->ctrl.lock); if (!current->signal->tty || (current->signal->tty != real_tty) || - (real_tty->ctrl.session != task_session(current))) { - retval = -ENOTTY; - goto out_unlock_ctrl; - } - rcu_read_lock(); + (real_tty->ctrl.session != task_session(current))) + return -ENOTTY; + + guard(rcu)(); pgrp = find_vpid(pgrp_nr); - retval = -ESRCH; if (!pgrp) - goto out_unlock; - retval = -EPERM; + return -ESRCH; if (session_of_pgrp(pgrp) != task_session(current)) - goto out_unlock; - retval = 0; + return -EPERM; + put_pid(real_tty->ctrl.pgrp); real_tty->ctrl.pgrp = get_pid(pgrp); -out_unlock: - rcu_read_unlock(); -out_unlock_ctrl: - spin_unlock_irq(&real_tty->ctrl.lock); - return retval; + + return 0; } /** @@ -542,7 +536,6 @@ out_unlock_ctrl: */ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) { - unsigned long flags; pid_t sid; /* @@ -552,17 +545,13 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; - spin_lock_irqsave(&real_tty->ctrl.lock, flags); - if (!real_tty->ctrl.session) - goto err; - sid = pid_vnr(real_tty->ctrl.session); - spin_unlock_irqrestore(&real_tty->ctrl.lock, flags); + scoped_guard(spinlock_irqsave, &real_tty->ctrl.lock) { + if (!real_tty->ctrl.session) + return -ENOTTY; + sid = pid_vnr(real_tty->ctrl.session); + } return put_user(sid, p); - -err: - spin_unlock_irqrestore(&real_tty->ctrl.lock, flags); - return -ENOTTY; } /* diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 7d40eacc21b3..bf1502fd5bd4 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -53,8 +53,6 @@ #define HEADER_SIZE 4u #define CON_BUF_SIZE (IS_ENABLED(CONFIG_BASE_SMALL) ? 256 : PAGE_SIZE) -DEFINE_FREE(free_page_ptr, void *, if (_T) free_page((unsigned long)_T)); - /* * Our minor space: * @@ -371,7 +369,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) loff_t pos; bool viewed, attr, uni_mode; - char *con_buf __free(free_page_ptr) = (char *)__get_free_page(GFP_KERNEL); + char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!con_buf) return -ENOMEM; @@ -596,7 +594,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) if (use_unicode(inode)) return -EOPNOTSUPP; - char *con_buf __free(free_page_ptr) = (char *)__get_free_page(GFP_KERNEL); + char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!con_buf) return -ENOMEM; diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index bb5405460035..e3f32c3e9535 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -147,12 +147,12 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); if (IS_ERR(cdns->usb2_phy)) return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), - "Failed to get cdn3,usb2-phy\n"); + "Failed to get cdns3,usb2-phy\n"); 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"); + "Failed to get cdns3,usb3-phy\n"); ret = phy_init(cdns->usb2_phy); if (ret) diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index a247da73f34d..6f0354aba38b 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -37,6 +37,7 @@ */ #include <linux/fs.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/gfp.h> #include <linux/usb.h> @@ -408,7 +409,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, return 0; /* allocate 2^1 pages = 8K (on i386); * should be more than enough for one device */ - pages_start = (char *)__get_free_pages(GFP_NOIO, 1); + pages_start = kmalloc(PAGE_SIZE << 1, GFP_NOIO); if (!pages_start) return -ENOMEM; @@ -479,7 +480,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, if (length > *nbytes) length = *nbytes; if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) { - free_pages((unsigned long)pages_start, 1); + kfree(pages_start); return -EFAULT; } *nbytes -= length; @@ -490,7 +491,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, } else *skip_bytes -= length; - free_pages((unsigned long)pages_start, 1); + kfree(pages_start); /* Now look at all of this device's children. */ usb_hub_for_each_child(usbdev, chix, childdev) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 24960ba9caa9..5262e11c12cd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3148,7 +3148,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, delay = HUB_LONG_RESET_TIME; } - dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); + dev_err_ratelimited(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); done: if (status == 0) { diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 517aa7f1486d..fd5c2cd36c59 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -789,9 +789,9 @@ static void dwc3_ulpi_setup(struct dwc3 *dwc) if (dwc->enable_usb2_transceiver_delay) { for (index = 0; index < dwc->num_usb2_ports; index++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index)); reg |= DWC3_GUSB2PHYCFG_XCVRDLY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg); } } } @@ -2188,22 +2188,89 @@ static void dwc3_vbus_draw_work(struct work_struct *work) ret, dwc->current_limit); } -static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) +static int dwc3_psy_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct dwc3 *dwc = container_of(nb, struct dwc3, psy_nb); + struct power_supply *psy = data; + unsigned long flags; + + if (dwc->usb_psy) + return NOTIFY_DONE; + + if (strcmp(psy->desc->name, dwc->usb_psy_name) != 0) + return NOTIFY_DONE; + + /* Explicitly get the reference for this psy */ + psy = power_supply_get_by_name(dwc->usb_psy_name); + if (!psy) + return NOTIFY_DONE; + + spin_lock_irqsave(&dwc->lock, flags); + /* + * The USB power_supply may already be set. This can happen if notifier + * callbacks for the USB power_supply race, or if a previous notifier + * callback has already successfully fetched and associated the instance. + * In such cases, release the newly acquired reference and ignore + * subsequent notifications until the notifier is unregistered. + */ + if (dwc->usb_psy) { + spin_unlock_irqrestore(&dwc->lock, flags); + power_supply_put(psy); + return NOTIFY_DONE; + } + + dwc->usb_psy = psy; + if (dwc->current_limit != DWC3_CURRENT_UNSPECIFIED) + schedule_work(&dwc->vbus_draw_work); + spin_unlock_irqrestore(&dwc->lock, flags); + + return NOTIFY_OK; +} + +static void dwc3_get_usb_power_supply(struct dwc3 *dwc) { - struct power_supply *usb_psy; - const char *usb_psy_name; + struct power_supply *psy; + unsigned long flags; int ret; - ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name); + ret = device_property_read_string(dwc->dev, "usb-psy-name", &dwc->usb_psy_name); if (ret < 0) - return NULL; - - usb_psy = power_supply_get_by_name(usb_psy_name); - if (!usb_psy) - return ERR_PTR(-EPROBE_DEFER); + return; INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work); - return usb_psy; + + dwc->current_limit = DWC3_CURRENT_UNSPECIFIED; + dwc->psy_nb.notifier_call = dwc3_psy_notifier; + ret = power_supply_reg_notifier(&dwc->psy_nb); + if (ret) { + dev_err(dwc->dev, "Failed to register power supply notifier: %d\n", ret); + dwc->psy_nb.notifier_call = NULL; + return; + } + + psy = power_supply_get_by_name(dwc->usb_psy_name); + if (!psy) + return; + + /* Unregister the notifier now that we have the power supply */ + power_supply_unreg_notifier(&dwc->psy_nb); + dwc->psy_nb.notifier_call = NULL; + + spin_lock_irqsave(&dwc->lock, flags); + /* + * It is possible that the notifier callback ran before we reached here + * and successfully fetched the power supply. In that case we need to + * release the above reference. + */ + if (dwc->usb_psy) { + spin_unlock_irqrestore(&dwc->lock, flags); + power_supply_put(psy); + return; + } + + dwc->usb_psy = psy; + spin_unlock_irqrestore(&dwc->lock, flags); } int dwc3_core_probe(const struct dwc3_probe_data *data) @@ -2251,9 +2318,9 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc3_get_software_properties(dwc, &data->properties); - dwc->usb_psy = dwc3_get_usb_power_supply(dwc); - if (IS_ERR(dwc->usb_psy)) - return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); + spin_lock_init(&dwc->lock); + + dwc3_get_usb_power_supply(dwc); if (!data->ignore_clocks_and_resets) { dwc->reset = devm_reset_control_array_get_optional_shared(dev); @@ -2305,7 +2372,6 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc->num_usb3_ports = 1; } - spin_lock_init(&dwc->lock); mutex_init(&dwc->mutex); pm_runtime_get_noresume(dev); @@ -2373,6 +2439,8 @@ err_disable_clks: err_assert_reset: reset_control_assert(dwc->reset); err_put_psy: + if (dwc->psy_nb.notifier_call) + power_supply_unreg_notifier(&dwc->psy_nb); if (dwc->usb_psy) power_supply_put(dwc->usb_psy); @@ -2429,6 +2497,9 @@ void dwc3_core_remove(struct dwc3 *dwc) dwc3_free_event_buffers(dwc); + if (dwc->psy_nb.notifier_call) + power_supply_unreg_notifier(&dwc->psy_nb); + if (dwc->usb_psy) { cancel_work_sync(&dwc->vbus_draw_work); power_supply_put(dwc->usb_psy); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e0dee9d28740..608daeb7ef10 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -722,7 +722,6 @@ struct dwc3_event_buffer { * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint - * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers * @trb_pool_dma: dma address of @trb_pool * @trb_enqueue: enqueue 'pointer' into TRB array @@ -1059,6 +1058,8 @@ struct dwc3_glue_ops { * @role_switch_default_mode: default operation mode of controller while * usb role is USB_ROLE_NONE. * @usb_psy: pointer to power supply interface. + * @usb_psy_name: name of the USB power supply + * @psy_nb: power supply notifier block * @vbus_draw_work: Work to set the vbus drawing limit * @current_limit: How much current to draw from vbus, in milliAmperes. * @usb2_phy: pointer to USB2 PHY @@ -1251,9 +1252,13 @@ struct dwc3 { enum usb_dr_mode role_switch_default_mode; struct power_supply *usb_psy; + const char *usb_psy_name; + struct notifier_block psy_nb; struct work_struct vbus_draw_work; unsigned int current_limit; +#define DWC3_CURRENT_UNSPECIFIED UINT_MAX + u32 fladj; u32 ref_clk_per; u32 irq_gadget; diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index e11d7643f966..632634d6e81e 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -205,7 +205,9 @@ static int dwc3_ti_init(struct dwc3_am62 *am62) dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg); - clk_prepare_enable(am62->usb2_refclk); + ret = clk_prepare_enable(am62->usb2_refclk); + if (ret) + return ret; /* Set mode valid bit to indicate role is valid */ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); @@ -361,14 +363,19 @@ static int dwc3_ti_resume_common(struct device *dev) { struct dwc3_am62 *am62 = dev_get_drvdata(dev); u32 reg; + int ret; reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG); if (reg != USBSS_DEBUG_CFG_DISABLED) { /* lost power/context */ - dwc3_ti_init(am62); + ret = dwc3_ti_init(am62); + if (ret) + return ret; } else { dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF); - clk_prepare_enable(am62->usb2_refclk); + ret = clk_prepare_enable(am62->usb2_refclk); + if (ret) + return ret; } if (device_may_wakeup(dev)) { diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index f43f73ac36ff..ac68b4218b56 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -602,7 +602,7 @@ static void dwc3_qcom_run_stop_notifier(struct dwc3 *dwc, bool is_on) pm_runtime_mark_last_busy(qcom->dev); } -struct dwc3_glue_ops dwc3_qcom_glue_ops = { +static struct dwc3_glue_ops dwc3_qcom_glue_ops = { .pre_set_role = dwc3_qcom_set_role_notifier, .pre_run_stop = dwc3_qcom_run_stop_notifier, }; diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index 9b9525592a85..b832505e1b04 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -98,18 +98,10 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) dwc3_xlnx_mask_phy_rst(priv_data, false); - /* Assert and De-assert reset */ - ret = reset_control_assert(crst); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to assert Reset\n"); - return ret; - } - - ret = reset_control_deassert(crst); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to De-assert Reset\n"); - return ret; - } + /* assert and deassert reset */ + ret = reset_control_reset(crst); + if (ret) + return dev_err_probe(dev, ret, "failed to assert and deassert reset\n"); dwc3_xlnx_mask_phy_rst(priv_data, true); dwc3_xlnx_set_coherency(priv_data, XLNX_USB2_TRAFFIC_ROUTE_CONFIG); @@ -194,7 +186,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } if (priv_data->usb3_phy) { - /* Set PIPE Power Present signal in FPD Power Present Register*/ + /* Set PIPE Power Present signal in FPD Power Present Register */ writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3d4ca68e584c..fa0f16ffafef 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3124,15 +3124,26 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) { struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; if (dwc->usb2_phy) return usb_phy_set_power(dwc->usb2_phy, mA); - if (!dwc->usb_psy) - return -EOPNOTSUPP; + spin_lock_irqsave(&dwc->lock, flags); + if (!dwc->usb_psy) { + if (!dwc->psy_nb.notifier_call) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -EOPNOTSUPP; + } + dwc->current_limit = mA; + spin_unlock_irqrestore(&dwc->lock, flags); + dev_dbg(dwc->dev, "Stored VBUS draw: %u mA (power supply not ready)\n", mA); + return 0; + } dwc->current_limit = mA; schedule_work(&dwc->vbus_draw_work); + spin_unlock_irqrestore(&dwc->lock, flags); return 0; } @@ -3934,15 +3945,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, } } +static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc, + struct usb_gadget_driver **driver, + struct usb_gadget **gadget) +{ + if (!dwc->async_callbacks || !dwc->gadget_driver || + !dwc->gadget_driver->disconnect) + return false; + + *driver = dwc->gadget_driver; + *gadget = dwc->gadget; + + return true; +} + static void dwc3_disconnect_gadget(struct dwc3 *dwc) { - if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + + if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(dwc->gadget); + driver->disconnect(gadget); spin_lock(&dwc->lock); } } +static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc) +{ + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + driver->disconnect(gadget); +} + static void dwc3_suspend_gadget(struct dwc3 *dwc) { if (dwc->async_callbacks && dwc->gadget_driver->suspend) { @@ -4838,7 +4882,6 @@ EXPORT_SYMBOL_GPL(dwc3_gadget_exit); int dwc3_gadget_suspend(struct dwc3 *dwc) { - unsigned long flags; int ret; ret = dwc3_gadget_soft_disconnect(dwc); @@ -4852,10 +4895,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) return -EAGAIN; } - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) - dwc3_disconnect_gadget(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_disconnect_gadget_sleepable(dwc); return 0; } diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 1a48329a4e08..956be5b56510 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -4267,8 +4267,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, return 0; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -4562,6 +4560,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, else usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&fotg210->lock, flags); done: return status; diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index e4f7828ae75d..837f753d0cae 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd) ret = 0; /* Change the printer status to show that it's on-line. */ dev->printer_status |= PRINTER_SELECTED; + kref_get(&dev->kref); } spin_unlock_irqrestore(&dev->lock, flags); - kref_get(&dev->kref); - return ret; } diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 3da54a7d7aba..a2fd239b7ad3 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params, static int rndis_query_response(struct rndis_params *params, rndis_query_msg_type *buf) { + u32 BufLength, BufOffset; rndis_query_cmplt_type *resp; rndis_resp_t *r; @@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params, if (!params->dev) return -ENOTSUPP; + BufLength = le32_to_cpu(buf->InformationBufferLength); + BufOffset = le32_to_cpu(buf->InformationBufferOffset); + if ((BufLength > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE)) + return -EINVAL; + /* * we need more memory: * gen_ndis_query_resp expects enough space for @@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params, resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), - le32_to_cpu(buf->InformationBufferOffset) - + 8 + (u8 *)buf, - le32_to_cpu(buf->InformationBufferLength), - r)) { + BufOffset + 8 + (u8 *)buf, + BufLength, r)) { /* OID not supported */ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); resp->MessageLength = cpu_to_le32(sizeof *resp); @@ -1074,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port, /* tmp points to a struct rndis_packet_msg_type */ __le32 *tmp = (void *)skb->data; + /* Need at least MessageType, MessageLength, DataOffset, DataLength */ + if (skb->len < 16) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + /* MessageType, MessageLength */ if (cpu_to_le32(RNDIS_MSG_PACKET) != get_unaligned(tmp++)) { diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 60340ff9edbf..f6da12b553a0 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type; /** * struct usb_udc - describes one usb device controller * @driver: the gadget driver pointer. For use by the class code - * @dev: the child device to the actual controller * @gadget: the gadget. For use by the class code + * @gadget_release: the gadget's release routine + * @dev: the child device to the actual controller * @list: for use by the udc class driver * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true @@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type; struct usb_udc { struct usb_gadget_driver *driver; struct usb_gadget *gadget; + void (*gadget_release)(struct device *dev); struct device dev; struct list_head list; bool vbus; @@ -1362,6 +1364,17 @@ static void usb_udc_nop_release(struct device *dev) dev_vdbg(dev, "%s\n", __func__); } +static void usb_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + /* Cache the gadget's release routine to prevent UAF */ + void (*release)(struct device *dev) = udc->gadget_release; + + put_device(&udc->dev); + release(dev); +} + /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's @@ -1418,6 +1431,14 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_init(&udc->connect_lock); udc->started = false; + /* + * Align decoupled lifecycles: take a UDC reference to ensure it + * remains allocated until the gadget is released, requiring an + * override of the gadget's release routine to drop it. + */ + udc->gadget_release = gadget->dev.release; + gadget->dev.release = usb_gadget_release; + get_device(&udc->dev); mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -1462,6 +1483,12 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); + /* + * Revert the override and drop the UDC reference to prevent + * leaking the UDC if the gadget was statically allocated. + */ + gadget->dev.release = udc->gadget_release; + put_device(&udc->dev); err_put_udc: put_device(&udc->dev); diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index e7a5d8553c0e..d190e16d43fc 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1951,7 +1951,6 @@ static int r8a66597_probe(struct platform_device *pdev) return 0; err_add_udc: - r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); clean_up2: if (r8a66597->pdata->on_chip) clk_disable_unprepare(r8a66597->clk); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a241337c9af8..57d07d1c2dfa 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1623,6 +1623,7 @@ iso_stream_schedule( status = 1; /* and give it back immediately */ iso_sched_free(stream, sched); sched = NULL; + urb->hcpriv = NULL; } } urb->error_count = skip / period; @@ -1653,8 +1654,6 @@ iso_stream_schedule( return status; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -1966,6 +1965,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; @@ -2343,6 +2346,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 9e0e06bbc570..23dc9eddc06c 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -683,7 +683,7 @@ static int fill_buffer(struct debug_buffer *buf) int ret; if (!buf->page) - buf->page = (char *)get_zeroed_page(GFP_KERNEL); + buf->page = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!buf->page) { ret = -ENOMEM; @@ -729,11 +729,8 @@ static int debug_close(struct inode *inode, struct file *file) { struct debug_buffer *buf = file->private_data; - if (buf) { - if (buf->page) - free_page((unsigned long)buf->page); - kfree(buf); - } + kfree(buf->page); + kfree(buf); return 0; } diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 4ae47edd4b8b..b044977f6f56 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1591,6 +1591,7 @@ sl811h_remove(struct platform_device *dev) remove_debug_file(sl811); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); /* some platforms may use IORESOURCE_IO */ res = platform_get_resource(dev, IORESOURCE_MEM, 1); diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index 23153e136d4b..a5deeee4d5dc 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -58,6 +58,8 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring) if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) goto err; + kvfree(pages); + /* * Save first segment dma address to sg dma_address field for the sideband * client to have access to the IOVA of the ring. diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6922cc5496c1..f44ccee5fa07 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3785,6 +3785,7 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_virt_device *vdev; struct xhci_command *command; struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_stream_info *stream_info[EP_CTX_PER_DEV]; unsigned int ep_index; unsigned long flags; u32 changed_ep_bitmask; @@ -3845,10 +3846,15 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, if (ret < 0) return ret; + /* + * dma_free_coherent() called by xhci_free_stream_info() may sleep, + * so save stream_info pointers and clear references under lock, + * then free the memory outside lock. + */ spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < num_eps; i++) { ep_index = xhci_get_endpoint_index(&eps[i]->desc); - xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info); + stream_info[i] = vdev->eps[ep_index].stream_info; vdev->eps[ep_index].stream_info = NULL; /* FIXME Unset maxPstreams in endpoint context and * update deq ptr to point to normal string ring. @@ -3858,6 +3864,9 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, } spin_unlock_irqrestore(&xhci->lock, flags); + for (i = 0; i < num_eps; i++) + xhci_free_stream_info(xhci, stream_info[i]); + return 0; } diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index d8016540953f..9c06f7775301 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -320,7 +320,6 @@ bail: mutex_unlock(&dev->lock); destruction: mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "release success"); return rv; } diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 24c4cd0df829..3c2474dca810 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -522,7 +522,7 @@ static int usbio_resume(struct usb_interface *intf) static void usbio_disconnect(struct usb_interface *intf) { struct usbio_device *usbio = usb_get_intfdata(intf); - struct usbio_client *client; + struct usbio_client *client, *next; /* Wakeup any clients waiting for a reply */ usbio->rxdat_len = 0; @@ -539,7 +539,7 @@ static void usbio_disconnect(struct usb_interface *intf) usb_kill_urb(usbio->urb); usb_free_urb(usbio->urb); - list_for_each_entry_reverse(client, &usbio->cli_list, link) { + list_for_each_entry_safe_reverse(client, next, &usbio->cli_list, link) { auxiliary_device_delete(&client->auxdev); auxiliary_device_uninit(&client->auxdev); } diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index b7d3c44b970e..1ce48f5832d7 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -732,8 +732,11 @@ static int uss720_probe(struct usb_interface *intf, * here. */ ret = get_1284_register(pp, 0, ®, GFP_KERNEL); dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg); - if (ret < 0) + if (ret < 0) { + priv->pp = NULL; + parport_del_port(pp); goto probe_abort; + } ret = usb_find_last_int_in_endpoint(interface, &epd); if (!ret) { diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 8770de01a384..ed49a3bc859c 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2305,7 +2305,8 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) /*US_DEBUG(usb_stor_show_command(us, srb)); */ scsi_set_resid(srb, 0); - if (unlikely(!(info->SD_Status & SD_Ready) || (info->MS_Status & MS_Ready))) + if (unlikely(!(info->SD_Status & SD_Ready) && + !(info->MS_Status & MS_Ready))) result = ene_init(us); if (result == USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 263a89c5f324..51c92dd887ab 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -764,6 +764,7 @@ int dp_altmode_probe(struct typec_altmode *alt) struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); struct fwnode_handle *fwnode; struct dp_altmode *dp; + u32 cap = DP_CAP_CAPABILITY(alt->vdo); /* Port can only be DFP_U. */ if (typec_altmode_get_data_role(alt) != TYPEC_HOST) @@ -778,6 +779,18 @@ int dp_altmode_probe(struct typec_altmode *alt) return -ENODEV; } + /* + * Make sure the DisplayPort VDO is valid (VESA DPAM v2.1a, Section + * 5.4.1, Table 5-6, DP Capabilities VDO). A device exposing DP on a + * USB-C receptacle must advertise at least one pin assignment for the + * capability it claims, otherwise Alt Mode can never be configured. + */ + if ((cap == DP_CAP_DFP_D && !DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)) || + (cap == DP_CAP_UFP_D && !DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo))) { + typec_altmode_put_plug(plug); + return -ENODEV; + } + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); if (!dp) { typec_altmode_put_plug(plug); @@ -790,7 +803,6 @@ int dp_altmode_probe(struct typec_altmode *alt) dp->alt = alt; alt->desc = "DisplayPort"; - typec_altmode_set_ops(alt, &dp_altmode_ops); if (plug) { plug->desc = "Displayport"; @@ -811,6 +823,10 @@ int dp_altmode_probe(struct typec_altmode *alt) if (plug) typec_altmode_set_drvdata(plug, dp); + if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt)) + return 0; + + typec_altmode_set_ops(alt, &dp_altmode_ops); if (!alt->mode_selection) { dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; schedule_work(&dp->work); diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c index 32250b94262a..2eccdddf1b1f 100644 --- a/drivers/usb/typec/altmodes/thunderbolt.c +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt) alt->desc = "Thunderbolt3"; typec_altmode_set_drvdata(alt, tbt); + + if (typec_cable_altmode_unsupported(alt)) + return 0; + typec_altmode_set_ops(alt, &tbt_altmode_ops); if (!alt->mode_selection && tbt_ready(alt)) { diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 604868ebf422..41df115912b9 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1537,7 +1537,9 @@ static int anx7411_i2c_probe(struct i2c_client *client) if (anx7411_typec_check_connection(plat)) dev_err(dev, "check status\n"); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto free_wq; return 0; diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 0977581ad1b6..54c7810b563b 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable) } EXPORT_SYMBOL_GPL(typec_cable_is_active); +enum typec_cable_altmode_support { + CABLE_SUPPORT_UNKNOWN, + CABLE_SUPPORTED, + CABLE_NOT_SUPPORTED, +}; + +static enum typec_cable_altmode_support +typec_cable_check_altmode_support(struct typec_cable *cable, + struct typec_altmode *alt) +{ + struct typec_altmode *plug; + u32 speed; + + /* + * Check if the cable has an e-marker, supports modal operation, and the + * SOP' altmode nodes are created. + */ + plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); + if (plug) { + typec_altmode_put_plug(plug); + return CABLE_SUPPORTED; + } + + /* The identity is not specified */ + if (!cable->identity) + return CABLE_SUPPORT_UNKNOWN; + + /* Non-e-marked cable */ + if (!cable->identity->id_header) + return CABLE_NOT_SUPPORTED; + + switch (PD_IDH_PTYPE(cable->identity->id_header)) { + case IDH_PTYPE_PCABLE: + speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]); + if (speed == CABLE_USB2_ONLY) + return CABLE_NOT_SUPPORTED; + return CABLE_SUPPORTED; + case IDH_PTYPE_ACABLE: + /* + * Active cables must establish an SOP' communication + * node. Since that check failed at the beginning of + * this function, this active cable does not support + * this specific altmode. + */ + return CABLE_NOT_SUPPORTED; + } + + return CABLE_SUPPORT_UNKNOWN; +} + +/** + * typec_cable_altmode_unsupported - Check if a cable restricts altmode + * @alt: The Alternate Mode to evaluate + * + * Returns true if the connected cable is incapable of handling the altmode. + */ +bool typec_cable_altmode_unsupported(struct typec_altmode *alt) +{ + enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN; + struct typec_cable *cable; + + cable = typec_cable_get(typec_altmode2port(alt)); + if (cable) { + support = typec_cable_check_altmode_support(cable, alt); + typec_cable_put(cable); + } + + return support == CABLE_NOT_SUPPORTED; +} +EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported); + /** * typec_cable_set_identity - Report result from Discover Identity command * @cable: The cable updated identity values @@ -1619,6 +1690,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev, return -EINVAL; ret = port->ops->pd_set(port, pd); + put_device(&pd->dev); if (ret) return ret; diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index db5e4a4c0a99..9b908c46bd7d 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -275,9 +275,7 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { - struct typec_mux_dev **mux_devs = data; struct device *dev; - int i; /* * Device graph (OF graph) does not give any means to identify the @@ -293,14 +291,6 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); - /* Skip duplicates */ - for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) - if (to_typec_mux_dev(dev) == mux_devs[i]) { - put_device(dev); - return NULL; - } - - return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -326,8 +316,7 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - (void **)mux_devs, - typec_mux_match, + NULL, typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index f52443638ee2..64e0a61b776a 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -206,12 +206,12 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state CONN_STATUS_1_DP_HPD_LEVEL; switch (state->mode) { + case TYPEC_DP_STATE_D: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + fallthrough; case TYPEC_DP_STATE_C: cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; - fallthrough; - case TYPEC_DP_STATE_D: - cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; break; default: /* MODE_E */ break; diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c index d6e1289a4945..34ef81f75693 100644 --- a/drivers/usb/typec/mux/tusb1046.c +++ b/drivers/usb/typec/mux/tusb1046.c @@ -178,6 +178,7 @@ static const struct of_device_id tusb1046_match_table[] = { {.compatible = "ti,tusb1046"}, {}, }; +MODULE_DEVICE_TABLE(of, tusb1046_match_table); static struct i2c_driver tusb1046_driver = { .driver = { diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 9d3b1fcf7e27..af8356df6b98 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -294,6 +294,8 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip) return 0; } +static void rt1711h_unregister_tcpci_port(void *tcpci); + static int rt1711h_probe(struct i2c_client *client) { int ret; @@ -339,6 +341,10 @@ static int rt1711h_probe(struct i2c_client *client) if (IS_ERR_OR_NULL(chip->tcpci)) return PTR_ERR(chip->tcpci); + ret = devm_add_action_or_reset(chip->dev, rt1711h_unregister_tcpci_port, chip->tcpci); + if (ret) + return ret; + ret = devm_request_threaded_irq(chip->dev, client->irq, NULL, rt1711h_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW, @@ -356,11 +362,9 @@ static int rt1711h_probe(struct i2c_client *client) return 0; } -static void rt1711h_remove(struct i2c_client *client) +static void rt1711h_unregister_tcpci_port(void *tcpci) { - struct rt1711h_chip *chip = i2c_get_clientdata(client); - - tcpci_unregister_port(chip->tcpci); + tcpci_unregister_port(tcpci); } static const struct rt1711h_chip_info rt1711h = { @@ -393,7 +397,6 @@ static struct i2c_driver rt1711h_i2c_driver = { .of_match_table = rt1711h_of_match, }, .probe = rt1711h_probe, - .remove = rt1711h_remove, .id_table = rt1711h_id, }; module_i2c_driver(rt1711h_i2c_driver); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index bc531923b1ca..5e652f3d449b 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2034,7 +2034,7 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) if (!port->partner) return; - for (i = 0; i < modep->altmodes; i++) { + for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { altmode = typec_partner_register_altmode(port->partner, &modep->altmode_desc[i]); if (IS_ERR(altmode)) { @@ -2052,9 +2052,10 @@ static void tcpm_register_plug_altmodes(struct tcpm_port *port) struct typec_altmode *altmode; int i; - typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes); + typec_plug_set_num_altmodes(port->plug_prime, + min(modep->altmodes, ALTMODE_DISCOVERY_MAX)); - for (i = 0; i < modep->altmodes; i++) { + for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { altmode = typec_plug_register_altmode(port->plug_prime, &modep->altmode_desc[i]); if (IS_ERR(altmode)) { @@ -3093,7 +3094,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP); @@ -3141,7 +3142,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME); @@ -4896,11 +4897,11 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) struct pd_mode_data *modep_prime = &port->mode_data_prime; int i; - for (i = 0; i < modep->altmodes; i++) { + for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { typec_unregister_altmode(port->partner_altmode[i]); port->partner_altmode[i] = NULL; } - for (i = 0; i < modep_prime->altmodes; i++) { + for (i = 0; i < modep_prime->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { typec_unregister_altmode(port->plug_prime_altmode[i]); port->plug_prime_altmode[i] = NULL; } diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index d5ee0af9058b..b6335b36d384 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1744,7 +1744,7 @@ static int tps6598x_probe(struct i2c_client *client) struct tps6598x *tps; struct fwnode_handle *fwnode; u32 status; - u32 vid; + u32 vid = 0; int ret; data = i2c_get_match_data(client); @@ -1772,8 +1772,11 @@ static int tps6598x_probe(struct i2c_client *client) if (!device_is_compatible(tps->dev, "ti,tps25750")) { ret = tps6598x_read32(tps, TPS_REG_VID, &vid); - if (ret < 0 || !vid) + if (ret < 0 || !vid) { + dev_err(tps->dev, "failed to read vendor ID: %d, vid: %#x\n", + ret, vid); return -ENODEV; + } } /* @@ -1851,7 +1854,7 @@ static int tps6598x_probe(struct i2c_client *client) IRQF_SHARED | IRQF_ONESHOT, dev_name(&client->dev), tps); } else { - dev_warn(tps->dev, "Unable to find the interrupt, switching to polling\n"); + dev_dbg(tps->dev, "no IRQ specified, using polling mode\n"); INIT_DELAYED_WORK(&tps->wq_poll, tps6598x_poll_work); queue_delayed_work(system_power_efficient_wq, &tps->wq_poll, msecs_to_jiffies(POLL_INTERVAL)); diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index 03edbb77bbd6..d4140f4da5bb 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -142,9 +142,13 @@ #define TPS_SYSTEM_POWER_STATE_S4 0x04 #define TPS_SYSTEM_POWER_STATE_S5 0x05 -/* TPS_REG_POWER_STATUS bits */ -#define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x)) -#define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x)) +/* TPS_REG_POWER_STATUS bits (masks shared by TPS_FIELD_GET accessors and FIELD_PREP) */ +#define TPS_POWER_STATUS_CONNECTION_MASK BIT(0) +#define TPS_POWER_STATUS_SOURCESINK_MASK BIT(1) +#define TPS_POWER_STATUS_CONNECTION(x) \ + TPS_FIELD_GET(TPS_POWER_STATUS_CONNECTION_MASK, (x)) +#define TPS_POWER_STATUS_SOURCESINK(x) \ + TPS_FIELD_GET(TPS_POWER_STATUS_SOURCESINK_MASK, (x)) #define TPS_POWER_STATUS_BC12_DET(x) TPS_FIELD_GET(BIT(2), (x)) #define TPS_POWER_STATUS_TYPEC_CURRENT_MASK GENMASK(3, 2) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index ff33a5e7c6b0..77a0dd75edd3 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -82,8 +82,8 @@ static int ucsi_resp_show(struct seq_file *s, void *not_used) if (ucsi->debugfs->status) return ucsi->debugfs->status; - seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high, - ucsi->debugfs->response.low); + seq_printf(s, "0x%016llx%016llx%016llx\n", ucsi->debugfs->response.ext, + ucsi->debugfs->response.high, ucsi->debugfs->response.low); return 0; } DEFINE_SHOW_ATTRIBUTE(ucsi_resp); @@ -162,6 +162,7 @@ void ucsi_debugfs_unregister(struct ucsi *ucsi) debugfs_remove_recursive(ucsi->debugfs->dentry); kfree(ucsi->debugfs); + ucsi->debugfs = NULL; } void ucsi_debugfs_init(void) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index c44da2fae81f..7067f2561b84 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -185,13 +185,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) static int ucsi_displayport_configure(struct ucsi_dp *dp) { - u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); u64 command; if (!dp->override) return 0; - command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); + command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, dp->data.conf); return ucsi_send_command(dp->con->ucsi, command, NULL, 0); } diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 92166a3725b1..acc2144c2d3b 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -2017,6 +2017,26 @@ static void ucsi_resume_work(struct work_struct *work) } } +int ucsi_suspend(struct ucsi *ucsi) +{ + int i; + + /* + * Cancel pending work so it cannot access the firmware after the ACPI + * EC is stopped for suspend; state is re-read on resume. + */ + cancel_delayed_work_sync(&ucsi->work); + + if (!ucsi->connector) + return 0; + + for (i = 0; i < ucsi->cap.num_connectors; i++) + cancel_work_sync(&ucsi->connector[i].work); + + return 0; +} +EXPORT_SYMBOL_GPL(ucsi_suspend); + int ucsi_resume(struct ucsi *ucsi) { if (ucsi->connector) @@ -2040,7 +2060,7 @@ static void ucsi_init_work(struct work_struct *work) return; } - queue_delayed_work(system_long_wq, &ucsi->work, + queue_delayed_work(system_dfl_long_wq, &ucsi->work, UCSI_ROLE_SWITCH_INTERVAL); } } @@ -2164,7 +2184,7 @@ int ucsi_register(struct ucsi *ucsi) UCSI_BCD_GET_MINOR(ucsi->version), UCSI_BCD_GET_SUBMINOR(ucsi->version)); - queue_delayed_work(system_long_wq, &ucsi->work, 0); + queue_delayed_work(system_dfl_long_wq, &ucsi->work, 0); ucsi_debugfs_register(ucsi); return 0; @@ -2186,6 +2206,8 @@ void ucsi_unregister(struct ucsi *ucsi) cancel_delayed_work_sync(&ucsi->work); cancel_work_sync(&ucsi->resume_work); + ucsi_debugfs_unregister(ucsi); + /* Disable notifications */ ucsi->ops->async_control(ucsi, cmd); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 325ed1e5ca80..dc594388dcd0 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -466,6 +466,7 @@ struct ucsi_debugfs_entry { struct ucsi_data { u64 low; u64 high; + u64 ext; } response; int status; u8 message_out[MESSAGE_OUT_MAX_LEN]; @@ -582,6 +583,7 @@ int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command, void *msg_out, size_t msg_out_size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_suspend(struct ucsi *ucsi); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 60b12961e1a4..18286d3e9cc5 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -263,6 +263,13 @@ static void ucsi_acpi_remove(struct platform_device *pdev) ucsi_acpi_notify); } +static int ucsi_acpi_suspend(struct device *dev) +{ + struct ucsi_acpi *ua = dev_get_drvdata(dev); + + return ucsi_suspend(ua->ucsi); +} + static int ucsi_acpi_resume(struct device *dev) { struct ucsi_acpi *ua = dev_get_drvdata(dev); @@ -270,7 +277,8 @@ static int ucsi_acpi_resume(struct device *dev) return ucsi_resume(ua->ucsi); } -static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, ucsi_acpi_suspend, + ucsi_acpi_resume); static const struct acpi_device_id ucsi_acpi_match[] = { { "PNP0CA0", 0 }, diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index ad669d2f8b9c..ca1b534cb183 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -84,6 +84,8 @@ struct gaokun_ucsi_port { struct auxiliary_device *bridge; struct typec_mux *typec_mux; + struct typec_mux_state state; + struct typec_altmode dp_alt; int idx; enum gaokun_ucsi_ccx ccx; @@ -292,24 +294,22 @@ static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec) static void gaokun_ucsi_handle_usb_mode(struct gaokun_ucsi_port *port) { struct gaokun_ucsi *uec = port->ucsi; - struct typec_mux_state state = {}; - struct typec_altmode dp_alt = {}; int idx = port->idx, ret; /* * For every typec port on this platform, the only mode-switch is * controlled by its qmp combo phy which consumes svid and mode only. */ - dp_alt.svid = port->svid; - state.mode = port->mode; - state.alt = &dp_alt; + port->dp_alt.svid = port->svid; + port->state.mode = port->mode; + port->state.alt = &port->dp_alt; if (idx >= uec->num_ports) { dev_warn(uec->dev, "altmode port out of range: %d\n", idx); return; } - ret = typec_mux_set(port->typec_mux, &state); + ret = typec_mux_set(port->typec_mux, &port->state); if (ret) dev_err(uec->dev, "failed to set mux %d\n", ret); diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index faf61c9c6a98..5ef0e7d9b23a 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -38,7 +38,6 @@ struct vep { struct vrequest { struct usb_request req; - struct vudc *udc; struct list_head req_entry; /* Request queue */ }; diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index c5f079c5a1ea..5ef88117965d 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -333,7 +333,6 @@ static int vep_queue(struct usb_ep *_ep, struct usb_request *_req, static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct vep *ep; - struct vrequest *req; struct vudc *udc; struct vrequest *lst; unsigned long flags; @@ -343,8 +342,7 @@ static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) return ret; ep = to_vep(_ep); - req = to_vrequest(_req); - udc = req->udc; + udc = ep_to_vudc(ep); if (!udc->driver) return -ESHUTDOWN; diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 7aa1c4b21111..7c66b8840d88 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -207,6 +207,14 @@ config BACKLIGHT_KTZ8866 Say Y to enable the backlight driver for the Kinetic KTZ8866 found in Xiaomi Mi Pad 5 series. +config BACKLIGHT_SY7758 + tristate "Backlight Driver for Silergy SY7758" + depends on I2C + select REGMAP_I2C + help + Say Y to enable the backlight driver for the Silergy SY7758 + backlight controller found in Ayaneo Pocket S2. + config BACKLIGHT_LM3533 tristate "Backlight Driver for LM3533" depends on MFD_LM3533 diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 21c8313cfb12..34469711c6cd 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o obj-$(CONFIG_BACKLIGHT_QCOM_WLED) += qcom-wled.o obj-$(CONFIG_BACKLIGHT_RT4831) += rt4831-backlight.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +obj-$(CONFIG_BACKLIGHT_SY7758) += sy7758.o obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o diff --git a/drivers/video/backlight/sy7758.c b/drivers/video/backlight/sy7758.c new file mode 100644 index 000000000000..786589cb8df9 --- /dev/null +++ b/drivers/video/backlight/sy7758.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Silergy SY7758 6-channel High Efficiency LED Driver + * + * Copyright (C) 2025 Kancy Joe <kancy2333@outlook.com> + * Copyright (C) 2026 Linaro Limited + * Author: Neil Armstrong <neil.armstrong@linaro.org> + */ +#include <linux/backlight.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/err.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include <linux/bitfield.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +#define DEFAULT_BRIGHTNESS 1024 +#define MAX_BRIGHTNESS 4080 +#define REG_MAX 0xAE + +/* Registers */ +#define REG_DEV_CTL 0x01 +#define REG_DEV_ID 0x03 +#define REG_BRT_12BIT_L 0x10 +#define REG_BRT_12BIT_H 0x11 + +/* OTP memory */ +#define REG_OTP_CFG0 0xA0 +#define REG_OTP_CFG1 0xA1 +#define REG_OTP_CFG2 0xA2 +#define REG_OTP_CFG5 0xA5 +#define REG_OTP_CFG9 0xA9 + +/* Fields */ +#define BIT_DEV_CTL_FAST BIT(7) +#define MSK_DEV_CTL_BRT_MODE GENMASK(2, 1) +#define BIT_DEV_CTL_BL_CTLB BIT(0) + +#define MSK_BRT_12BIT_L GENMASK(7, 0) +#define MSK_BRT_12BIT_H GENMASK(3, 0) + +#define MSK_CFG0_CURRENT_LOW GENMASK(7, 0) + +#define BIT_CFG1_PDET_STDBY BIT(7) +#define MSK_CFG1_CURRENT_MAX GENMASK(6, 4) +#define MSK_CFG1_CURRENT_HIGH GENMASK(3, 0) + +#define BIT_CFG2_UVLO_EN BIT(5) +#define BIT_CFG2_UVLO_TH BIT(4) +#define BIT_CFG2_BL_ON BIT(3) +#define BIT_CFG2_ISET_EN BIT(2) +#define BIT_CFG2_BST_ESET_EN BIT(1) + +#define BIT_CFG5_PWM_DIRECT BIT(7) +#define MSK_CFG5_PS_MODE GENMASK(6, 4) +#define MSK_CFG5_PWM_FREQ GENMASK(3, 0) + +#define MSK_CFG9_VBST_MAX GENMASK(7, 5) +#define BIT_CFG9_JUMP_EN BIT(4) +#define MSK_CFG9_JUMP_TH GENMASK(3, 2) +#define MSK_CFG9_JUMP_VOLTAGE GENMASK(1, 0) + +struct sy7758 { + struct i2c_client *client; + struct regmap *regmap; + struct gpio_desc *gpio; + struct backlight_device *bl; +}; + +static const struct regmap_config sy7758_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_MAX, +}; + +static int sy7758_backlight_update_status(struct backlight_device *backlight_dev) +{ + struct sy7758 *sydev = bl_get_data(backlight_dev); + unsigned int brightness = backlight_get_brightness(backlight_dev); + int ret; + + ret = regmap_write(sydev->regmap, REG_BRT_12BIT_L, + FIELD_PREP(MSK_BRT_12BIT_L, + brightness & 0xff)); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_BRT_12BIT_H, + FIELD_PREP(MSK_BRT_12BIT_H, + (brightness >> 8) & 0xf)); + if (ret) + return ret; + + return 0; +} + +static const struct backlight_ops sy7758_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = sy7758_backlight_update_status, +}; + +static int sy7758_init(struct sy7758 *sydev) +{ + int ret = 0; + + ret = regmap_write(sydev->regmap, REG_DEV_CTL, + BIT_DEV_CTL_FAST | BIT_DEV_CTL_BL_CTLB | + FIELD_PREP(MSK_DEV_CTL_BRT_MODE, 2)); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_BRT_12BIT_L, + FIELD_PREP(MSK_BRT_12BIT_L, + DEFAULT_BRIGHTNESS & 0xff)); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_BRT_12BIT_H, + FIELD_PREP(MSK_BRT_12BIT_H, + (DEFAULT_BRIGHTNESS >> 8))); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_OTP_CFG5, + FIELD_PREP(MSK_CFG5_PS_MODE, 6) | + FIELD_PREP(MSK_CFG5_PWM_FREQ, 4)); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_OTP_CFG0, + FIELD_PREP(MSK_CFG0_CURRENT_LOW, 85)); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_OTP_CFG1, + BIT_CFG1_PDET_STDBY | + FIELD_PREP(MSK_CFG1_CURRENT_MAX, 1) | + FIELD_PREP(MSK_CFG1_CURRENT_HIGH, 10)); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_OTP_CFG9, + FIELD_PREP(MSK_CFG9_VBST_MAX, 4)); + if (ret) + return ret; + + ret = regmap_write(sydev->regmap, REG_OTP_CFG2, + BIT_CFG2_BL_ON | BIT_CFG2_UVLO_EN); + if (ret) + return ret; + + return 0; +} + +static int sy7758_probe(struct i2c_client *client) +{ + struct backlight_properties props = { }; + struct device *dev = &client->dev; + struct sy7758 *sydev; + unsigned int dev_id; + int ret; + + sydev = devm_kzalloc(dev, sizeof(*sydev), GFP_KERNEL); + if (!sydev) + return -ENOMEM; + + i2c_set_clientdata(client, sydev); + + /* Initialize regmap */ + sydev->client = client; + sydev->regmap = devm_regmap_init_i2c(client, &sy7758_regmap_config); + if (IS_ERR(sydev->regmap)) + return dev_err_probe(dev, PTR_ERR(sydev->regmap), + "failed to init regmap\n"); + + /* Get and enable regulator */ + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get regulator\n"); + + fsleep(100); + + /* Get enable GPIO and set to high */ + sydev->gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(sydev->gpio)) + return dev_err_probe(dev, PTR_ERR(sydev->gpio), + "failed to get enable GPIO\n"); + + /* Let some time for HW to settle */ + fsleep(10000); + + /* try read and check device id */ + ret = regmap_read(sydev->regmap, REG_DEV_ID, &dev_id); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read device id\n"); + if (dev_id != 0x63) { + dev_err(dev, "unexpected device id: 0x%02x\n", dev_id); + return -ENODEV; + } + + /* Initialize and set default brightness */ + ret = sy7758_init(sydev); + if (ret) + return ret; + + props.type = BACKLIGHT_RAW; + props.max_brightness = MAX_BRIGHTNESS; + props.brightness = DEFAULT_BRIGHTNESS; + props.scale = BACKLIGHT_SCALE_LINEAR; + + sydev->bl = devm_backlight_device_register(dev, "sy7758-backlight", + dev, sydev, &sy7758_backlight_ops, + &props); + if (IS_ERR(sydev->bl)) + return dev_err_probe(dev, PTR_ERR(sydev->bl), + "failed to register backlight device\n"); + + return backlight_update_status(sydev->bl); +} + +static void sy7758_remove(struct i2c_client *client) +{ + struct sy7758 *sydev = i2c_get_clientdata(client); + + backlight_disable(sydev->bl); +} + +static const struct i2c_device_id sy7758_ids[] = { + { "sy7758" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sy7758_ids); + +static const struct of_device_id sy7758_match_table[] = { + { .compatible = "silergy,sy7758", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sy7758_match_table); + +static struct i2c_driver sy7758_driver = { + .driver = { + .name = "sy7758", + .of_match_table = sy7758_match_table, + }, + .probe = sy7758_probe, + .remove = sy7758_remove, + .id_table = sy7758_ids, +}; + +module_i2c_driver(sy7758_driver); + +MODULE_DESCRIPTION("Silergy SY7758 Backlight Driver"); +MODULE_AUTHOR("Kancy Joe <kancy2333@outlook.com>"); +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 08cb8612d41f..c93bc0d285f0 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -93,10 +93,19 @@ config WATCHDOG_PRETIMEOUT_GOV_SEL tristate depends on WATCHDOG_PRETIMEOUT_GOV default m - select WATCHDOG_PRETIMEOUT_GOV_PANIC if WATCHDOG_PRETIMEOUT_GOV_NOOP=n + select WATCHDOG_PRETIMEOUT_GOV_PANIC if \ + WATCHDOG_PRETIMEOUT_GOV_NOOP=n && WATCHDOG_PRETIMEOUT_GOV_DUMP=n if WATCHDOG_PRETIMEOUT_GOV +config WATCHDOG_PRETIMEOUT_GOV_DUMP + bool "Dump watchdog pretimeout governor" + depends on WATCHDOG_CORE=y + default WATCHDOG_CORE + help + Dump watchdog pretimeout governor, all cpu backtrace is + added to kernel log buffer. + config WATCHDOG_PRETIMEOUT_GOV_NOOP tristate "Noop watchdog pretimeout governor" depends on WATCHDOG_CORE @@ -121,6 +130,14 @@ choice The governor takes its action, if a watchdog is capable to report a pretimeout event. +config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_DUMP + bool "dump" + depends on WATCHDOG_PRETIMEOUT_GOV_DUMP + help + Use dump watchdog pretimeout governor by default. If dump + governor is selected by a user, dump all cpu backtrace to + the kernel log buffer and don't do any system changes. + config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP bool "noop" depends on WATCHDOG_PRETIMEOUT_GOV_NOOP diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index bc1d52220f22..598556f03bc3 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -11,6 +11,7 @@ watchdog-objs += watchdog_core.o watchdog_dev.o watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o watchdog-$(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT) += watchdog_hrtimer_pretimeout.o +obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_DUMP) += pretimeout_dump.o obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o diff --git a/drivers/watchdog/db8500_wdt.c b/drivers/watchdog/db8500_wdt.c index 97148ac0aa54..70ccea13288d 100644 --- a/drivers/watchdog/db8500_wdt.c +++ b/drivers/watchdog/db8500_wdt.c @@ -16,7 +16,7 @@ #include <linux/watchdog.h> #include <linux/platform_device.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #define WATCHDOG_TIMEOUT 600 /* 10 minutes */ @@ -37,24 +37,24 @@ MODULE_PARM_DESC(nowayout, static int db8500_wdt_start(struct watchdog_device *wdd) { - return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); + return db8500_prcmu_enable_a9wdog(PRCMU_WDOG_ALL); } static int db8500_wdt_stop(struct watchdog_device *wdd) { - return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); + return db8500_prcmu_disable_a9wdog(PRCMU_WDOG_ALL); } static int db8500_wdt_keepalive(struct watchdog_device *wdd) { - return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); + return db8500_prcmu_kick_a9wdog(PRCMU_WDOG_ALL); } static int db8500_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { db8500_wdt_stop(wdd); - prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + db8500_prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); db8500_wdt_start(wdd); return 0; @@ -91,10 +91,10 @@ static int db8500_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&db8500_wdt, nowayout); /* disable auto off on sleep */ - prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + db8500_prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); /* set HW initial value */ - prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + db8500_prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); ret = devm_watchdog_register_device(dev, &db8500_wdt); if (ret) @@ -110,9 +110,9 @@ static int db8500_wdt_suspend(struct platform_device *pdev, { if (watchdog_active(&db8500_wdt)) { db8500_wdt_stop(&db8500_wdt); - prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); + db8500_prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); - prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + db8500_prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); db8500_wdt_start(&db8500_wdt); } return 0; @@ -122,9 +122,9 @@ static int db8500_wdt_resume(struct platform_device *pdev) { if (watchdog_active(&db8500_wdt)) { db8500_wdt_stop(&db8500_wdt); - prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + db8500_prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); - prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + db8500_prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); db8500_wdt_start(&db8500_wdt); } return 0; diff --git a/drivers/watchdog/pretimeout_dump.c b/drivers/watchdog/pretimeout_dump.c new file mode 100644 index 000000000000..c5d3dac2606c --- /dev/null +++ b/drivers/watchdog/pretimeout_dump.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026 Google LLC + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/nmi.h> +#include <linux/watchdog.h> + +#include "watchdog_pretimeout.h" + +/** + * pretimeout_dump - Dump on watchdog pretimeout event + * @wdd: watchdog_device + * + * Dump all cpu backtrace on pretimeout event. + */ +static void pretimeout_dump(struct watchdog_device *wdd) +{ + pr_alert("watchdog%d: pretimeout event\n", wdd->id); + if (!trigger_all_cpu_backtrace()) + pr_alert("trigger_all_cpu_backtrace() isn't available\n"); +} + +static struct watchdog_governor watchdog_gov_dump = { + .name = "dump", + .pretimeout = pretimeout_dump, +}; + +static int __init watchdog_gov_dump_register(void) +{ + return watchdog_register_governor(&watchdog_gov_dump); +} + +static void __exit watchdog_gov_dump_unregister(void) +{ + watchdog_unregister_governor(&watchdog_gov_dump); +} +module_init(watchdog_gov_dump_register); +module_exit(watchdog_gov_dump_unregister); + +MODULE_AUTHOR("Tzung-Bi Shih <tzungbi@kernel.org>"); +MODULE_DESCRIPTION("Dump watchdog pretimeout governor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c index 19eb2ed2c7cb..02e09b9e396d 100644 --- a/drivers/watchdog/watchdog_pretimeout.c +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -167,6 +167,8 @@ void watchdog_unregister_governor(struct watchdog_governor *gov) } spin_lock_irq(&pretimeout_lock); + if (default_gov == gov) + default_gov = NULL; list_for_each_entry(p, &pretimeout_list, entry) if (p->wdd->gov == gov) p->wdd->gov = default_gov; diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h index a3f1abc68839..57aa790e2669 100644 --- a/drivers/watchdog/watchdog_pretimeout.h +++ b/drivers/watchdog/watchdog_pretimeout.h @@ -24,7 +24,9 @@ int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf); int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, const char *buf); -#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP) +#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_DUMP) +#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "dump" +#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP) #define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "noop" #elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC) #define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "panic" |
