diff options
Diffstat (limited to 'drivers')
618 files changed, 22323 insertions, 6890 deletions
diff --git a/drivers/acpi/acpi_configfs.c b/drivers/acpi/acpi_configfs.c index b58850389094..81bfc6197293 100644 --- a/drivers/acpi/acpi_configfs.c +++ b/drivers/acpi/acpi_configfs.c @@ -97,12 +97,12 @@ static ssize_t acpi_table_aml_read(struct config_item *cfg, CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE); -struct configfs_bin_attribute *acpi_table_bin_attrs[] = { +static struct configfs_bin_attribute *acpi_table_bin_attrs[] = { &acpi_table_attr_aml, NULL, }; -ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) +static ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) { struct acpi_table_header *h = get_header(cfg); @@ -112,7 +112,7 @@ ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->signature); } -ssize_t acpi_table_length_show(struct config_item *cfg, char *str) +static ssize_t acpi_table_length_show(struct config_item *cfg, char *str) { struct acpi_table_header *h = get_header(cfg); @@ -122,7 +122,7 @@ ssize_t acpi_table_length_show(struct config_item *cfg, char *str) return sprintf(str, "%d\n", h->length); } -ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) +static ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) { struct acpi_table_header *h = get_header(cfg); @@ -132,7 +132,7 @@ ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) return sprintf(str, "%d\n", h->revision); } -ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) +static ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) { struct acpi_table_header *h = get_header(cfg); @@ -142,7 +142,7 @@ ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id); } -ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) +static ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) { struct acpi_table_header *h = get_header(cfg); @@ -152,7 +152,7 @@ ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id); } -ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) +static ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) { struct acpi_table_header *h = get_header(cfg); @@ -162,7 +162,8 @@ ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) return sprintf(str, "%d\n", h->oem_revision); } -ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str) +static ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, + char *str) { struct acpi_table_header *h = get_header(cfg); @@ -172,8 +173,8 @@ ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str) return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->asl_compiler_id); } -ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, - char *str) +static ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, + char *str) { struct acpi_table_header *h = get_header(cfg); @@ -192,7 +193,7 @@ CONFIGFS_ATTR_RO(acpi_table_, oem_revision); CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id); CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision); -struct configfs_attribute *acpi_table_attrs[] = { +static struct configfs_attribute *acpi_table_attrs[] = { &acpi_table_attr_signature, &acpi_table_attr_length, &acpi_table_attr_revision, @@ -232,7 +233,7 @@ static void acpi_table_drop_item(struct config_group *group, acpi_tb_unload_table(table->index); } -struct configfs_group_operations acpi_table_group_ops = { +static struct configfs_group_operations acpi_table_group_ops = { .make_item = acpi_table_make_item, .drop_item = acpi_table_drop_item, }; diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 5f94c35d165f..1e2a10a06b9d 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -18,7 +18,7 @@ #include <linux/mutex.h> #include <linux/pci.h> #include <linux/platform_device.h> -#include <linux/platform_data/clk-lpss.h> +#include <linux/platform_data/x86/clk-lpss.h> #include <linux/platform_data/x86/pmc_atom.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index 545e91420cde..8940054d6250 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -202,11 +202,15 @@ static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, { struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; const union acpi_object *of_compatible, *obj; + acpi_status status; int len, count; int i, nval; char *c; - acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); + status = acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); + if (ACPI_FAILURE(status)) + return -ENODEV; + /* DT strings are all in lower case */ for (c = buf.pointer; *c != '\0'; c++) *c = tolower(*c); diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index e18ade5d74e9..5a389a4f4f65 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -55,6 +55,10 @@ static bool no_init_ars; module_param(no_init_ars, bool, 0644); MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time"); +static bool force_labels; +module_param(force_labels, bool, 0444); +MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods"); + LIST_HEAD(acpi_descs); DEFINE_MUTEX(acpi_desc_lock); @@ -415,7 +419,7 @@ static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd, if (call_pkg) { int i; - if (nfit_mem->family != call_pkg->nd_family) + if (nfit_mem && nfit_mem->family != call_pkg->nd_family) return -ENOTTY; for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++) @@ -424,6 +428,10 @@ static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd, return call_pkg->nd_command; } + /* In the !call_pkg case, bus commands == bus functions */ + if (!nfit_mem) + return cmd; + /* Linux ND commands == NVDIMM_FAMILY_INTEL function numbers */ if (nfit_mem->family == NVDIMM_FAMILY_INTEL) return cmd; @@ -454,17 +462,18 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, if (cmd_rc) *cmd_rc = -EINVAL; + if (cmd == ND_CMD_CALL) + call_pkg = buf; + func = cmd_to_func(nfit_mem, cmd, call_pkg); + if (func < 0) + return func; + if (nvdimm) { struct acpi_device *adev = nfit_mem->adev; if (!adev) return -ENOTTY; - if (cmd == ND_CMD_CALL) - call_pkg = buf; - func = cmd_to_func(nfit_mem, cmd, call_pkg); - if (func < 0) - return func; dimm_name = nvdimm_name(nvdimm); cmd_name = nvdimm_cmd_name(cmd); cmd_mask = nvdimm_cmd_mask(nvdimm); @@ -475,12 +484,9 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, } else { struct acpi_device *adev = to_acpi_dev(acpi_desc); - func = cmd; cmd_name = nvdimm_bus_cmd_name(cmd); cmd_mask = nd_desc->cmd_mask; - dsm_mask = cmd_mask; - if (cmd == ND_CMD_CALL) - dsm_mask = nd_desc->bus_dsm_mask; + dsm_mask = nd_desc->bus_dsm_mask; desc = nd_cmd_bus_desc(cmd); guid = to_nfit_uuid(NFIT_DEV_BUS); handle = adev->handle; @@ -554,6 +560,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, return -EINVAL; } + if (out_obj->type != ACPI_TYPE_BUFFER) { + dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n", + dimm_name, cmd_name, out_obj->type); + rc = -EINVAL; + goto out; + } + if (call_pkg) { call_pkg->nd_fw_size = out_obj->buffer.length; memcpy(call_pkg->nd_payload + call_pkg->nd_size_in, @@ -572,13 +585,6 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, return 0; } - if (out_obj->package.type != ACPI_TYPE_BUFFER) { - dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n", - dimm_name, cmd_name, out_obj->type); - rc = -EINVAL; - goto out; - } - dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name, cmd_name, out_obj->buffer.length); print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4, @@ -1317,19 +1323,30 @@ static ssize_t scrub_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nvdimm_bus_descriptor *nd_desc; + struct acpi_nfit_desc *acpi_desc; ssize_t rc = -ENXIO; + bool busy; device_lock(dev); nd_desc = dev_get_drvdata(dev); - if (nd_desc) { - struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); + if (!nd_desc) { + device_unlock(dev); + return rc; + } + acpi_desc = to_acpi_desc(nd_desc); - mutex_lock(&acpi_desc->init_mutex); - rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, - acpi_desc->scrub_busy - && !acpi_desc->cancel ? "+\n" : "\n"); - mutex_unlock(&acpi_desc->init_mutex); + mutex_lock(&acpi_desc->init_mutex); + busy = test_bit(ARS_BUSY, &acpi_desc->scrub_flags) + && !test_bit(ARS_CANCEL, &acpi_desc->scrub_flags); + rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, busy ? "+\n" : "\n"); + /* Allow an admin to poll the busy state at a higher rate */ + if (busy && capable(CAP_SYS_RAWIO) && !test_and_set_bit(ARS_POLL, + &acpi_desc->scrub_flags)) { + acpi_desc->scrub_tmo = 1; + mod_delayed_work(nfit_wq, &acpi_desc->dwork, HZ); } + + mutex_unlock(&acpi_desc->init_mutex); device_unlock(dev); return rc; } @@ -1759,14 +1776,14 @@ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method) __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) { + struct device *dev = &nfit_mem->adev->dev; struct nd_intel_smart smart = { 0 }; union acpi_object in_buf = { - .type = ACPI_TYPE_BUFFER, - .buffer.pointer = (char *) &smart, - .buffer.length = sizeof(smart), + .buffer.type = ACPI_TYPE_BUFFER, + .buffer.length = 0, }; union acpi_object in_obj = { - .type = ACPI_TYPE_PACKAGE, + .package.type = ACPI_TYPE_PACKAGE, .package.count = 1, .package.elements = &in_buf, }; @@ -1781,8 +1798,15 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) return; out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj); - if (!out_obj) + if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER + || out_obj->buffer.length < sizeof(smart)) { + dev_dbg(dev->parent, "%s: failed to retrieve initial health\n", + dev_name(dev)); + ACPI_FREE(out_obj); return; + } + memcpy(&smart, out_obj->buffer.pointer, sizeof(smart)); + ACPI_FREE(out_obj); if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) { if (smart.shutdown_state) @@ -1793,7 +1817,6 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags); nfit_mem->dirty_shutdown = smart.shutdown_count; } - ACPI_FREE(out_obj); } static void populate_shutdown_status(struct nfit_mem *nfit_mem) @@ -1861,9 +1884,17 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, dev_set_drvdata(&adev_dimm->dev, nfit_mem); /* - * Until standardization materializes we need to consider 4 - * different command sets. Note, that checking for function0 (bit0) - * tells us if any commands are reachable through this GUID. + * There are 4 "legacy" NVDIMM command sets + * (NVDIMM_FAMILY_{INTEL,MSFT,HPE1,HPE2}) that were created before + * an EFI working group was established to constrain this + * proliferation. The nfit driver probes for the supported command + * set by GUID. Note, if you're a platform developer looking to add + * a new command set to this probe, consider using an existing set, + * or otherwise seek approval to publish the command set at + * http://www.uefi.org/RFIC_LIST. + * + * Note, that checking for function0 (bit0) tells us if any commands + * are reachable through this GUID. */ for (i = 0; i <= NVDIMM_FAMILY_MAX; i++) if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) @@ -1886,6 +1917,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, dsm_mask &= ~(1 << 8); } else if (nfit_mem->family == NVDIMM_FAMILY_MSFT) { dsm_mask = 0xffffffff; + } else if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) { + dsm_mask = 0x1f; } else { dev_dbg(dev, "unknown dimm command family\n"); nfit_mem->family = -1; @@ -1915,18 +1948,32 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, | 1 << ND_CMD_SET_CONFIG_DATA; if (family == NVDIMM_FAMILY_INTEL && (dsm_mask & label_mask) == label_mask) - return 0; + /* skip _LS{I,R,W} enabling */; + else { + if (acpi_nvdimm_has_method(adev_dimm, "_LSI") + && acpi_nvdimm_has_method(adev_dimm, "_LSR")) { + dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); + set_bit(NFIT_MEM_LSR, &nfit_mem->flags); + } - if (acpi_nvdimm_has_method(adev_dimm, "_LSI") - && acpi_nvdimm_has_method(adev_dimm, "_LSR")) { - dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); - set_bit(NFIT_MEM_LSR, &nfit_mem->flags); - } + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags) + && acpi_nvdimm_has_method(adev_dimm, "_LSW")) { + dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); + set_bit(NFIT_MEM_LSW, &nfit_mem->flags); + } - if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags) - && acpi_nvdimm_has_method(adev_dimm, "_LSW")) { - dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); - set_bit(NFIT_MEM_LSW, &nfit_mem->flags); + /* + * Quirk read-only label configurations to preserve + * access to label-less namespaces by default. + */ + if (!test_bit(NFIT_MEM_LSW, &nfit_mem->flags) + && !force_labels) { + dev_dbg(dev, "%s: No _LSW, disable labels\n", + dev_name(&adev_dimm->dev)); + clear_bit(NFIT_MEM_LSR, &nfit_mem->flags); + } else + dev_dbg(dev, "%s: Force enable labels\n", + dev_name(&adev_dimm->dev)); } populate_shutdown_status(nfit_mem); @@ -2027,6 +2074,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK; } + /* Quirk to ignore LOCAL for labels on HYPERV DIMMs */ + if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) + set_bit(NDD_NOBLK, &flags); + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) { set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask); set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask); @@ -2050,7 +2101,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0) continue; - dev_info(acpi_desc->dev, "%s flags:%s%s%s%s%s\n", + dev_err(acpi_desc->dev, "Error found in NVDIMM %s flags:%s%s%s%s%s\n", nvdimm_name(nvdimm), mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "", mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"", @@ -2641,7 +2692,10 @@ static int ars_start(struct acpi_nfit_desc *acpi_desc, if (rc < 0) return rc; - return cmd_rc; + if (cmd_rc < 0) + return cmd_rc; + set_bit(ARS_VALID, &acpi_desc->scrub_flags); + return 0; } static int ars_continue(struct acpi_nfit_desc *acpi_desc) @@ -2651,11 +2705,11 @@ static int ars_continue(struct acpi_nfit_desc *acpi_desc) struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status; - memset(&ars_start, 0, sizeof(ars_start)); - ars_start.address = ars_status->restart_address; - ars_start.length = ars_status->restart_length; - ars_start.type = ars_status->type; - ars_start.flags = acpi_desc->ars_start_flags; + ars_start = (struct nd_cmd_ars_start) { + .address = ars_status->restart_address, + .length = ars_status->restart_length, + .type = ars_status->type, + }; rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start, sizeof(ars_start), &cmd_rc); if (rc < 0) @@ -2734,6 +2788,17 @@ static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc) */ if (ars_status->out_length < 44) return 0; + + /* + * Ignore potentially stale results that are only refreshed + * after a start-ARS event. + */ + if (!test_and_clear_bit(ARS_VALID, &acpi_desc->scrub_flags)) { + dev_dbg(acpi_desc->dev, "skip %d stale records\n", + ars_status->num_records); + return 0; + } + for (i = 0; i < ars_status->num_records; i++) { /* only process full records */ if (ars_status->out_length @@ -2891,11 +2956,15 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, ndr_desc->res = &res; ndr_desc->provider_data = nfit_spa; ndr_desc->attr_groups = acpi_nfit_region_attribute_groups; - if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) + if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) { ndr_desc->numa_node = acpi_map_pxm_to_online_node( spa->proximity_domain); - else + ndr_desc->target_node = acpi_map_pxm_to_node( + spa->proximity_domain); + } else { ndr_desc->numa_node = NUMA_NO_NODE; + ndr_desc->target_node = NUMA_NO_NODE; + } /* * Persistence domain bits are hierarchical, if @@ -3004,14 +3073,16 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc, { int rc; - if (no_init_ars || test_bit(ARS_FAILED, &nfit_spa->ars_state)) + if (test_bit(ARS_FAILED, &nfit_spa->ars_state)) return acpi_nfit_register_region(acpi_desc, nfit_spa); set_bit(ARS_REQ_SHORT, &nfit_spa->ars_state); - set_bit(ARS_REQ_LONG, &nfit_spa->ars_state); + if (!no_init_ars) + set_bit(ARS_REQ_LONG, &nfit_spa->ars_state); switch (acpi_nfit_query_poison(acpi_desc)) { case 0: + case -ENOSPC: case -EAGAIN: rc = ars_start(acpi_desc, nfit_spa, ARS_REQ_SHORT); /* shouldn't happen, try again later */ @@ -3036,7 +3107,6 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc, break; case -EBUSY: case -ENOMEM: - case -ENOSPC: /* * BIOS was using ARS, wait for it to complete (or * resources to become available) and then perform our @@ -3071,7 +3141,7 @@ static unsigned int __acpi_nfit_scrub(struct acpi_nfit_desc *acpi_desc, lockdep_assert_held(&acpi_desc->init_mutex); - if (acpi_desc->cancel) + if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags)) return 0; if (query_rc == -EBUSY) { @@ -3145,7 +3215,7 @@ static void __sched_ars(struct acpi_nfit_desc *acpi_desc, unsigned int tmo) { lockdep_assert_held(&acpi_desc->init_mutex); - acpi_desc->scrub_busy = 1; + set_bit(ARS_BUSY, &acpi_desc->scrub_flags); /* note this should only be set from within the workqueue */ if (tmo) acpi_desc->scrub_tmo = tmo; @@ -3161,7 +3231,7 @@ static void notify_ars_done(struct acpi_nfit_desc *acpi_desc) { lockdep_assert_held(&acpi_desc->init_mutex); - acpi_desc->scrub_busy = 0; + clear_bit(ARS_BUSY, &acpi_desc->scrub_flags); acpi_desc->scrub_count++; if (acpi_desc->scrub_count_state) sysfs_notify_dirent(acpi_desc->scrub_count_state); @@ -3182,6 +3252,7 @@ static void acpi_nfit_scrub(struct work_struct *work) else notify_ars_done(acpi_desc); memset(acpi_desc->ars_status, 0, acpi_desc->max_ars); + clear_bit(ARS_POLL, &acpi_desc->scrub_flags); mutex_unlock(&acpi_desc->init_mutex); } @@ -3216,6 +3287,7 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) struct nfit_spa *nfit_spa; int rc; + set_bit(ARS_VALID, &acpi_desc->scrub_flags); list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { switch (nfit_spa_type(nfit_spa->spa)) { case NFIT_SPA_VOLATILE: @@ -3450,7 +3522,7 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_spa; mutex_lock(&acpi_desc->init_mutex); - if (acpi_desc->cancel) { + if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags)) { mutex_unlock(&acpi_desc->init_mutex); return 0; } @@ -3529,7 +3601,7 @@ void acpi_nfit_shutdown(void *data) mutex_unlock(&acpi_desc_lock); mutex_lock(&acpi_desc->init_mutex); - acpi_desc->cancel = 1; + set_bit(ARS_CANCEL, &acpi_desc->scrub_flags); cancel_delayed_work_sync(&acpi_desc->dwork); mutex_unlock(&acpi_desc->init_mutex); @@ -3729,6 +3801,7 @@ static __init int nfit_init(void) guid_parse(UUID_NFIT_DIMM_N_HPE1, &nfit_uuid[NFIT_DEV_DIMM_N_HPE1]); guid_parse(UUID_NFIT_DIMM_N_HPE2, &nfit_uuid[NFIT_DEV_DIMM_N_HPE2]); guid_parse(UUID_NFIT_DIMM_N_MSFT, &nfit_uuid[NFIT_DEV_DIMM_N_MSFT]); + guid_parse(UUID_NFIT_DIMM_N_HYPERV, &nfit_uuid[NFIT_DEV_DIMM_N_HYPERV]); nfit_wq = create_singlethread_workqueue("nfit"); if (!nfit_wq) diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index 33691aecfcee..2f8cf2a11e3b 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -34,11 +34,14 @@ /* https://msdn.microsoft.com/library/windows/hardware/mt604741 */ #define UUID_NFIT_DIMM_N_MSFT "1ee68b36-d4bd-4a1a-9a16-4f8e53d46e05" +/* http://www.uefi.org/RFIC_LIST (see "Virtual NVDIMM 0x1901") */ +#define UUID_NFIT_DIMM_N_HYPERV "5746c5f2-a9a2-4264-ad0e-e4ddc9e09e80" + #define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ | ACPI_NFIT_MEM_NOT_ARMED | ACPI_NFIT_MEM_MAP_FAILED) -#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_MSFT +#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_HYPERV #define NVDIMM_STANDARD_CMDMASK \ (1 << ND_CMD_SMART | 1 << ND_CMD_SMART_THRESHOLD | 1 << ND_CMD_DIMM_FLAGS \ @@ -94,6 +97,7 @@ enum nfit_uuids { NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1, NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2, NFIT_DEV_DIMM_N_MSFT = NVDIMM_FAMILY_MSFT, + NFIT_DEV_DIMM_N_HYPERV = NVDIMM_FAMILY_HYPERV, NFIT_SPA_VOLATILE, NFIT_SPA_PM, NFIT_SPA_DCR, @@ -210,6 +214,13 @@ struct nfit_mem { int family; }; +enum scrub_flags { + ARS_BUSY, + ARS_CANCEL, + ARS_VALID, + ARS_POLL, +}; + struct acpi_nfit_desc { struct nvdimm_bus_descriptor nd_desc; struct acpi_table_header acpi_header; @@ -223,7 +234,6 @@ struct acpi_nfit_desc { struct list_head idts; struct nvdimm_bus *nvdimm_bus; struct device *dev; - u8 ars_start_flags; struct nd_cmd_ars_status *ars_status; struct nfit_spa *scrub_spa; struct delayed_work dwork; @@ -232,8 +242,7 @@ struct acpi_nfit_desc { unsigned int max_ars; unsigned int scrub_count; unsigned int scrub_mode; - unsigned int scrub_busy:1; - unsigned int cancel:1; + unsigned long scrub_flags; unsigned long dimm_cmd_force_en; unsigned long bus_cmd_force_en; unsigned long bus_nfit_cmd_force_en; diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 7bbbf8256a41..867f6e3f2b4f 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -84,6 +84,7 @@ int acpi_map_pxm_to_node(int pxm) return node; } +EXPORT_SYMBOL(acpi_map_pxm_to_node); /** * acpi_map_pxm_to_online_node - Map proximity ID to online node diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index ad31c50de3be..065c4fc245d1 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -209,6 +209,9 @@ static int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr, struct acpi_pptt_processor *cpu_node; u32 proc_sz; + if (table_hdr->revision > 1) + return (node->flags & ACPI_PPTT_ACPI_LEAF_NODE); + table_end = (unsigned long)table_hdr + table_hdr->length; node_entry = ACPI_PTR_DIFF(node, table_hdr); entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 41324f0b1bee..fa76f5e41b5c 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -648,26 +648,29 @@ static void acpi_global_event_handler(u32 event_type, acpi_handle device, } } -static int get_status(u32 index, acpi_event_status *status, +static int get_status(u32 index, acpi_event_status *ret, acpi_handle *handle) { - int result; + acpi_status status; if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) return -EINVAL; if (index < num_gpes) { - result = acpi_get_gpe_device(index, handle); - if (result) { + status = acpi_get_gpe_device(index, handle); + if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, "Invalid GPE 0x%x", index)); - return result; + return -ENXIO; } - result = acpi_get_gpe_status(*handle, index, status); - } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) - result = acpi_get_event_status(index - num_gpes, status); + status = acpi_get_gpe_status(*handle, index, ret); + } else { + status = acpi_get_event_status(index - num_gpes, ret); + } + if (ACPI_FAILURE(status)) + return -EIO; - return result; + return 0; } static ssize_t counter_show(struct kobject *kobj, diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 41b706403ef7..b4dae624b9af 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -26,19 +26,36 @@ #define to_amba_driver(d) container_of(d, struct amba_driver, drv) -static const struct amba_id * -amba_lookup(const struct amba_id *table, struct amba_device *dev) +/* called on periphid match and class 0x9 coresight device. */ +static int +amba_cs_uci_id_match(const struct amba_id *table, struct amba_device *dev) { int ret = 0; + struct amba_cs_uci_id *uci; + + uci = table->data; + /* no table data or zero mask - return match on periphid */ + if (!uci || (uci->devarch_mask == 0)) + return 1; + + /* test against read devtype and masked devarch value */ + ret = (dev->uci.devtype == uci->devtype) && + ((dev->uci.devarch & uci->devarch_mask) == uci->devarch); + return ret; +} + +static const struct amba_id * +amba_lookup(const struct amba_id *table, struct amba_device *dev) +{ while (table->mask) { - ret = (dev->periphid & table->mask) == table->id; - if (ret) - break; + if (((dev->periphid & table->mask) == table->id) && + ((dev->cid != CORESIGHT_CID) || + (amba_cs_uci_id_match(table, dev)))) + return table; table++; } - - return ret ? table : NULL; + return NULL; } static int amba_match(struct device *dev, struct device_driver *drv) @@ -399,10 +416,22 @@ static int amba_device_try_add(struct amba_device *dev, struct resource *parent) cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); + if (cid == CORESIGHT_CID) { + /* set the base to the start of the last 4k block */ + void __iomem *csbase = tmp + size - 4096; + + dev->uci.devarch = + readl(csbase + UCI_REG_DEVARCH_OFFSET); + dev->uci.devtype = + readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff; + } + amba_put_disable_pclk(dev); - if (cid == AMBA_CID || cid == CORESIGHT_CID) + if (cid == AMBA_CID || cid == CORESIGHT_CID) { dev->periphid = pid; + dev->cid = cid; + } if (!dev->periphid) ret = -ENODEV; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 048cbf7d5233..cb8347500ce2 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -88,6 +88,7 @@ unsigned long __weak memory_block_size_bytes(void) { return MIN_MEMORY_BLOCK_SIZE; } +EXPORT_SYMBOL_GPL(memory_block_size_bytes); static unsigned long get_memory_block_size(void) { diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2c334c01fc43..76c9969b7124 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -6,6 +6,8 @@ * This file is released under the GPLv2. */ +#define pr_fmt(fmt) "PM: " fmt + #include <linux/delay.h> #include <linux/kernel.h> #include <linux/io.h> @@ -457,19 +459,19 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) time_start = ktime_get(); ret = genpd->power_off(genpd); - if (ret == -EBUSY) + if (ret) return ret; elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) - return ret; + return 0; genpd->states[state_idx].power_off_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", genpd->name, "off", elapsed_ns); - return ret; + return 0; } /** @@ -1657,8 +1659,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); if (!list_empty(&subdomain->master_links) || subdomain->device_count) { - pr_warn("%s: unable to remove subdomain %s\n", genpd->name, - subdomain->name); + pr_warn("%s: unable to remove subdomain %s\n", + genpd->name, subdomain->name); ret = -EBUSY; goto out; } @@ -1766,8 +1768,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd, ret = genpd_set_default_power_state(genpd); if (ret) return ret; - } else if (!gov) { - pr_warn("%s : no governor for states\n", genpd->name); + } else if (!gov && genpd->state_count > 1) { + pr_warn("%s: no governor for states\n", genpd->name); } device_initialize(&genpd->dev); @@ -2514,7 +2516,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, &entry_latency); if (err) { pr_debug(" * %pOF missing entry-latency-us property\n", - state_node); + state_node); return -EINVAL; } @@ -2522,7 +2524,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, &exit_latency); if (err) { pr_debug(" * %pOF missing exit-latency-us property\n", - state_node); + state_node); return -EINVAL; } diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 99896fbf18e4..4d07e38a8247 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -128,7 +128,6 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, off_on_time_ns = genpd->states[state].power_off_latency_ns + genpd->states[state].power_on_latency_ns; - min_off_time_ns = -1; /* * Check if subdomains can be off for enough time. diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 5a8149829ab3..f80d298de3fa 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -17,6 +17,8 @@ * subsystem list maintains. */ +#define pr_fmt(fmt) "PM: " fmt + #include <linux/device.h> #include <linux/export.h> #include <linux/mutex.h> @@ -128,7 +130,7 @@ void device_pm_add(struct device *dev) if (device_pm_not_required(dev)) return; - pr_debug("PM: Adding info for %s:%s\n", + pr_debug("Adding info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); device_pm_check_callbacks(dev); mutex_lock(&dpm_list_mtx); @@ -149,7 +151,7 @@ void device_pm_remove(struct device *dev) if (device_pm_not_required(dev)) return; - pr_debug("PM: Removing info for %s:%s\n", + pr_debug("Removing info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); complete_all(&dev->power.completion); mutex_lock(&dpm_list_mtx); @@ -168,7 +170,7 @@ void device_pm_remove(struct device *dev) */ void device_pm_move_before(struct device *deva, struct device *devb) { - pr_debug("PM: Moving %s:%s before %s:%s\n", + pr_debug("Moving %s:%s before %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); /* Delete deva from dpm_list and reinsert before devb. */ @@ -182,7 +184,7 @@ void device_pm_move_before(struct device *deva, struct device *devb) */ void device_pm_move_after(struct device *deva, struct device *devb) { - pr_debug("PM: Moving %s:%s after %s:%s\n", + pr_debug("Moving %s:%s after %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); /* Delete deva from dpm_list and reinsert after devb. */ @@ -195,7 +197,7 @@ void device_pm_move_after(struct device *deva, struct device *devb) */ void device_pm_move_last(struct device *dev) { - pr_debug("PM: Moving %s:%s to end of list\n", + pr_debug("Moving %s:%s to end of list\n", dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); list_move_tail(&dev->power.entry, &dpm_list); } @@ -418,8 +420,8 @@ static void pm_dev_dbg(struct device *dev, pm_message_t state, const char *info) static void pm_dev_err(struct device *dev, pm_message_t state, const char *info, int error) { - printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n", - dev_name(dev), pm_verb(state.event), info, error); + pr_err("Device %s failed to %s%s: error %d\n", + dev_name(dev), pm_verb(state.event), info, error); } static void dpm_show_time(ktime_t starttime, pm_message_t state, int error, @@ -2022,8 +2024,7 @@ int dpm_prepare(pm_message_t state) error = 0; continue; } - printk(KERN_INFO "PM: Device %s not prepared " - "for power transition: code %d\n", + pr_info("Device %s not prepared for power transition: code %d\n", dev_name(dev), error); put_device(dev); break; @@ -2062,7 +2063,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_start); void __suspend_report_result(const char *function, void *fn, int ret) { if (ret) - printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); + pr_err("%s(): %pF returns %d\n", function, fn, ret); } EXPORT_SYMBOL_GPL(__suspend_report_result); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index c511def48b48..ec33fbdb919b 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -21,6 +21,7 @@ static inline void pm_runtime_early_init(struct device *dev) extern void pm_runtime_init(struct device *dev); extern void pm_runtime_reinit(struct device *dev); extern void pm_runtime_remove(struct device *dev); +extern u64 pm_runtime_active_time(struct device *dev); #define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0) #define WAKE_IRQ_DEDICATED_MANAGED BIT(1) diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 3382542b39b7..f80e402ef778 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -22,7 +22,7 @@ * per-device constraint data struct. * * Note about the per-device constraint data struct allocation: - * . The per-device constraints data struct ptr is tored into the device + * . The per-device constraints data struct ptr is stored into the device * dev_pm_info. * . To minimize the data usage by the per-device constraints, the data struct * is only allocated at the first call to dev_pm_qos_add_request. diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index a80dbf08a99c..977db40378b0 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -64,7 +64,7 @@ static int rpm_suspend(struct device *dev, int rpmflags); * runtime_status field is updated, to account the time in the old state * correctly. */ -void update_pm_runtime_accounting(struct device *dev) +static void update_pm_runtime_accounting(struct device *dev) { u64 now, last, delta; @@ -98,7 +98,7 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status) dev->power.runtime_status = status; } -u64 pm_runtime_suspended_time(struct device *dev) +static u64 rpm_get_accounted_time(struct device *dev, bool suspended) { u64 time; unsigned long flags; @@ -106,12 +106,22 @@ u64 pm_runtime_suspended_time(struct device *dev) spin_lock_irqsave(&dev->power.lock, flags); update_pm_runtime_accounting(dev); - time = dev->power.suspended_time; + time = suspended ? dev->power.suspended_time : dev->power.active_time; spin_unlock_irqrestore(&dev->power.lock, flags); return time; } + +u64 pm_runtime_active_time(struct device *dev) +{ + return rpm_get_accounted_time(dev, false); +} + +u64 pm_runtime_suspended_time(struct device *dev) +{ + return rpm_get_accounted_time(dev, true); +} EXPORT_SYMBOL_GPL(pm_runtime_suspended_time); /** diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index c6bf76124184..1226e441ddfe 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -125,13 +125,9 @@ static ssize_t runtime_active_time_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; - u64 tmp; - spin_lock_irq(&dev->power.lock); - update_pm_runtime_accounting(dev); - tmp = dev->power.active_time; + u64 tmp = pm_runtime_active_time(dev); do_div(tmp, NSEC_PER_MSEC); ret = sprintf(buf, "%llu\n", tmp); - spin_unlock_irq(&dev->power.lock); return ret; } @@ -141,13 +137,9 @@ static ssize_t runtime_suspended_time_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; - u64 tmp; - spin_lock_irq(&dev->power.lock); - update_pm_runtime_accounting(dev); - tmp = dev->power.suspended_time; + u64 tmp = pm_runtime_suspended_time(dev); do_div(tmp, NSEC_PER_MSEC); ret = sprintf(buf, "%llu\n", tmp); - spin_unlock_irq(&dev->power.lock); return ret; } diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index b11f47a1e819..2bd9d2c744ca 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -7,6 +7,8 @@ * devices may be working. */ +#define pr_fmt(fmt) "PM: " fmt + #include <linux/pm-trace.h> #include <linux/export.h> #include <linux/rtc.h> diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index f1fee72ed970..bb1ae175fae1 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -6,6 +6,8 @@ * This file is released under the GPLv2. */ +#define pr_fmt(fmt) "PM: " fmt + #include <linux/device.h> #include <linux/slab.h> #include <linux/sched/signal.h> @@ -106,23 +108,6 @@ struct wakeup_source *wakeup_source_create(const char *name) } EXPORT_SYMBOL_GPL(wakeup_source_create); -/** - * wakeup_source_drop - Prepare a struct wakeup_source object for destruction. - * @ws: Wakeup source to prepare for destruction. - * - * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never - * be run in parallel with this function for the same wakeup source object. - */ -void wakeup_source_drop(struct wakeup_source *ws) -{ - if (!ws) - return; - - del_timer_sync(&ws->timer); - __pm_relax(ws); -} -EXPORT_SYMBOL_GPL(wakeup_source_drop); - /* * Record wakeup_source statistics being deleted into a dummy wakeup_source. */ @@ -162,7 +147,7 @@ void wakeup_source_destroy(struct wakeup_source *ws) if (!ws) return; - wakeup_source_drop(ws); + __pm_relax(ws); wakeup_source_record(ws); kfree_const(ws->name); kfree(ws); @@ -205,6 +190,13 @@ void wakeup_source_remove(struct wakeup_source *ws) list_del_rcu(&ws->entry); raw_spin_unlock_irqrestore(&events_lock, flags); synchronize_srcu(&wakeup_srcu); + + del_timer_sync(&ws->timer); + /* + * Clear timer.function to make wakeup_source_not_registered() treat + * this wakeup source as not registered. + */ + ws->timer.function = NULL; } EXPORT_SYMBOL_GPL(wakeup_source_remove); @@ -853,7 +845,7 @@ bool pm_wakeup_pending(void) raw_spin_unlock_irqrestore(&events_lock, flags); if (ret) { - pr_debug("PM: Wakeup pending, aborting suspend\n"); + pr_debug("Wakeup pending, aborting suspend\n"); pm_print_active_wakeup_sources(); } diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 74088d8dbaf3..4ba967d65cf9 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -115,12 +115,14 @@ static int atomic_dec_return_safe(atomic_t *v) #define RBD_FEATURE_LAYERING (1ULL<<0) #define RBD_FEATURE_STRIPINGV2 (1ULL<<1) #define RBD_FEATURE_EXCLUSIVE_LOCK (1ULL<<2) +#define RBD_FEATURE_DEEP_FLATTEN (1ULL<<5) #define RBD_FEATURE_DATA_POOL (1ULL<<7) #define RBD_FEATURE_OPERATIONS (1ULL<<8) #define RBD_FEATURES_ALL (RBD_FEATURE_LAYERING | \ RBD_FEATURE_STRIPINGV2 | \ RBD_FEATURE_EXCLUSIVE_LOCK | \ + RBD_FEATURE_DEEP_FLATTEN | \ RBD_FEATURE_DATA_POOL | \ RBD_FEATURE_OPERATIONS) @@ -214,28 +216,40 @@ enum obj_operation_type { OBJ_OP_READ = 1, OBJ_OP_WRITE, OBJ_OP_DISCARD, + OBJ_OP_ZEROOUT, }; /* * Writes go through the following state machine to deal with * layering: * - * need copyup - * RBD_OBJ_WRITE_GUARD ---------------> RBD_OBJ_WRITE_COPYUP - * | ^ | - * v \------------------------------/ - * done - * ^ - * | - * RBD_OBJ_WRITE_FLAT + * . . . . . RBD_OBJ_WRITE_GUARD. . . . . . . . . . . . . . + * . | . + * . v . + * . RBD_OBJ_WRITE_READ_FROM_PARENT. . . . + * . | . . + * . v v (deep-copyup . + * (image . RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC . not needed) . + * flattened) v | . . + * . v . . + * . . . .RBD_OBJ_WRITE_COPYUP_OPS. . . . . (copyup . + * | not needed) v + * v . + * done . . . . . . . . . . . . . . . . . . + * ^ + * | + * RBD_OBJ_WRITE_FLAT * * Writes start in RBD_OBJ_WRITE_GUARD or _FLAT, depending on whether - * there is a parent or not. + * assert_exists guard is needed or not (in some cases it's not needed + * even if there is a parent). */ enum rbd_obj_write_state { RBD_OBJ_WRITE_FLAT = 1, RBD_OBJ_WRITE_GUARD, - RBD_OBJ_WRITE_COPYUP, + RBD_OBJ_WRITE_READ_FROM_PARENT, + RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC, + RBD_OBJ_WRITE_COPYUP_OPS, }; struct rbd_obj_request { @@ -291,7 +305,6 @@ struct rbd_img_request { int result; /* first nonzero obj_request result */ struct list_head object_extents; /* obj_req.ex structs */ - u32 obj_request_count; u32 pending_count; struct kref kref; @@ -421,6 +434,10 @@ static DEFINE_IDA(rbd_dev_id_ida); static struct workqueue_struct *rbd_wq; +static struct ceph_snap_context rbd_empty_snapc = { + .nref = REFCOUNT_INIT(1), +}; + /* * single-major requires >= 0.75 version of userspace rbd utility. */ @@ -732,6 +749,7 @@ static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts) */ enum { Opt_queue_depth, + Opt_alloc_size, Opt_lock_timeout, Opt_last_int, /* int args above */ @@ -748,6 +766,7 @@ enum { static match_table_t rbd_opts_tokens = { {Opt_queue_depth, "queue_depth=%d"}, + {Opt_alloc_size, "alloc_size=%d"}, {Opt_lock_timeout, "lock_timeout=%d"}, /* int args above */ {Opt_pool_ns, "_pool_ns=%s"}, @@ -764,6 +783,7 @@ static match_table_t rbd_opts_tokens = { struct rbd_options { int queue_depth; + int alloc_size; unsigned long lock_timeout; bool read_only; bool lock_on_read; @@ -772,6 +792,7 @@ struct rbd_options { }; #define RBD_QUEUE_DEPTH_DEFAULT BLKDEV_MAX_RQ +#define RBD_ALLOC_SIZE_DEFAULT (64 * 1024) #define RBD_LOCK_TIMEOUT_DEFAULT 0 /* no timeout */ #define RBD_READ_ONLY_DEFAULT false #define RBD_LOCK_ON_READ_DEFAULT false @@ -811,6 +832,17 @@ static int parse_rbd_opts_token(char *c, void *private) } pctx->opts->queue_depth = intval; break; + case Opt_alloc_size: + if (intval < 1) { + pr_err("alloc_size out of range\n"); + return -EINVAL; + } + if (!is_power_of_2(intval)) { + pr_err("alloc_size must be a power of 2\n"); + return -EINVAL; + } + pctx->opts->alloc_size = intval; + break; case Opt_lock_timeout: /* 0 is "wait forever" (i.e. infinite timeout) */ if (intval < 0 || intval > INT_MAX / 1000) { @@ -857,6 +889,8 @@ static char* obj_op_name(enum obj_operation_type op_type) return "write"; case OBJ_OP_DISCARD: return "discard"; + case OBJ_OP_ZEROOUT: + return "zeroout"; default: return "???"; } @@ -1344,7 +1378,6 @@ static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request, /* Image request now owns object's original reference */ obj_request->img_request = img_request; - img_request->obj_request_count++; img_request->pending_count++; dout("%s: img %p obj %p\n", __func__, img_request, obj_request); } @@ -1354,8 +1387,6 @@ static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request, { dout("%s: img %p obj %p\n", __func__, img_request, obj_request); list_del(&obj_request->ex.oe_item); - rbd_assert(img_request->obj_request_count > 0); - img_request->obj_request_count--; rbd_assert(obj_request->img_request == img_request); rbd_obj_request_put(obj_request); } @@ -1409,6 +1440,19 @@ static bool rbd_obj_is_tail(struct rbd_obj_request *obj_req) rbd_dev->layout.object_size; } +/* + * Must be called after rbd_obj_calc_img_extents(). + */ +static bool rbd_obj_copyup_enabled(struct rbd_obj_request *obj_req) +{ + if (!obj_req->num_img_extents || + (rbd_obj_is_entire(obj_req) && + !obj_req->img_request->snapc->num_snaps)) + return false; + + return true; +} + static u64 rbd_obj_img_extents_bytes(struct rbd_obj_request *obj_req) { return ceph_file_extents_bytes(obj_req->img_extents, @@ -1422,6 +1466,7 @@ static bool rbd_img_is_write(struct rbd_img_request *img_req) return false; case OBJ_OP_WRITE: case OBJ_OP_DISCARD: + case OBJ_OP_ZEROOUT: return true; default: BUG(); @@ -1470,18 +1515,16 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request) } static struct ceph_osd_request * -rbd_osd_req_create(struct rbd_obj_request *obj_req, unsigned int num_ops) +__rbd_osd_req_create(struct rbd_obj_request *obj_req, + struct ceph_snap_context *snapc, unsigned int num_ops) { - struct rbd_img_request *img_req = obj_req->img_request; - struct rbd_device *rbd_dev = img_req->rbd_dev; + struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev; struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; struct ceph_osd_request *req; const char *name_format = rbd_dev->image_format == 1 ? RBD_V1_DATA_FORMAT : RBD_V2_DATA_FORMAT; - req = ceph_osdc_alloc_request(osdc, - (rbd_img_is_write(img_req) ? img_req->snapc : NULL), - num_ops, false, GFP_NOIO); + req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false, GFP_NOIO); if (!req) return NULL; @@ -1506,6 +1549,13 @@ err_req: return NULL; } +static struct ceph_osd_request * +rbd_osd_req_create(struct rbd_obj_request *obj_req, unsigned int num_ops) +{ + return __rbd_osd_req_create(obj_req, obj_req->img_request->snapc, + num_ops); +} + static void rbd_osd_req_destroy(struct ceph_osd_request *osd_req) { ceph_osdc_put_request(osd_req); @@ -1671,7 +1721,6 @@ static void rbd_img_request_destroy(struct kref *kref) for_each_obj_request_safe(img_request, obj_request, next_obj_request) rbd_img_obj_request_del(img_request, obj_request); - rbd_assert(img_request->obj_request_count == 0); if (img_request_layered_test(img_request)) { img_request_layered_clear(img_request); @@ -1754,7 +1803,7 @@ static void rbd_osd_req_setup_data(struct rbd_obj_request *obj_req, u32 which) static int rbd_obj_setup_read(struct rbd_obj_request *obj_req) { - obj_req->osd_req = rbd_osd_req_create(obj_req, 1); + obj_req->osd_req = __rbd_osd_req_create(obj_req, NULL, 1); if (!obj_req->osd_req) return -ENOMEM; @@ -1790,6 +1839,11 @@ static int __rbd_obj_setup_stat(struct rbd_obj_request *obj_req, return 0; } +static int count_write_ops(struct rbd_obj_request *obj_req) +{ + return 2; /* setallochint + write/writefull */ +} + static void __rbd_obj_setup_write(struct rbd_obj_request *obj_req, unsigned int which) { @@ -1816,6 +1870,7 @@ static void __rbd_obj_setup_write(struct rbd_obj_request *obj_req, static int rbd_obj_setup_write(struct rbd_obj_request *obj_req) { unsigned int num_osd_ops, which = 0; + bool need_guard; int ret; /* reverse map the entire object onto the parent */ @@ -1823,47 +1878,112 @@ static int rbd_obj_setup_write(struct rbd_obj_request *obj_req) if (ret) return ret; - if (obj_req->num_img_extents) { - obj_req->write_state = RBD_OBJ_WRITE_GUARD; - num_osd_ops = 3; /* stat + setallochint + write/writefull */ - } else { - obj_req->write_state = RBD_OBJ_WRITE_FLAT; - num_osd_ops = 2; /* setallochint + write/writefull */ - } + need_guard = rbd_obj_copyup_enabled(obj_req); + num_osd_ops = need_guard + count_write_ops(obj_req); obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops); if (!obj_req->osd_req) return -ENOMEM; - if (obj_req->num_img_extents) { + if (need_guard) { ret = __rbd_obj_setup_stat(obj_req, which++); if (ret) return ret; + + obj_req->write_state = RBD_OBJ_WRITE_GUARD; + } else { + obj_req->write_state = RBD_OBJ_WRITE_FLAT; } __rbd_obj_setup_write(obj_req, which); return 0; } -static void __rbd_obj_setup_discard(struct rbd_obj_request *obj_req, +static u16 truncate_or_zero_opcode(struct rbd_obj_request *obj_req) +{ + return rbd_obj_is_tail(obj_req) ? CEPH_OSD_OP_TRUNCATE : + CEPH_OSD_OP_ZERO; +} + +static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req) +{ + struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev; + u64 off = obj_req->ex.oe_off; + u64 next_off = obj_req->ex.oe_off + obj_req->ex.oe_len; + int ret; + + /* + * Align the range to alloc_size boundary and punt on discards + * that are too small to free up any space. + * + * alloc_size == object_size && is_tail() is a special case for + * filestore with filestore_punch_hole = false, needed to allow + * truncate (in addition to delete). + */ + if (rbd_dev->opts->alloc_size != rbd_dev->layout.object_size || + !rbd_obj_is_tail(obj_req)) { + off = round_up(off, rbd_dev->opts->alloc_size); + next_off = round_down(next_off, rbd_dev->opts->alloc_size); + if (off >= next_off) + return 1; + } + + /* reverse map the entire object onto the parent */ + ret = rbd_obj_calc_img_extents(obj_req, true); + if (ret) + return ret; + + obj_req->osd_req = rbd_osd_req_create(obj_req, 1); + if (!obj_req->osd_req) + return -ENOMEM; + + if (rbd_obj_is_entire(obj_req) && !obj_req->num_img_extents) { + osd_req_op_init(obj_req->osd_req, 0, CEPH_OSD_OP_DELETE, 0); + } else { + dout("%s %p %llu~%llu -> %llu~%llu\n", __func__, + obj_req, obj_req->ex.oe_off, obj_req->ex.oe_len, + off, next_off - off); + osd_req_op_extent_init(obj_req->osd_req, 0, + truncate_or_zero_opcode(obj_req), + off, next_off - off, 0, 0); + } + + obj_req->write_state = RBD_OBJ_WRITE_FLAT; + rbd_osd_req_format_write(obj_req); + return 0; +} + +static int count_zeroout_ops(struct rbd_obj_request *obj_req) +{ + int num_osd_ops; + + if (rbd_obj_is_entire(obj_req) && obj_req->num_img_extents && + !rbd_obj_copyup_enabled(obj_req)) + num_osd_ops = 2; /* create + truncate */ + else + num_osd_ops = 1; /* delete/truncate/zero */ + + return num_osd_ops; +} + +static void __rbd_obj_setup_zeroout(struct rbd_obj_request *obj_req, unsigned int which) { u16 opcode; if (rbd_obj_is_entire(obj_req)) { if (obj_req->num_img_extents) { - osd_req_op_init(obj_req->osd_req, which++, - CEPH_OSD_OP_CREATE, 0); + if (!rbd_obj_copyup_enabled(obj_req)) + osd_req_op_init(obj_req->osd_req, which++, + CEPH_OSD_OP_CREATE, 0); opcode = CEPH_OSD_OP_TRUNCATE; } else { osd_req_op_init(obj_req->osd_req, which++, CEPH_OSD_OP_DELETE, 0); opcode = 0; } - } else if (rbd_obj_is_tail(obj_req)) { - opcode = CEPH_OSD_OP_TRUNCATE; } else { - opcode = CEPH_OSD_OP_ZERO; + opcode = truncate_or_zero_opcode(obj_req); } if (opcode) @@ -1875,9 +1995,10 @@ static void __rbd_obj_setup_discard(struct rbd_obj_request *obj_req, rbd_osd_req_format_write(obj_req); } -static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req) +static int rbd_obj_setup_zeroout(struct rbd_obj_request *obj_req) { unsigned int num_osd_ops, which = 0; + bool need_guard; int ret; /* reverse map the entire object onto the parent */ @@ -1885,33 +2006,24 @@ static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req) if (ret) return ret; - if (rbd_obj_is_entire(obj_req)) { - obj_req->write_state = RBD_OBJ_WRITE_FLAT; - if (obj_req->num_img_extents) - num_osd_ops = 2; /* create + truncate */ - else - num_osd_ops = 1; /* delete */ - } else { - if (obj_req->num_img_extents) { - obj_req->write_state = RBD_OBJ_WRITE_GUARD; - num_osd_ops = 2; /* stat + truncate/zero */ - } else { - obj_req->write_state = RBD_OBJ_WRITE_FLAT; - num_osd_ops = 1; /* truncate/zero */ - } - } + need_guard = rbd_obj_copyup_enabled(obj_req); + num_osd_ops = need_guard + count_zeroout_ops(obj_req); obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops); if (!obj_req->osd_req) return -ENOMEM; - if (!rbd_obj_is_entire(obj_req) && obj_req->num_img_extents) { + if (need_guard) { ret = __rbd_obj_setup_stat(obj_req, which++); if (ret) return ret; + + obj_req->write_state = RBD_OBJ_WRITE_GUARD; + } else { + obj_req->write_state = RBD_OBJ_WRITE_FLAT; } - __rbd_obj_setup_discard(obj_req, which); + __rbd_obj_setup_zeroout(obj_req, which); return 0; } @@ -1922,10 +2034,10 @@ static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req) */ static int __rbd_img_fill_request(struct rbd_img_request *img_req) { - struct rbd_obj_request *obj_req; + struct rbd_obj_request *obj_req, *next_obj_req; int ret; - for_each_obj_request(img_req, obj_req) { + for_each_obj_request_safe(img_req, obj_req, next_obj_req) { switch (img_req->op_type) { case OBJ_OP_READ: ret = rbd_obj_setup_read(obj_req); @@ -1936,11 +2048,20 @@ static int __rbd_img_fill_request(struct rbd_img_request *img_req) case OBJ_OP_DISCARD: ret = rbd_obj_setup_discard(obj_req); break; + case OBJ_OP_ZEROOUT: + ret = rbd_obj_setup_zeroout(obj_req); + break; default: rbd_assert(0); } - if (ret) + if (ret < 0) return ret; + if (ret > 0) { + img_req->xferred += obj_req->ex.oe_len; + img_req->pending_count--; + rbd_img_obj_request_del(img_req, obj_req); + continue; + } ret = ceph_osdc_alloc_messages(obj_req->osd_req, GFP_NOIO); if (ret) @@ -2356,21 +2477,19 @@ static bool is_zero_bvecs(struct bio_vec *bvecs, u32 bytes) return true; } -static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes) +#define MODS_ONLY U32_MAX + +static int rbd_obj_issue_copyup_empty_snapc(struct rbd_obj_request *obj_req, + u32 bytes) { - unsigned int num_osd_ops = obj_req->osd_req->r_num_ops; int ret; dout("%s obj_req %p bytes %u\n", __func__, obj_req, bytes); rbd_assert(obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_STAT); + rbd_assert(bytes > 0 && bytes != MODS_ONLY); rbd_osd_req_destroy(obj_req->osd_req); - /* - * Create a copyup request with the same number of OSD ops as - * the original request. The original request was stat + op(s), - * the new copyup request will be copyup + the same op(s). - */ - obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops); + obj_req->osd_req = __rbd_osd_req_create(obj_req, &rbd_empty_snapc, 1); if (!obj_req->osd_req) return -ENOMEM; @@ -2378,27 +2497,65 @@ static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes) if (ret) return ret; - /* - * Only send non-zero copyup data to save some I/O and network - * bandwidth -- zero copyup data is equivalent to the object not - * existing. - */ - if (is_zero_bvecs(obj_req->copyup_bvecs, bytes)) { - dout("%s obj_req %p detected zeroes\n", __func__, obj_req); - bytes = 0; - } osd_req_op_cls_request_data_bvecs(obj_req->osd_req, 0, obj_req->copyup_bvecs, obj_req->copyup_bvec_count, bytes); + rbd_osd_req_format_write(obj_req); - switch (obj_req->img_request->op_type) { + ret = ceph_osdc_alloc_messages(obj_req->osd_req, GFP_NOIO); + if (ret) + return ret; + + rbd_obj_request_submit(obj_req); + return 0; +} + +static int rbd_obj_issue_copyup_ops(struct rbd_obj_request *obj_req, u32 bytes) +{ + struct rbd_img_request *img_req = obj_req->img_request; + unsigned int num_osd_ops = (bytes != MODS_ONLY); + unsigned int which = 0; + int ret; + + dout("%s obj_req %p bytes %u\n", __func__, obj_req, bytes); + rbd_assert(obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_STAT || + obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_CALL); + rbd_osd_req_destroy(obj_req->osd_req); + + switch (img_req->op_type) { case OBJ_OP_WRITE: - __rbd_obj_setup_write(obj_req, 1); + num_osd_ops += count_write_ops(obj_req); break; - case OBJ_OP_DISCARD: - rbd_assert(!rbd_obj_is_entire(obj_req)); - __rbd_obj_setup_discard(obj_req, 1); + case OBJ_OP_ZEROOUT: + num_osd_ops += count_zeroout_ops(obj_req); + break; + default: + rbd_assert(0); + } + + obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops); + if (!obj_req->osd_req) + return -ENOMEM; + + if (bytes != MODS_ONLY) { + ret = osd_req_op_cls_init(obj_req->osd_req, which, "rbd", + "copyup"); + if (ret) + return ret; + + osd_req_op_cls_request_data_bvecs(obj_req->osd_req, which++, + obj_req->copyup_bvecs, + obj_req->copyup_bvec_count, + bytes); + } + + switch (img_req->op_type) { + case OBJ_OP_WRITE: + __rbd_obj_setup_write(obj_req, which); + break; + case OBJ_OP_ZEROOUT: + __rbd_obj_setup_zeroout(obj_req, which); break; default: rbd_assert(0); @@ -2412,6 +2569,33 @@ static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes) return 0; } +static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes) +{ + /* + * Only send non-zero copyup data to save some I/O and network + * bandwidth -- zero copyup data is equivalent to the object not + * existing. + */ + if (is_zero_bvecs(obj_req->copyup_bvecs, bytes)) { + dout("%s obj_req %p detected zeroes\n", __func__, obj_req); + bytes = 0; + } + + if (obj_req->img_request->snapc->num_snaps && bytes > 0) { + /* + * Send a copyup request with an empty snapshot context to + * deep-copyup the object through all existing snapshots. + * A second request with the current snapshot context will be + * sent for the actual modification. + */ + obj_req->write_state = RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC; + return rbd_obj_issue_copyup_empty_snapc(obj_req, bytes); + } + + obj_req->write_state = RBD_OBJ_WRITE_COPYUP_OPS; + return rbd_obj_issue_copyup_ops(obj_req, bytes); +} + static int setup_copyup_bvecs(struct rbd_obj_request *obj_req, u64 obj_overlap) { u32 i; @@ -2451,22 +2635,19 @@ static int rbd_obj_handle_write_guard(struct rbd_obj_request *obj_req) if (!obj_req->num_img_extents) { /* * The overlap has become 0 (most likely because the - * image has been flattened). Use rbd_obj_issue_copyup() - * to re-submit the original write request -- the copyup - * operation itself will be a no-op, since someone must - * have populated the child object while we weren't - * looking. Move to WRITE_FLAT state as we'll be done - * with the operation once the null copyup completes. + * image has been flattened). Re-submit the original write + * request -- pass MODS_ONLY since the copyup isn't needed + * anymore. */ - obj_req->write_state = RBD_OBJ_WRITE_FLAT; - return rbd_obj_issue_copyup(obj_req, 0); + obj_req->write_state = RBD_OBJ_WRITE_COPYUP_OPS; + return rbd_obj_issue_copyup_ops(obj_req, MODS_ONLY); } ret = setup_copyup_bvecs(obj_req, rbd_obj_img_extents_bytes(obj_req)); if (ret) return ret; - obj_req->write_state = RBD_OBJ_WRITE_COPYUP; + obj_req->write_state = RBD_OBJ_WRITE_READ_FROM_PARENT; return rbd_obj_read_from_parent(obj_req); } @@ -2474,7 +2655,6 @@ static bool rbd_obj_handle_write(struct rbd_obj_request *obj_req) { int ret; -again: switch (obj_req->write_state) { case RBD_OBJ_WRITE_GUARD: rbd_assert(!obj_req->xferred); @@ -2493,6 +2673,7 @@ again: } /* fall through */ case RBD_OBJ_WRITE_FLAT: + case RBD_OBJ_WRITE_COPYUP_OPS: if (!obj_req->result) /* * There is no such thing as a successful short @@ -2500,15 +2681,26 @@ again: */ obj_req->xferred = obj_req->ex.oe_len; return true; - case RBD_OBJ_WRITE_COPYUP: - obj_req->write_state = RBD_OBJ_WRITE_GUARD; + case RBD_OBJ_WRITE_READ_FROM_PARENT: if (obj_req->result) - goto again; + return true; rbd_assert(obj_req->xferred); ret = rbd_obj_issue_copyup(obj_req, obj_req->xferred); if (ret) { obj_req->result = ret; + obj_req->xferred = 0; + return true; + } + return false; + case RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC: + if (obj_req->result) + return true; + + obj_req->write_state = RBD_OBJ_WRITE_COPYUP_OPS; + ret = rbd_obj_issue_copyup_ops(obj_req, MODS_ONLY); + if (ret) { + obj_req->result = ret; return true; } return false; @@ -2528,6 +2720,7 @@ static bool __rbd_obj_handle_request(struct rbd_obj_request *obj_req) case OBJ_OP_WRITE: return rbd_obj_handle_write(obj_req); case OBJ_OP_DISCARD: + case OBJ_OP_ZEROOUT: if (rbd_obj_handle_write(obj_req)) { /* * Hide -ENOENT from delete/truncate/zero -- discarding @@ -3640,9 +3833,11 @@ static void rbd_queue_workfn(struct work_struct *work) switch (req_op(rq)) { case REQ_OP_DISCARD: - case REQ_OP_WRITE_ZEROES: op_type = OBJ_OP_DISCARD; break; + case REQ_OP_WRITE_ZEROES: + op_type = OBJ_OP_ZEROOUT; + break; case REQ_OP_WRITE: op_type = OBJ_OP_WRITE; break; @@ -3722,12 +3917,12 @@ static void rbd_queue_workfn(struct work_struct *work) img_request->rq = rq; snapc = NULL; /* img_request consumes a ref */ - if (op_type == OBJ_OP_DISCARD) + if (op_type == OBJ_OP_DISCARD || op_type == OBJ_OP_ZEROOUT) result = rbd_img_fill_nodata(img_request, offset, length); else result = rbd_img_fill_from_bio(img_request, offset, length, rq->bio); - if (result) + if (result || !img_request->pending_count) goto err_img_request; rbd_img_request_submit(img_request); @@ -5388,6 +5583,7 @@ static int rbd_add_parse_args(const char *buf, pctx.opts->read_only = RBD_READ_ONLY_DEFAULT; pctx.opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT; + pctx.opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT; pctx.opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT; pctx.opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT; pctx.opts->exclusive = RBD_EXCLUSIVE_DEFAULT; @@ -5795,14 +5991,6 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) ret = rbd_dev_v2_parent_info(rbd_dev); if (ret) goto err_out_probe; - - /* - * Need to warn users if this image is the one being - * mapped and has a parent. - */ - if (!depth && rbd_dev->parent_spec) - rbd_warn(rbd_dev, - "WARNING: kernel layering is EXPERIMENTAL!"); } ret = rbd_dev_probe_parent(rbd_dev, depth); @@ -5885,6 +6073,12 @@ static ssize_t do_rbd_add(struct bus_type *bus, if (rbd_dev->spec->snap_id != CEPH_NOSNAP) rbd_dev->opts->read_only = true; + if (rbd_dev->opts->alloc_size > rbd_dev->layout.object_size) { + rbd_warn(rbd_dev, "alloc_size adjusted to %u", + rbd_dev->layout.object_size); + rbd_dev->opts->alloc_size = rbd_dev->layout.object_size; + } + rc = rbd_dev_device_setup(rbd_dev); if (rc) goto err_out_image_probe; diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index a4bc74e72c39..24896ffb04ed 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -926,7 +926,7 @@ static int read_per_ring_refs(struct xen_blkif_ring *ring, const char *dir) int err, i, j; struct xen_blkif *blkif = ring->blkif; struct xenbus_device *dev = blkif->be->dev; - unsigned int ring_page_order, nr_grefs, evtchn; + unsigned int nr_grefs, evtchn; err = xenbus_scanf(XBT_NIL, dir, "event-channel", "%u", &evtchn); @@ -936,43 +936,42 @@ static int read_per_ring_refs(struct xen_blkif_ring *ring, const char *dir) return err; } - err = xenbus_scanf(XBT_NIL, dev->otherend, "ring-page-order", "%u", - &ring_page_order); - if (err != 1) { - err = xenbus_scanf(XBT_NIL, dir, "ring-ref", "%u", &ring_ref[0]); + nr_grefs = blkif->nr_ring_pages; + + if (unlikely(!nr_grefs)) { + WARN_ON(true); + return -EINVAL; + } + + for (i = 0; i < nr_grefs; i++) { + char ring_ref_name[RINGREF_NAME_LEN]; + + snprintf(ring_ref_name, RINGREF_NAME_LEN, "ring-ref%u", i); + err = xenbus_scanf(XBT_NIL, dir, ring_ref_name, + "%u", &ring_ref[i]); + if (err != 1) { + if (nr_grefs == 1) + break; + err = -EINVAL; - xenbus_dev_fatal(dev, err, "reading %s/ring-ref", dir); + xenbus_dev_fatal(dev, err, "reading %s/%s", + dir, ring_ref_name); return err; } - nr_grefs = 1; - } else { - unsigned int i; + } - if (ring_page_order > xen_blkif_max_ring_order) { + if (err != 1) { + WARN_ON(nr_grefs != 1); + + err = xenbus_scanf(XBT_NIL, dir, "ring-ref", "%u", + &ring_ref[0]); + if (err != 1) { err = -EINVAL; - xenbus_dev_fatal(dev, err, "%s/request %d ring page order exceed max:%d", - dir, ring_page_order, - xen_blkif_max_ring_order); + xenbus_dev_fatal(dev, err, "reading %s/ring-ref", dir); return err; } - - nr_grefs = 1 << ring_page_order; - for (i = 0; i < nr_grefs; i++) { - char ring_ref_name[RINGREF_NAME_LEN]; - - snprintf(ring_ref_name, RINGREF_NAME_LEN, "ring-ref%u", i); - err = xenbus_scanf(XBT_NIL, dir, ring_ref_name, - "%u", &ring_ref[i]); - if (err != 1) { - err = -EINVAL; - xenbus_dev_fatal(dev, err, "reading %s/%s", - dir, ring_ref_name); - return err; - } - } } - blkif->nr_ring_pages = nr_grefs; for (i = 0; i < nr_grefs * XEN_BLKIF_REQS_PER_PAGE; i++) { req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -1023,6 +1022,7 @@ fail: static int connect_ring(struct backend_info *be) { struct xenbus_device *dev = be->dev; + struct xen_blkif *blkif = be->blkif; unsigned int pers_grants; char protocol[64] = ""; int err, i; @@ -1030,28 +1030,29 @@ static int connect_ring(struct backend_info *be) size_t xspathsize; const size_t xenstore_path_ext_size = 11; /* sufficient for "/queue-NNN" */ unsigned int requested_num_queues = 0; + unsigned int ring_page_order; pr_debug("%s %s\n", __func__, dev->otherend); - be->blkif->blk_protocol = BLKIF_PROTOCOL_DEFAULT; + blkif->blk_protocol = BLKIF_PROTOCOL_DEFAULT; err = xenbus_scanf(XBT_NIL, dev->otherend, "protocol", "%63s", protocol); if (err <= 0) strcpy(protocol, "unspecified, assuming default"); else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE)) - be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; + blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) - be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; + blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) - be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; + blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; else { xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol); return -ENOSYS; } pers_grants = xenbus_read_unsigned(dev->otherend, "feature-persistent", 0); - be->blkif->vbd.feature_gnt_persistent = pers_grants; - be->blkif->vbd.overflow_max_grants = 0; + blkif->vbd.feature_gnt_persistent = pers_grants; + blkif->vbd.overflow_max_grants = 0; /* * Read the number of hardware queues from frontend. @@ -1067,16 +1068,30 @@ static int connect_ring(struct backend_info *be) requested_num_queues, xenblk_max_queues); return -ENOSYS; } - be->blkif->nr_rings = requested_num_queues; - if (xen_blkif_alloc_rings(be->blkif)) + blkif->nr_rings = requested_num_queues; + if (xen_blkif_alloc_rings(blkif)) return -ENOMEM; pr_info("%s: using %d queues, protocol %d (%s) %s\n", dev->nodename, - be->blkif->nr_rings, be->blkif->blk_protocol, protocol, + blkif->nr_rings, blkif->blk_protocol, protocol, pers_grants ? "persistent grants" : ""); - if (be->blkif->nr_rings == 1) - return read_per_ring_refs(&be->blkif->rings[0], dev->otherend); + ring_page_order = xenbus_read_unsigned(dev->otherend, + "ring-page-order", 0); + + if (ring_page_order > xen_blkif_max_ring_order) { + err = -EINVAL; + xenbus_dev_fatal(dev, err, + "requested ring page order %d exceed max:%d", + ring_page_order, + xen_blkif_max_ring_order); + return err; + } + + blkif->nr_ring_pages = 1 << ring_page_order; + + if (blkif->nr_rings == 1) + return read_per_ring_refs(&blkif->rings[0], dev->otherend); else { xspathsize = strlen(dev->otherend) + xenstore_path_ext_size; xspath = kmalloc(xspathsize, GFP_KERNEL); @@ -1085,10 +1100,10 @@ static int connect_ring(struct backend_info *be) return -ENOMEM; } - for (i = 0; i < be->blkif->nr_rings; i++) { + for (i = 0; i < blkif->nr_rings; i++) { memset(xspath, 0, xspathsize); snprintf(xspath, xspathsize, "%s/queue-%u", dev->otherend, i); - err = read_per_ring_refs(&be->blkif->rings[i], xspath); + err = read_per_ring_refs(&blkif->rings[i], xspath); if (err) { kfree(xspath); return err; diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 04ca65912638..e7a5f1d1c314 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -41,7 +41,7 @@ static DEFINE_IDR(zram_index_idr); static DEFINE_MUTEX(zram_index_mutex); static int zram_major; -static const char *default_compressor = "lzo"; +static const char *default_compressor = "lzo-rle"; /* Module params (documentation at end) */ static unsigned int num_devices = 1; diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index 58c84784ba25..bfdff9271be0 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -74,7 +74,7 @@ static const char* tcpa_pc_event_id_strings[] = { /* returns pointer to start of pos. entry of tcg log */ static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos) { - loff_t i; + loff_t i = 0; struct tpm_chip *chip = m->private; struct tpm_bios_log *log = &chip->log; void *addr = log->bios_event_log; @@ -83,38 +83,29 @@ static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos) u32 converted_event_size; u32 converted_event_type; - /* read over *pos measurements */ - for (i = 0; i < *pos; i++) { + do { event = addr; + /* check if current entry is valid */ + if (addr + sizeof(struct tcpa_event) > limit) + return NULL; + converted_event_size = do_endian_conversion(event->event_size); converted_event_type = do_endian_conversion(event->event_type); - if ((addr + sizeof(struct tcpa_event)) < limit) { - if ((converted_event_type == 0) && - (converted_event_size == 0)) - return NULL; - addr += (sizeof(struct tcpa_event) + - converted_event_size); - } - } - - /* now check if current entry is valid */ - if ((addr + sizeof(struct tcpa_event)) >= limit) - return NULL; - - event = addr; + if (((converted_event_type == 0) && (converted_event_size == 0)) + || ((addr + sizeof(struct tcpa_event) + converted_event_size) + > limit)) + return NULL; - converted_event_size = do_endian_conversion(event->event_size); - converted_event_type = do_endian_conversion(event->event_type); + if (i++ == *pos) + break; - if (((converted_event_type == 0) && (converted_event_size == 0)) - || ((addr + sizeof(struct tcpa_event) + converted_event_size) - >= limit)) - return NULL; + addr += (sizeof(struct tcpa_event) + converted_event_size); + } while (1); return addr; } @@ -134,7 +125,7 @@ static void *tpm1_bios_measurements_next(struct seq_file *m, void *v, v += sizeof(struct tcpa_event) + converted_event_size; /* now check if current entry is valid */ - if ((v + sizeof(struct tcpa_event)) >= limit) + if ((v + sizeof(struct tcpa_event)) > limit) return NULL; event = v; @@ -143,7 +134,7 @@ static void *tpm1_bios_measurements_next(struct seq_file *m, void *v, converted_event_type = do_endian_conversion(event->event_type); if (((converted_event_type == 0) && (converted_event_size == 0)) || - ((v + sizeof(struct tcpa_event) + converted_event_size) >= limit)) + ((v + sizeof(struct tcpa_event) + converted_event_size) > limit)) return NULL; (*pos)++; diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c index 1b8fa9de2cac..d8b77133a83a 100644 --- a/drivers/char/tpm/eventlog/tpm2.c +++ b/drivers/char/tpm/eventlog/tpm2.c @@ -37,10 +37,10 @@ * * Returns size of the event. If it is an invalid event, returns 0. */ -static int calc_tpm2_event_size(struct tcg_pcr_event2 *event, +static int calc_tpm2_event_size(struct tcg_pcr_event2_head *event, struct tcg_pcr_event *event_header) { - struct tcg_efi_specid_event *efispecid; + struct tcg_efi_specid_event_head *efispecid; struct tcg_event_field *event_field; void *marker; void *marker_start; @@ -55,7 +55,7 @@ static int calc_tpm2_event_size(struct tcg_pcr_event2 *event, marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type) + sizeof(event->count); - efispecid = (struct tcg_efi_specid_event *)event_header->event; + efispecid = (struct tcg_efi_specid_event_head *)event_header->event; /* Check if event is malformed. */ if (event->count > efispecid->num_algs) @@ -95,7 +95,7 @@ static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos) void *addr = log->bios_event_log; void *limit = log->bios_event_log_end; struct tcg_pcr_event *event_header; - struct tcg_pcr_event2 *event; + struct tcg_pcr_event2_head *event; size_t size; int i; @@ -136,7 +136,7 @@ static void *tpm2_bios_measurements_next(struct seq_file *m, void *v, loff_t *pos) { struct tcg_pcr_event *event_header; - struct tcg_pcr_event2 *event; + struct tcg_pcr_event2_head *event; struct tpm_chip *chip = m->private; struct tpm_bios_log *log = &chip->log; void *limit = log->bios_event_log_end; @@ -180,7 +180,7 @@ static int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v) struct tpm_chip *chip = m->private; struct tpm_bios_log *log = &chip->log; struct tcg_pcr_event *event_header = log->bios_event_log; - struct tcg_pcr_event2 *event = v; + struct tcg_pcr_event2_head *event = v; void *temp_ptr; size_t size; diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index be5d1abd3e8e..8390c5b54c3b 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -33,7 +33,7 @@ struct st33zp24_i2c_phy { struct i2c_client *client; - u8 buf[TPM_BUFSIZE + 1]; + u8 buf[ST33ZP24_BUFSIZE + 1]; int io_lpcpd; }; diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index d7909ab287a8..ff019a1e3c68 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -63,7 +63,7 @@ * some latency byte before the answer is available (max 15). * We have 2048 + 1024 + 15. */ -#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\ +#define ST33ZP24_SPI_BUFFER_SIZE (ST33ZP24_BUFSIZE + (ST33ZP24_BUFSIZE / 2) +\ MAX_SPI_LATENCY) diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 64dc560859f2..13dc614b7ebc 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -436,7 +436,7 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, goto out_err; } - return len; + return 0; out_err: st33zp24_cancel(chip); release_locality(chip); diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h index 6f4a4198af6a..20da0a84988d 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.h +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -18,8 +18,8 @@ #ifndef __LOCAL_ST33ZP24_H__ #define __LOCAL_ST33ZP24_H__ -#define TPM_WRITE_DIRECTION 0x80 -#define TPM_BUFSIZE 2048 +#define TPM_WRITE_DIRECTION 0x80 +#define ST33ZP24_BUFSIZE 2048 struct st33zp24_dev { struct tpm_chip *chip; diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 32db84683c40..8804c9e916fd 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -37,6 +37,103 @@ struct class *tpm_class; struct class *tpmrm_class; dev_t tpm_devt; +static int tpm_request_locality(struct tpm_chip *chip) +{ + int rc; + + if (!chip->ops->request_locality) + return 0; + + rc = chip->ops->request_locality(chip, 0); + if (rc < 0) + return rc; + + chip->locality = rc; + return 0; +} + +static void tpm_relinquish_locality(struct tpm_chip *chip) +{ + int rc; + + if (!chip->ops->relinquish_locality) + return; + + rc = chip->ops->relinquish_locality(chip, chip->locality); + if (rc) + dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); + + chip->locality = -1; +} + +static int tpm_cmd_ready(struct tpm_chip *chip) +{ + if (!chip->ops->cmd_ready) + return 0; + + return chip->ops->cmd_ready(chip); +} + +static int tpm_go_idle(struct tpm_chip *chip) +{ + if (!chip->ops->go_idle) + return 0; + + return chip->ops->go_idle(chip); +} + +/** + * tpm_chip_start() - power on the TPM + * @chip: a TPM chip to use + * + * Return: + * * The response length - OK + * * -errno - A system error + */ +int tpm_chip_start(struct tpm_chip *chip) +{ + int ret; + + if (chip->ops->clk_enable) + chip->ops->clk_enable(chip, true); + + if (chip->locality == -1) { + ret = tpm_request_locality(chip); + if (ret) { + chip->ops->clk_enable(chip, false); + return ret; + } + } + + ret = tpm_cmd_ready(chip); + if (ret) { + tpm_relinquish_locality(chip); + if (chip->ops->clk_enable) + chip->ops->clk_enable(chip, false); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(tpm_chip_start); + +/** + * tpm_chip_stop() - power off the TPM + * @chip: a TPM chip to use + * + * Return: + * * The response length - OK + * * -errno - A system error + */ +void tpm_chip_stop(struct tpm_chip *chip) +{ + tpm_go_idle(chip); + tpm_relinquish_locality(chip); + if (chip->ops->clk_enable) + chip->ops->clk_enable(chip, false); +} +EXPORT_SYMBOL_GPL(tpm_chip_stop); + /** * tpm_try_get_ops() - Get a ref to the tpm_chip * @chip: Chip to ref @@ -56,10 +153,17 @@ int tpm_try_get_ops(struct tpm_chip *chip) down_read(&chip->ops_sem); if (!chip->ops) + goto out_ops; + + mutex_lock(&chip->tpm_mutex); + rc = tpm_chip_start(chip); + if (rc) goto out_lock; return 0; out_lock: + mutex_unlock(&chip->tpm_mutex); +out_ops: up_read(&chip->ops_sem); put_device(&chip->dev); return rc; @@ -75,6 +179,8 @@ EXPORT_SYMBOL_GPL(tpm_try_get_ops); */ void tpm_put_ops(struct tpm_chip *chip) { + tpm_chip_stop(chip); + mutex_unlock(&chip->tpm_mutex); up_read(&chip->ops_sem); put_device(&chip->dev); } @@ -160,6 +266,7 @@ static void tpm_dev_release(struct device *dev) kfree(chip->log.bios_event_log); kfree(chip->work_space.context_buf); kfree(chip->work_space.session_buf); + kfree(chip->allocated_banks); kfree(chip); } @@ -189,7 +296,10 @@ static int tpm_class_shutdown(struct device *dev) if (chip->flags & TPM_CHIP_FLAG_TPM2) { down_write(&chip->ops_sem); - tpm2_shutdown(chip, TPM2_SU_CLEAR); + if (!tpm_chip_start(chip)) { + tpm2_shutdown(chip, TPM2_SU_CLEAR); + tpm_chip_stop(chip); + } chip->ops = NULL; up_write(&chip->ops_sem); } @@ -368,8 +478,12 @@ static void tpm_del_char_device(struct tpm_chip *chip) /* Make the driver uncallable. */ down_write(&chip->ops_sem); - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_shutdown(chip, TPM2_SU_CLEAR); + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + if (!tpm_chip_start(chip)) { + tpm2_shutdown(chip, TPM2_SU_CLEAR); + tpm_chip_stop(chip); + } + } chip->ops = NULL; up_write(&chip->ops_sem); } @@ -451,7 +565,11 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; + rc = tpm_chip_start(chip); + if (rc) + return rc; rc = tpm_auto_startup(chip); + tpm_chip_stop(chip); if (rc) return rc; diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 5eecad233ea1..8856cce5a23b 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -27,7 +27,38 @@ static struct workqueue_struct *tpm_dev_wq; static DEFINE_MUTEX(tpm_dev_wq_lock); -static void tpm_async_work(struct work_struct *work) +static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, + u8 *buf, size_t bufsiz) +{ + struct tpm_header *header = (void *)buf; + ssize_t ret, len; + + ret = tpm2_prepare_space(chip, space, buf, bufsiz); + /* If the command is not implemented by the TPM, synthesize a + * response with a TPM2_RC_COMMAND_CODE return for user-space. + */ + if (ret == -EOPNOTSUPP) { + header->length = cpu_to_be32(sizeof(*header)); + header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); + header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | + TSS2_RESMGR_TPM_RC_LAYER); + ret = sizeof(*header); + } + if (ret) + goto out_rc; + + len = tpm_transmit(chip, buf, bufsiz); + if (len < 0) + ret = len; + + if (!ret) + ret = tpm2_commit_space(chip, space, buf, &len); + +out_rc: + return ret ? ret : len; +} + +static void tpm_dev_async_work(struct work_struct *work) { struct file_priv *priv = container_of(work, struct file_priv, async_work); @@ -35,9 +66,8 @@ static void tpm_async_work(struct work_struct *work) mutex_lock(&priv->buffer_mutex); priv->command_enqueued = false; - ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, - sizeof(priv->data_buffer), 0); - + ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, + sizeof(priv->data_buffer)); tpm_put_ops(priv->chip); if (ret > 0) { priv->response_length = ret; @@ -80,7 +110,7 @@ void tpm_common_open(struct file *file, struct tpm_chip *chip, mutex_init(&priv->buffer_mutex); timer_setup(&priv->user_read_timer, user_reader_timeout, 0); INIT_WORK(&priv->timeout_work, tpm_timeout_work); - INIT_WORK(&priv->async_work, tpm_async_work); + INIT_WORK(&priv->async_work, tpm_dev_async_work); init_waitqueue_head(&priv->async_wait); file->private_data = priv; } @@ -183,8 +213,8 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, return size; } - ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, - sizeof(priv->data_buffer), 0); + ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, + sizeof(priv->data_buffer)); tpm_put_ops(priv->chip); if (ret > 0) { diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index d9439f9abe78..83ece5639f86 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -62,137 +62,22 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -static int tpm_validate_command(struct tpm_chip *chip, - struct tpm_space *space, - const u8 *cmd, - size_t len) -{ - const struct tpm_input_header *header = (const void *)cmd; - int i; - u32 cc; - u32 attrs; - unsigned int nr_handles; - - if (len < TPM_HEADER_SIZE) - return -EINVAL; - - if (!space) - return 0; - - if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) { - cc = be32_to_cpu(header->ordinal); - - i = tpm2_find_cc(chip, cc); - if (i < 0) { - dev_dbg(&chip->dev, "0x%04X is an invalid command\n", - cc); - return -EOPNOTSUPP; - } - - attrs = chip->cc_attrs_tbl[i]; - nr_handles = - 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); - if (len < TPM_HEADER_SIZE + 4 * nr_handles) - goto err_len; - } - - return 0; -err_len: - dev_dbg(&chip->dev, - "%s: insufficient command length %zu", __func__, len); - return -EINVAL; -} - -static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) +static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) { - int rc; - - if (flags & TPM_TRANSMIT_NESTED) - return 0; - - if (!chip->ops->request_locality) - return 0; - - rc = chip->ops->request_locality(chip, 0); - if (rc < 0) - return rc; - - chip->locality = rc; - - return 0; -} - -static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) -{ - int rc; - - if (flags & TPM_TRANSMIT_NESTED) - return; - - if (!chip->ops->relinquish_locality) - return; - - rc = chip->ops->relinquish_locality(chip, chip->locality); - if (rc) - dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); - - chip->locality = -1; -} - -static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) -{ - if (flags & TPM_TRANSMIT_NESTED) - return 0; - - if (!chip->ops->cmd_ready) - return 0; - - return chip->ops->cmd_ready(chip); -} - -static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) -{ - if (flags & TPM_TRANSMIT_NESTED) - return 0; - - if (!chip->ops->go_idle) - return 0; - - return chip->ops->go_idle(chip); -} - -static ssize_t tpm_try_transmit(struct tpm_chip *chip, - struct tpm_space *space, - u8 *buf, size_t bufsiz, - unsigned int flags) -{ - struct tpm_output_header *header = (void *)buf; + struct tpm_header *header = buf; int rc; ssize_t len = 0; u32 count, ordinal; unsigned long stop; - bool need_locality; - rc = tpm_validate_command(chip, space, buf, bufsiz); - if (rc == -EINVAL) - return rc; - /* - * If the command is not implemented by the TPM, synthesize a - * response with a TPM2_RC_COMMAND_CODE return for user-space. - */ - if (rc == -EOPNOTSUPP) { - header->length = cpu_to_be32(sizeof(*header)); - header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); - header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | - TSS2_RESMGR_TPM_RC_LAYER); - return sizeof(*header); - } + if (bufsiz < TPM_HEADER_SIZE) + return -EINVAL; if (bufsiz > TPM_BUFSIZE) bufsiz = TPM_BUFSIZE; - count = be32_to_cpu(*((__be32 *) (buf + 2))); - ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + count = be32_to_cpu(header->length); + ordinal = be32_to_cpu(header->ordinal); if (count == 0) return -ENODATA; if (count > bufsiz) { @@ -201,37 +86,21 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, return -E2BIG; } - if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED)) - mutex_lock(&chip->tpm_mutex); - - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, true); - - /* Store the decision as chip->locality will be changed. */ - need_locality = chip->locality == -1; - - if (need_locality) { - rc = tpm_request_locality(chip, flags); - if (rc < 0) { - need_locality = false; - goto out_locality; - } - } - - rc = tpm_cmd_ready(chip, flags); - if (rc) - goto out_locality; - - rc = tpm2_prepare_space(chip, space, ordinal, buf); - if (rc) - goto out; - rc = chip->ops->send(chip, buf, count); if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, - "%s: tpm_send: error %d\n", __func__, rc); - goto out; + "%s: send(): error %d\n", __func__, rc); + return rc; + } + + /* A sanity check. send() should just return zero on success e.g. + * not the command length. + */ + if (rc > 0) { + dev_warn(&chip->dev, + "%s: send(): invalid value %d\n", __func__, rc); + rc = 0; } if (chip->flags & TPM_CHIP_FLAG_IRQ) @@ -246,8 +115,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, if (chip->ops->req_canceled(chip, status)) { dev_err(&chip->dev, "Operation Canceled\n"); - rc = -ECANCELED; - goto out; + return -ECANCELED; } tpm_msleep(TPM_TIMEOUT_POLL); @@ -256,77 +124,45 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, chip->ops->cancel(chip); dev_err(&chip->dev, "Operation Timed out\n"); - rc = -ETIME; - goto out; + return -ETIME; out_recv: len = chip->ops->recv(chip, buf, bufsiz); if (len < 0) { rc = len; - dev_err(&chip->dev, - "tpm_transmit: tpm_recv: error %d\n", rc); - goto out; - } else if (len < TPM_HEADER_SIZE) { + dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc); + } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) rc = -EFAULT; - goto out; - } - if (len != be32_to_cpu(header->length)) { - rc = -EFAULT; - goto out; - } - - rc = tpm2_commit_space(chip, space, ordinal, buf, &len); - if (rc) - dev_err(&chip->dev, "tpm2_commit_space: error %d\n", rc); - -out: - /* may fail but do not override previous error value in rc */ - tpm_go_idle(chip, flags); - -out_locality: - if (need_locality) - tpm_relinquish_locality(chip, flags); - - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, false); - - if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED)) - mutex_unlock(&chip->tpm_mutex); return rc ? rc : len; } /** * tpm_transmit - Internal kernel interface to transmit TPM commands. + * @chip: a TPM chip to use + * @buf: a TPM command buffer + * @bufsiz: length of the TPM command buffer * - * @chip: TPM chip to use - * @space: tpm space - * @buf: TPM command buffer - * @bufsiz: length of the TPM command buffer - * @flags: tpm transmit flags - bitmap - * - * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY - * returns from the TPM and retransmits the command after a delay up - * to a maximum wait of TPM2_DURATION_LONG. + * A wrapper around tpm_try_transmit() that handles TPM2_RC_RETRY returns from + * the TPM and retransmits the command after a delay up to a maximum wait of + * TPM2_DURATION_LONG. * - * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2 - * only + * Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0 + * only. * * Return: - * the length of the return when the operation is successful. - * A negative number for system errors (errno). + * * The response length - OK + * * -errno - A system error */ -ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, - u8 *buf, size_t bufsiz, unsigned int flags) +ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz) { - struct tpm_output_header *header = (struct tpm_output_header *)buf; + struct tpm_header *header = (struct tpm_header *)buf; /* space for header and handles */ u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)]; unsigned int delay_msec = TPM2_DURATION_SHORT; u32 rc = 0; ssize_t ret; - const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE, - bufsiz); + const size_t save_size = min(sizeof(save), bufsiz); /* the command code is where the return code will be */ u32 cc = be32_to_cpu(header->return_code); @@ -338,7 +174,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, memcpy(save, buf, save_size); for (;;) { - ret = tpm_try_transmit(chip, space, buf, bufsiz, flags); + ret = tpm_try_transmit(chip, buf, bufsiz); if (ret < 0) break; rc = be32_to_cpu(header->return_code); @@ -365,39 +201,33 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, } return ret; } + /** * tpm_transmit_cmd - send a tpm command to the device - * The function extracts tpm out header return code - * - * @chip: TPM chip to use - * @space: tpm space - * @buf: TPM command buffer - * @bufsiz: length of the buffer - * @min_rsp_body_length: minimum expected length of response body - * @flags: tpm transmit flags - bitmap - * @desc: command description used in the error message + * @chip: a TPM chip to use + * @buf: a TPM command buffer + * @min_rsp_body_length: minimum expected length of response body + * @desc: command description used in the error message * * Return: - * 0 when the operation is successful. - * A negative number for system errors (errno). - * A positive number for a TPM error. + * * 0 - OK + * * -errno - A system error + * * TPM_RC - A TPM error */ -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - void *buf, size_t bufsiz, - size_t min_rsp_body_length, unsigned int flags, - const char *desc) +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, + size_t min_rsp_body_length, const char *desc) { - const struct tpm_output_header *header = buf; + const struct tpm_header *header = (struct tpm_header *)buf->data; int err; ssize_t len; - len = tpm_transmit(chip, space, buf, bufsiz, flags); + len = tpm_transmit(chip, buf->data, PAGE_SIZE); if (len < 0) return len; err = be32_to_cpu(header->return_code); if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED - && desc) + && err != TPM2_RC_TESTING && desc) dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); if (err) @@ -451,11 +281,12 @@ EXPORT_SYMBOL_GPL(tpm_is_tpm2); * tpm_pcr_read - read a PCR value from SHA1 bank * @chip: a &struct tpm_chip instance, %NULL for the default chip * @pcr_idx: the PCR to be retrieved - * @res_buf: the value of the PCR + * @digest: the PCR bank and buffer current PCR value is written to * * Return: same as with tpm_transmit_cmd() */ -int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) +int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digest) { int rc; @@ -464,9 +295,9 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) - rc = tpm2_pcr_read(chip, pcr_idx, res_buf); + rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL); else - rc = tpm1_pcr_read(chip, pcr_idx, res_buf); + rc = tpm1_pcr_read(chip, pcr_idx, digest->digest); tpm_put_ops(chip); return rc; @@ -477,41 +308,34 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * tpm_pcr_extend - extend a PCR value in SHA1 bank. * @chip: a &struct tpm_chip instance, %NULL for the default chip * @pcr_idx: the PCR to be retrieved - * @hash: the hash value used to extend the PCR value + * @digests: array of tpm_digest structures used to extend PCRs * - * Note: with TPM 2.0 extends also those banks with a known digest size to the - * cryto subsystem in order to prevent malicious use of those PCR banks. In the - * future we should dynamically determine digest sizes. + * Note: callers must pass a digest for every allocated PCR bank, in the same + * order of the banks in chip->allocated_banks. * * Return: same as with tpm_transmit_cmd() */ -int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash) +int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digests) { int rc; - struct tpm2_digest digest_list[ARRAY_SIZE(chip->active_banks)]; - u32 count = 0; int i; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; - if (chip->flags & TPM_CHIP_FLAG_TPM2) { - memset(digest_list, 0, sizeof(digest_list)); - - for (i = 0; i < ARRAY_SIZE(chip->active_banks) && - chip->active_banks[i] != TPM2_ALG_ERROR; i++) { - digest_list[i].alg_id = chip->active_banks[i]; - memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE); - count++; - } + for (i = 0; i < chip->nr_allocated_banks; i++) + if (digests[i].alg_id != chip->allocated_banks[i].alg_id) + return -EINVAL; - rc = tpm2_pcr_extend(chip, pcr_idx, count, digest_list); + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + rc = tpm2_pcr_extend(chip, pcr_idx, digests); tpm_put_ops(chip); return rc; } - rc = tpm1_pcr_extend(chip, pcr_idx, hash, + rc = tpm1_pcr_extend(chip, pcr_idx, digests[0].digest, "attempting extend a PCR value"); tpm_put_ops(chip); return rc; @@ -528,14 +352,21 @@ EXPORT_SYMBOL_GPL(tpm_pcr_extend); */ int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) { + struct tpm_buf buf; int rc; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; - rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0, - "attempting to a send a command"); + rc = tpm_buf_init(&buf, 0, 0); + if (rc) + goto out; + + memcpy(buf.data, cmd, buflen); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to a send a command"); + tpm_buf_destroy(&buf); +out: tpm_put_ops(chip); return rc; } @@ -571,10 +402,16 @@ int tpm_pm_suspend(struct device *dev) if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) return 0; - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_shutdown(chip, TPM2_SU_STATE); - else + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + mutex_lock(&chip->tpm_mutex); + if (!tpm_chip_start(chip)) { + tpm2_shutdown(chip, TPM2_SU_STATE); + tpm_chip_stop(chip); + } + mutex_unlock(&chip->tpm_mutex); + } else { rc = tpm1_pm_suspend(chip, tpm_suspend_pcr); + } return rc; } diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index b88e08ec2c59..533a260d744e 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -39,7 +39,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, { struct tpm_buf tpm_buf; struct tpm_readpubek_out *out; - ssize_t rc; int i; char *str = buf; struct tpm_chip *chip = to_tpm_chip(dev); @@ -47,19 +46,17 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, memset(&anti_replay, 0, sizeof(anti_replay)); - rc = tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK); - if (rc) - return rc; + if (tpm_try_get_ops(chip)) + return 0; + + if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK)) + goto out_ops; tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay)); - rc = tpm_transmit_cmd(chip, NULL, tpm_buf.data, PAGE_SIZE, - READ_PUBEK_RESULT_MIN_BODY_SIZE, 0, - "attempting to read the PUBEK"); - if (rc) { - tpm_buf_destroy(&tpm_buf); - return 0; - } + if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, + "attempting to read the PUBEK")) + goto out_buf; out = (struct tpm_readpubek_out *)&tpm_buf.data[10]; str += @@ -90,9 +87,11 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, str += sprintf(str, "\n"); } - rc = str - buf; +out_buf: tpm_buf_destroy(&tpm_buf); - return rc; +out_ops: + tpm_put_ops(chip); + return str - buf; } static DEVICE_ATTR_RO(pubek); @@ -101,27 +100,32 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, { cap_t cap; u8 digest[TPM_DIGEST_SIZE]; - ssize_t rc; u32 i, j, num_pcrs; char *str = buf; struct tpm_chip *chip = to_tpm_chip(dev); - rc = tpm1_getcap(chip, TPM_CAP_PROP_PCR, &cap, - "attempting to determine the number of PCRS", - sizeof(cap.num_pcrs)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(chip, TPM_CAP_PROP_PCR, &cap, + "attempting to determine the number of PCRS", + sizeof(cap.num_pcrs))) { + tpm_put_ops(chip); + return 0; + } + num_pcrs = be32_to_cpu(cap.num_pcrs); for (i = 0; i < num_pcrs; i++) { - rc = tpm1_pcr_read(chip, i, digest); - if (rc) + if (tpm1_pcr_read(chip, i, digest)) { + str = buf; break; + } str += sprintf(str, "PCR-%02d: ", i); for (j = 0; j < TPM_DIGEST_SIZE; j++) str += sprintf(str, "%02X ", digest[j]); str += sprintf(str, "\n"); } + tpm_put_ops(chip); return str - buf; } static DEVICE_ATTR_RO(pcrs); @@ -129,16 +133,21 @@ static DEVICE_ATTR_RO(pcrs); static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent enabled state", - sizeof(cap.perm_flags)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(chip, TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent enabled state", + sizeof(cap.perm_flags))) + goto out_ops; + rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(enabled); @@ -146,16 +155,21 @@ static DEVICE_ATTR_RO(enabled); static ssize_t active_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent active state", - sizeof(cap.perm_flags)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(chip, TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent active state", + sizeof(cap.perm_flags))) + goto out_ops; + rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(active); @@ -163,16 +177,21 @@ static DEVICE_ATTR_RO(active); static ssize_t owned_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, - "attempting to determine the owner state", - sizeof(cap.owned)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, + "attempting to determine the owner state", + sizeof(cap.owned))) + goto out_ops; + rc = sprintf(buf, "%d\n", cap.owned); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(owned); @@ -180,16 +199,21 @@ static DEVICE_ATTR_RO(owned); static ssize_t temp_deactivated_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, - "attempting to determine the temporary state", - sizeof(cap.stclear_flags)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, + "attempting to determine the temporary state", + sizeof(cap.stclear_flags))) + goto out_ops; + rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(temp_deactivated); @@ -198,15 +222,18 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, char *buf) { struct tpm_chip *chip = to_tpm_chip(dev); - cap_t cap; - ssize_t rc; + ssize_t rc = 0; char *str = buf; + cap_t cap; - rc = tpm1_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, - "attempting to determine the manufacturer", - sizeof(cap.manufacturer_id)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + + if (tpm1_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, + "attempting to determine the manufacturer", + sizeof(cap.manufacturer_id))) + goto out_ops; + str += sprintf(str, "Manufacturer: 0x%x\n", be32_to_cpu(cap.manufacturer_id)); @@ -223,11 +250,10 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, cap.tpm_version_1_2.revMinor); } else { /* Otherwise just use TPM_STRUCT_VER */ - rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, - "attempting to determine the 1.1 version", - sizeof(cap.tpm_version)); - if (rc) - return 0; + if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, + "attempting to determine the 1.1 version", + sizeof(cap.tpm_version))) + goto out_ops; str += sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n", cap.tpm_version.Major, @@ -235,8 +261,10 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, cap.tpm_version.revMajor, cap.tpm_version.revMinor); } - - return str - buf; + rc = str - buf; +out_ops: + tpm_put_ops(chip); + return rc; } static DEVICE_ATTR_RO(caps); @@ -244,10 +272,12 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct tpm_chip *chip = to_tpm_chip(dev); - if (chip == NULL) + + if (tpm_try_get_ops(chip)) return 0; chip->ops->cancel(chip); + tpm_put_ops(chip); return count; } static DEVICE_ATTR_WO(cancel); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f27d1f38a93d..2cce072f25b5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -25,30 +25,22 @@ #include <linux/module.h> #include <linux/delay.h> -#include <linux/fs.h> -#include <linux/hw_random.h> #include <linux/mutex.h> #include <linux/sched.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/tpm.h> -#include <linux/acpi.h> -#include <linux/cdev.h> #include <linux/highmem.h> #include <linux/tpm_eventlog.h> -#include <crypto/hash_info.h> #ifdef CONFIG_X86 #include <asm/intel-family.h> #endif -enum tpm_const { - TPM_MINOR = 224, /* officially assigned */ - TPM_BUFSIZE = 4096, - TPM_NUM_DEVICES = 65536, - TPM_RETRY = 50, /* 5 seconds */ - TPM_NUM_EVENT_LOG_FILES = 3, -}; +#define TPM_MINOR 224 /* officially assigned */ +#define TPM_BUFSIZE 4096 +#define TPM_NUM_DEVICES 65536 +#define TPM_RETRY 50 enum tpm_timeout { TPM_TIMEOUT = 5, /* msecs */ @@ -65,16 +57,6 @@ enum tpm_addr { TPM_ADDR = 0x4E, }; -/* Indexes the duration array */ -enum tpm_duration { - TPM_SHORT = 0, - TPM_MEDIUM = 1, - TPM_LONG = 2, - TPM_LONG_LONG = 3, - TPM_UNDEFINED, - TPM_NUM_DURATIONS = TPM_UNDEFINED, -}; - #define TPM_WARN_RETRY 0x800 #define TPM_WARN_DOING_SELFTEST 0x802 #define TPM_ERR_DEACTIVATED 0x6 @@ -122,17 +104,6 @@ enum tpm2_return_codes { TPM2_RC_RETRY = 0x0922, }; -enum tpm2_algorithms { - TPM2_ALG_ERROR = 0x0000, - TPM2_ALG_SHA1 = 0x0004, - TPM2_ALG_KEYEDHASH = 0x0008, - TPM2_ALG_SHA256 = 0x000B, - TPM2_ALG_SHA384 = 0x000C, - TPM2_ALG_SHA512 = 0x000D, - TPM2_ALG_NULL = 0x0010, - TPM2_ALG_SM3_256 = 0x0012, -}; - enum tpm2_command_codes { TPM2_CC_FIRST = 0x011F, TPM2_CC_HIERARCHY_CONTROL = 0x0121, @@ -190,15 +161,6 @@ enum tpm2_cc_attrs { #define TPM_VID_WINBOND 0x1050 #define TPM_VID_STM 0x104A -#define TPM_PPI_VERSION_LEN 3 - -struct tpm_space { - u32 context_tbl[3]; - u8 *context_buf; - u32 session_tbl[3]; - u8 *session_buf; -}; - enum tpm_chip_flags { TPM_CHIP_FLAG_TPM2 = BIT(1), TPM_CHIP_FLAG_IRQ = BIT(2), @@ -207,82 +169,15 @@ enum tpm_chip_flags { TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), }; -struct tpm_bios_log { - void *bios_event_log; - void *bios_event_log_end; -}; - -struct tpm_chip_seqops { - struct tpm_chip *chip; - const struct seq_operations *seqops; -}; - -struct tpm_chip { - struct device dev; - struct device devs; - struct cdev cdev; - struct cdev cdevs; - - /* A driver callback under ops cannot be run unless ops_sem is held - * (sometimes implicitly, eg for the sysfs code). ops becomes null - * when the driver is unregistered, see tpm_try_get_ops. - */ - struct rw_semaphore ops_sem; - const struct tpm_class_ops *ops; - - struct tpm_bios_log log; - struct tpm_chip_seqops bin_log_seqops; - struct tpm_chip_seqops ascii_log_seqops; - - unsigned int flags; - - int dev_num; /* /dev/tpm# */ - unsigned long is_open; /* only one allowed */ - - char hwrng_name[64]; - struct hwrng hwrng; - - struct mutex tpm_mutex; /* tpm is processing */ - - unsigned long timeout_a; /* jiffies */ - unsigned long timeout_b; /* jiffies */ - unsigned long timeout_c; /* jiffies */ - unsigned long timeout_d; /* jiffies */ - bool timeout_adjusted; - unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */ - bool duration_adjusted; - - struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES]; - - const struct attribute_group *groups[3]; - unsigned int groups_cnt; - - u16 active_banks[7]; -#ifdef CONFIG_ACPI - acpi_handle acpi_dev_handle; - char ppi_version[TPM_PPI_VERSION_LEN + 1]; -#endif /* CONFIG_ACPI */ - - struct tpm_space work_space; - u32 nr_commands; - u32 *cc_attrs_tbl; - - /* active locality */ - int locality; -}; - #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) -struct tpm_input_header { - __be16 tag; - __be32 length; - __be32 ordinal; -} __packed; - -struct tpm_output_header { - __be16 tag; - __be32 length; - __be32 return_code; +struct tpm_header { + __be16 tag; + __be32 length; + union { + __be32 ordinal; + __be32 return_code; + }; } __packed; #define TPM_TAG_RQU_COMMAND 193 @@ -401,8 +296,8 @@ struct tpm_buf { static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal) { - struct tpm_input_header *head; - head = (struct tpm_input_header *)buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; + head->tag = cpu_to_be16(tag); head->length = cpu_to_be32(sizeof(*head)); head->ordinal = cpu_to_be32(ordinal); @@ -428,14 +323,14 @@ static inline void tpm_buf_destroy(struct tpm_buf *buf) static inline u32 tpm_buf_length(struct tpm_buf *buf) { - struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; return be32_to_cpu(head->length); } static inline u16 tpm_buf_tag(struct tpm_buf *buf) { - struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; return be16_to_cpu(head->tag); } @@ -444,7 +339,7 @@ static inline void tpm_buf_append(struct tpm_buf *buf, const unsigned char *new_data, unsigned int new_len) { - struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; u32 len = tpm_buf_length(buf); /* Return silently if overflow has already happened. */ @@ -487,25 +382,9 @@ extern const struct file_operations tpm_fops; extern const struct file_operations tpmrm_fops; extern struct idr dev_nums_idr; -/** - * enum tpm_transmit_flags - flags for tpm_transmit() - * - * @TPM_TRANSMIT_UNLOCKED: do not lock the chip - * @TPM_TRANSMIT_NESTED: discard setup steps (power management, - * locality) including locking (i.e. implicit - * UNLOCKED) - */ -enum tpm_transmit_flags { - TPM_TRANSMIT_UNLOCKED = BIT(0), - TPM_TRANSMIT_NESTED = BIT(1), -}; - -ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, - u8 *buf, size_t bufsiz, unsigned int flags); -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - void *buf, size_t bufsiz, - size_t min_rsp_body_length, unsigned int flags, - const char *desc); +ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz); +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, + size_t min_rsp_body_length, const char *desc); int tpm_get_timeouts(struct tpm_chip *); int tpm_auto_startup(struct tpm_chip *chip); @@ -530,6 +409,8 @@ static inline void tpm_msleep(unsigned int delay_msec) delay_msec * 1000); }; +int tpm_chip_start(struct tpm_chip *chip); +void tpm_chip_stop(struct tpm_chip *chip); struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); __must_check int tpm_try_get_ops(struct tpm_chip *chip); void tpm_put_ops(struct tpm_chip *chip); @@ -558,12 +439,12 @@ static inline u32 tpm2_rc_value(u32 rc) } int tpm2_get_timeouts(struct tpm_chip *chip); -int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf); -int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, - struct tpm2_digest *digests); +int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digest, u16 *digest_size_ptr); +int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); -void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, - unsigned int flags); +void tpm2_flush_context(struct tpm_chip *chip, u32 handle); int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options); @@ -580,10 +461,11 @@ int tpm2_probe(struct tpm_chip *chip); int tpm2_find_cc(struct tpm_chip *chip, u32 cc); int tpm2_init_space(struct tpm_space *space); void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space); -int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, - u8 *cmd); -int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, - u32 cc, u8 *buf, size_t *bufsiz); +void tpm2_flush_space(struct tpm_chip *chip); +int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd, + size_t cmdsiz); +int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, void *buf, + size_t *bufsiz); int tpm_bios_log_setup(struct tpm_chip *chip); void tpm_bios_log_teardown(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 6f306338953b..85dcf2654d11 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -334,11 +334,8 @@ static int tpm1_startup(struct tpm_chip *chip) tpm_buf_append_u16(&buf, TPM_ST_CLEAR); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, - "attempting to start the TPM"); - + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM"); tpm_buf_destroy(&buf); - return rc; } @@ -380,8 +377,7 @@ int tpm1_get_timeouts(struct tpm_chip *chip) * of misreporting. */ if (chip->ops->update_timeouts) - chip->timeout_adjusted = - chip->ops->update_timeouts(chip, timeout_eff); + chip->ops->update_timeouts(chip, timeout_eff); if (!chip->timeout_adjusted) { /* Restore default if chip reported 0 */ @@ -462,9 +458,7 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash, tpm_buf_append_u32(&buf, pcr_idx); tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - TPM_DIGEST_SIZE, 0, log_msg); - + rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg); tpm_buf_destroy(&buf); return rc; } @@ -494,11 +488,9 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, tpm_buf_append_u32(&buf, 4); tpm_buf_append_u32(&buf, subcap_id); } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - min_cap_length, 0, desc); + rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc); if (!rc) *cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4]; - tpm_buf_destroy(&buf); return rc; } @@ -537,8 +529,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_append_u32(&buf, num_bytes); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - sizeof(out->rng_data_len), 0, + rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len), "attempting get random"); if (rc) goto out; @@ -583,8 +574,7 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) tpm_buf_append_u32(&buf, pcr_idx); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - TPM_DIGEST_SIZE, 0, + rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, "attempting to read a pcr value"); if (rc) goto out; @@ -618,11 +608,8 @@ static int tpm1_continue_selftest(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - 0, 0, "continue selftest"); - + rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest"); tpm_buf_destroy(&buf); - return rc; } @@ -709,6 +696,18 @@ int tpm1_auto_startup(struct tpm_chip *chip) goto out; } + chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks), + GFP_KERNEL); + if (!chip->allocated_banks) { + rc = -ENOMEM; + goto out; + } + + chip->allocated_banks[0].alg_id = TPM_ALG_SHA1; + chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1]; + chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1; + chip->nr_allocated_banks = 1; + return rc; out: if (rc > 0) @@ -747,9 +746,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr) return rc; /* now do the actual savestate */ for (try = 0; try < TPM_RETRY; try++) { - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - 0, 0, NULL); - + rc = tpm_transmit_cmd(chip, &buf, 0, NULL); /* * If the TPM indicates that it is too busy to respond to * this command then retry before giving up. It can take diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index a6bec13afa69..e74c5b7b64bf 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -33,11 +33,11 @@ struct tpm2_hash { }; static struct tpm2_hash tpm2_hash_map[] = { - {HASH_ALGO_SHA1, TPM2_ALG_SHA1}, - {HASH_ALGO_SHA256, TPM2_ALG_SHA256}, - {HASH_ALGO_SHA384, TPM2_ALG_SHA384}, - {HASH_ALGO_SHA512, TPM2_ALG_SHA512}, - {HASH_ALGO_SM3_256, TPM2_ALG_SM3_256}, + {HASH_ALGO_SHA1, TPM_ALG_SHA1}, + {HASH_ALGO_SHA256, TPM_ALG_SHA256}, + {HASH_ALGO_SHA384, TPM_ALG_SHA384}, + {HASH_ALGO_SHA512, TPM_ALG_SHA512}, + {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, }; int tpm2_get_timeouts(struct tpm_chip *chip) @@ -171,20 +171,36 @@ struct tpm2_pcr_read_out { * tpm2_pcr_read() - read a PCR value * @chip: TPM chip to use. * @pcr_idx: index of the PCR to read. - * @res_buf: buffer to store the resulting hash. + * @digest: PCR bank and buffer current PCR value is written to. + * @digest_size_ptr: pointer to variable that stores the digest size. * * Return: Same as with tpm_transmit_cmd. */ -int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) +int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digest, u16 *digest_size_ptr) { + int i; int rc; struct tpm_buf buf; struct tpm2_pcr_read_out *out; u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0}; + u16 digest_size; + u16 expected_digest_size = 0; if (pcr_idx >= TPM2_PLATFORM_PCR) return -EINVAL; + if (!digest_size_ptr) { + for (i = 0; i < chip->nr_allocated_banks && + chip->allocated_banks[i].alg_id != digest->alg_id; i++) + ; + + if (i == chip->nr_allocated_banks) + return -EINVAL; + + expected_digest_size = chip->allocated_banks[i].digest_size; + } + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ); if (rc) return rc; @@ -192,18 +208,28 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); tpm_buf_append_u32(&buf, 1); - tpm_buf_append_u16(&buf, TPM2_ALG_SHA1); + tpm_buf_append_u16(&buf, digest->alg_id); tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN); tpm_buf_append(&buf, (const unsigned char *)pcr_select, sizeof(pcr_select)); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, - res_buf ? "attempting to read a pcr value" : NULL); - if (rc == 0 && res_buf) { - out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; - memcpy(res_buf, out->digest, SHA1_DIGEST_SIZE); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value"); + if (rc) + goto out; + + out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; + digest_size = be16_to_cpu(out->digest_size); + if (digest_size > sizeof(digest->digest) || + (!digest_size_ptr && digest_size != expected_digest_size)) { + rc = -EINVAL; + goto out; } + if (digest_size_ptr) + *digest_size_ptr = digest_size; + + memcpy(digest->digest, out->digest, digest_size); +out: tpm_buf_destroy(&buf); return rc; } @@ -220,22 +246,17 @@ struct tpm2_null_auth_area { * * @chip: TPM chip to use. * @pcr_idx: index of the PCR. - * @count: number of digests passed. * @digests: list of pcr banks and corresponding digest values to extend. * * Return: Same as with tpm_transmit_cmd. */ -int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, - struct tpm2_digest *digests) +int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digests) { struct tpm_buf buf; struct tpm2_null_auth_area auth_area; int rc; int i; - int j; - - if (count > ARRAY_SIZE(chip->active_banks)) - return -EINVAL; rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND); if (rc) @@ -251,21 +272,15 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area)); tpm_buf_append(&buf, (const unsigned char *)&auth_area, sizeof(auth_area)); - tpm_buf_append_u32(&buf, count); - - for (i = 0; i < count; i++) { - for (j = 0; j < ARRAY_SIZE(tpm2_hash_map); j++) { - if (digests[i].alg_id != tpm2_hash_map[j].tpm_id) - continue; - tpm_buf_append_u16(&buf, digests[i].alg_id); - tpm_buf_append(&buf, (const unsigned char - *)&digests[i].digest, - hash_digest_size[tpm2_hash_map[j].crypto_id]); - } + tpm_buf_append_u32(&buf, chip->nr_allocated_banks); + + for (i = 0; i < chip->nr_allocated_banks; i++) { + tpm_buf_append_u16(&buf, digests[i].alg_id); + tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest, + chip->allocated_banks[i].digest_size); } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, - "attempting extend a PCR value"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value"); tpm_buf_destroy(&buf); @@ -309,10 +324,10 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM); tpm_buf_append_u16(&buf, num_bytes); - err = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, + err = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, buffer), - 0, "attempting get random"); + "attempting get random"); if (err) goto out; @@ -341,14 +356,11 @@ out: } /** - * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command + * tpm2_flush_context() - execute a TPM2_FlushContext command * @chip: TPM chip to use * @handle: context handle - * @flags: tpm transmit flags - bitmap - * */ -void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, - unsigned int flags) +void tpm2_flush_context(struct tpm_chip *chip, u32 handle) { struct tpm_buf buf; int rc; @@ -362,9 +374,7 @@ void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, tpm_buf_append_u32(&buf, handle); - (void) tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, flags, - "flushing context"); - + tpm_transmit_cmd(chip, &buf, 0, "flushing context"); tpm_buf_destroy(&buf); } @@ -449,7 +459,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, /* public */ tpm_buf_append_u16(&buf, 14 + options->policydigest_len); - tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); + tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); tpm_buf_append_u16(&buf, hash); /* policy */ @@ -464,7 +474,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, } /* public parameters */ - tpm_buf_append_u16(&buf, TPM2_ALG_NULL); + tpm_buf_append_u16(&buf, TPM_ALG_NULL); tpm_buf_append_u16(&buf, 0); /* outside info */ @@ -478,8 +488,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, 0, - "sealing data"); + rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); if (rc) goto out; @@ -516,7 +525,6 @@ out: * @payload: the key data in clear and encrypted form * @options: authentication values and other options * @blob_handle: returned blob handle - * @flags: tpm transmit flags * * Return: 0 on success. * -E2BIG on wrong payload size. @@ -526,7 +534,7 @@ out: static int tpm2_load_cmd(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options, - u32 *blob_handle, unsigned int flags) + u32 *blob_handle) { struct tpm_buf buf; unsigned int private_len; @@ -561,8 +569,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, flags, - "loading blob"); + rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -583,7 +590,6 @@ out: * @payload: the key data in clear and encrypted form * @options: authentication values and other options * @blob_handle: blob handle - * @flags: tpm_transmit_cmd flags * * Return: 0 on success * -EPERM on tpm error status @@ -592,7 +598,7 @@ out: static int tpm2_unseal_cmd(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options, - u32 blob_handle, unsigned int flags) + u32 blob_handle) { struct tpm_buf buf; u16 data_len; @@ -612,8 +618,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, options->blobauth /* hmac */, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 6, flags, - "unsealing"); + rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); if (rc > 0) rc = -EPERM; @@ -657,17 +662,12 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, u32 blob_handle; int rc; - mutex_lock(&chip->tpm_mutex); - rc = tpm2_load_cmd(chip, payload, options, &blob_handle, - TPM_TRANSMIT_UNLOCKED); + rc = tpm2_load_cmd(chip, payload, options, &blob_handle); if (rc) - goto out; + return rc; - rc = tpm2_unseal_cmd(chip, payload, options, blob_handle, - TPM_TRANSMIT_UNLOCKED); - tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED); -out: - mutex_unlock(&chip->tpm_mutex); + rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); + tpm2_flush_context(chip, blob_handle); return rc; } @@ -703,7 +703,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, property_id); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, NULL); if (!rc) { out = (struct tpm2_get_cap_out *) &buf.data[TPM_HEADER_SIZE]; @@ -733,8 +733,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) if (rc) return; tpm_buf_append_u16(&buf, shutdown_type); - tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, - "stopping the TPM"); + tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM"); tpm_buf_destroy(&buf); } @@ -763,7 +762,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip) return rc; tpm_buf_append_u8(&buf, full); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting the self test"); tpm_buf_destroy(&buf); @@ -790,7 +789,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip) */ int tpm2_probe(struct tpm_chip *chip) { - struct tpm_output_header *out; + struct tpm_header *out; struct tpm_buf buf; int rc; @@ -800,10 +799,10 @@ int tpm2_probe(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, NULL); /* We ignore TPM return codes on purpose. */ if (rc >= 0) { - out = (struct tpm_output_header *)buf.data; + out = (struct tpm_header *)buf.data; if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS) chip->flags |= TPM_CHIP_FLAG_TPM2; } @@ -812,6 +811,30 @@ int tpm2_probe(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm2_probe); +static int tpm2_init_bank_info(struct tpm_chip *chip, u32 bank_index) +{ + struct tpm_bank_info *bank = chip->allocated_banks + bank_index; + struct tpm_digest digest = { .alg_id = bank->alg_id }; + int i; + + /* + * Avoid unnecessary PCR read operations to reduce overhead + * and obtain identifiers of the crypto subsystem. + */ + for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { + enum hash_algo crypto_algo = tpm2_hash_map[i].crypto_id; + + if (bank->alg_id != tpm2_hash_map[i].tpm_id) + continue; + + bank->digest_size = hash_digest_size[crypto_algo]; + bank->crypto_id = crypto_algo; + return 0; + } + + return tpm2_pcr_read(chip, 0, &digest, &bank->digest_size); +} + struct tpm2_pcr_selection { __be16 hash_alg; u8 size_of_select; @@ -825,8 +848,10 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) void *marker; void *end; void *pcr_select_offset; - unsigned int count; u32 sizeof_pcr_selection; + u32 nr_possible_banks; + u32 nr_alloc_banks = 0; + u16 hash_alg; u32 rsp_len; int rc; int i = 0; @@ -839,16 +864,18 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) tpm_buf_append_u32(&buf, 0); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 9, 0, - "get tpm pcr allocation"); + rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation"); if (rc) goto out; - count = be32_to_cpup( + nr_possible_banks = be32_to_cpup( (__be32 *)&buf.data[TPM_HEADER_SIZE + 5]); - if (count > ARRAY_SIZE(chip->active_banks)) { - rc = -ENODEV; + chip->allocated_banks = kcalloc(nr_possible_banks, + sizeof(*chip->allocated_banks), + GFP_KERNEL); + if (!chip->allocated_banks) { + rc = -ENOMEM; goto out; } @@ -857,7 +884,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) rsp_len = be32_to_cpup((__be32 *)&buf.data[2]); end = &buf.data[rsp_len]; - for (i = 0; i < count; i++) { + for (i = 0; i < nr_possible_banks; i++) { pcr_select_offset = marker + offsetof(struct tpm2_pcr_selection, size_of_select); if (pcr_select_offset >= end) { @@ -866,17 +893,28 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) } memcpy(&pcr_selection, marker, sizeof(pcr_selection)); - chip->active_banks[i] = be16_to_cpu(pcr_selection.hash_alg); + hash_alg = be16_to_cpu(pcr_selection.hash_alg); + + pcr_select_offset = memchr_inv(pcr_selection.pcr_select, 0, + pcr_selection.size_of_select); + if (pcr_select_offset) { + chip->allocated_banks[nr_alloc_banks].alg_id = hash_alg; + + rc = tpm2_init_bank_info(chip, nr_alloc_banks); + if (rc < 0) + break; + + nr_alloc_banks++; + } + sizeof_pcr_selection = sizeof(pcr_selection.hash_alg) + sizeof(pcr_selection.size_of_select) + pcr_selection.size_of_select; marker = marker + sizeof_pcr_selection; } + chip->nr_allocated_banks = nr_alloc_banks; out: - if (i < ARRAY_SIZE(chip->active_banks)) - chip->active_banks[i] = TPM2_ALG_ERROR; - tpm_buf_destroy(&buf); return rc; @@ -911,8 +949,7 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CC_FIRST); tpm_buf_append_u32(&buf, nr_commands); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - 9 + 4 * nr_commands, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL); if (rc) { tpm_buf_destroy(&buf); goto out; @@ -969,8 +1006,7 @@ static int tpm2_startup(struct tpm_chip *chip) return rc; tpm_buf_append_u16(&buf, TPM2_SU_CLEAR); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, - "attempting to start the TPM"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM"); tpm_buf_destroy(&buf); return rc; diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index dcdfde3c253e..4a2773c3374f 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -38,8 +38,7 @@ static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { if (space->session_tbl[i]) - tpm2_flush_context_cmd(chip, space->session_tbl[i], - TPM_TRANSMIT_NESTED); + tpm2_flush_context(chip, space->session_tbl[i]); } } @@ -61,7 +60,10 @@ int tpm2_init_space(struct tpm_space *space) void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) { mutex_lock(&chip->tpm_mutex); - tpm2_flush_sessions(chip, space); + if (!tpm_chip_start(chip)) { + tpm2_flush_sessions(chip, space); + tpm_chip_stop(chip); + } mutex_unlock(&chip->tpm_mutex); kfree(space->context_buf); kfree(space->session_buf); @@ -83,8 +85,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); tpm_buf_append(&tbuf, &buf[*offset], body_size); - rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4, - TPM_TRANSMIT_NESTED, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -132,8 +133,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, tpm_buf_append_u32(&tbuf, handle); - rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0, - TPM_TRANSMIT_NESTED, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -162,15 +162,14 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, return 0; } -static void tpm2_flush_space(struct tpm_chip *chip) +void tpm2_flush_space(struct tpm_chip *chip) { struct tpm_space *space = &chip->work_space; int i; for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) if (space->context_tbl[i] && ~space->context_tbl[i]) - tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_NESTED); + tpm2_flush_context(chip, space->context_tbl[i]); tpm2_flush_sessions(chip, space); } @@ -264,14 +263,54 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd) return 0; } -int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, - u8 *cmd) +static int tpm_find_and_validate_cc(struct tpm_chip *chip, + struct tpm_space *space, + const void *cmd, size_t len) +{ + const struct tpm_header *header = (const void *)cmd; + int i; + u32 cc; + u32 attrs; + unsigned int nr_handles; + + if (len < TPM_HEADER_SIZE || !chip->nr_commands) + return -EINVAL; + + cc = be32_to_cpu(header->ordinal); + + i = tpm2_find_cc(chip, cc); + if (i < 0) { + dev_dbg(&chip->dev, "0x%04X is an invalid command\n", + cc); + return -EOPNOTSUPP; + } + + attrs = chip->cc_attrs_tbl[i]; + nr_handles = + 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); + if (len < TPM_HEADER_SIZE + 4 * nr_handles) + goto err_len; + + return cc; +err_len: + dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__, + len); + return -EINVAL; +} + +int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd, + size_t cmdsiz) { int rc; + int cc; if (!space) return 0; + cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz); + if (cc < 0) + return cc; + memcpy(&chip->work_space.context_tbl, &space->context_tbl, sizeof(space->context_tbl)); memcpy(&chip->work_space.session_tbl, &space->session_tbl, @@ -291,6 +330,7 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, return rc; } + chip->last_cc = cc; return 0; } @@ -334,7 +374,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) { struct tpm_space *space = &chip->work_space; - struct tpm_output_header *header = (void *)rsp; + struct tpm_header *header = (struct tpm_header *)rsp; u32 phandle; u32 phandle_type; u32 vhandle; @@ -377,7 +417,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, return 0; out_no_slots: - tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED); + tpm2_flush_context(chip, phandle); dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, phandle); return -ENOMEM; @@ -394,7 +434,7 @@ static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) { struct tpm_space *space = &chip->work_space; - struct tpm_output_header *header = (void *)rsp; + struct tpm_header *header = (struct tpm_header *)rsp; struct tpm2_cap_handles *data; u32 phandle; u32 phandle_type; @@ -464,8 +504,7 @@ static int tpm2_save_space(struct tpm_chip *chip) } else if (rc) return rc; - tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_NESTED); + tpm2_flush_context(chip, space->context_tbl[i]); space->context_tbl[i] = ~0; } @@ -490,30 +529,30 @@ static int tpm2_save_space(struct tpm_chip *chip) } int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, - u32 cc, u8 *buf, size_t *bufsiz) + void *buf, size_t *bufsiz) { - struct tpm_output_header *header = (void *)buf; + struct tpm_header *header = buf; int rc; if (!space) return 0; - rc = tpm2_map_response_header(chip, cc, buf, *bufsiz); + rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz); if (rc) { tpm2_flush_space(chip); - return rc; + goto out; } - rc = tpm2_map_response_body(chip, cc, buf, *bufsiz); + rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz); if (rc) { tpm2_flush_space(chip); - return rc; + goto out; } rc = tpm2_save_space(chip); if (rc) { tpm2_flush_space(chip); - return rc; + goto out; } *bufsiz = be32_to_cpu(header->length); @@ -526,4 +565,7 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE); return 0; +out: + dev_err(&chip->dev, "%s: error %d\n", __func__, rc); + return rc; } diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 66a14526aaf4..a290b30a0c35 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -105,7 +105,7 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) iowrite8(buf[i], priv->iobase); } - return count; + return 0; } static void tpm_atml_cancel(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 36952ef98f90..763fc7e6c005 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -287,19 +287,29 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) struct crb_priv *priv = dev_get_drvdata(&chip->dev); unsigned int expected; - /* sanity check */ - if (count < 6) + /* A sanity check that the upper layer wants to get at least the header + * as that is the minimum size for any TPM response. + */ + if (count < TPM_HEADER_SIZE) return -EIO; + /* If this bit is set, according to the spec, the TPM is in + * unrecoverable condition. + */ if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR) return -EIO; - memcpy_fromio(buf, priv->rsp, 6); - expected = be32_to_cpup((__be32 *) &buf[2]); - if (expected > count || expected < 6) + /* Read the first 8 bytes in order to get the length of the response. + * We read exactly a quad word in order to make sure that the remaining + * reads will be aligned. + */ + memcpy_fromio(buf, priv->rsp, 8); + + expected = be32_to_cpup((__be32 *)&buf[2]); + if (expected > count || expected < TPM_HEADER_SIZE) return -EIO; - memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6); + memcpy_fromio(&buf[8], &priv->rsp[8], expected - 8); return expected; } diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 95ce2e9ccdc6..8a7e80923091 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -46,7 +46,7 @@ struct priv_data { /* This is the amount we read on the first try. 25 was chosen to fit a * fair number of read responses in the buffer so a 2nd retry can be * avoided in small message cases. */ - u8 buffer[sizeof(struct tpm_output_header) + 25]; + u8 buffer[sizeof(struct tpm_header) + 25]; }; static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) @@ -65,15 +65,22 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) dev_dbg(&chip->dev, "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__, (int)min_t(size_t, 64, len), buf, len, status); - return status; + + if (status < 0) + return status; + + /* The upper layer does not support incomplete sends. */ + if (status != len) + return -E2BIG; + + return 0; } static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) { struct priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); - struct tpm_output_header *hdr = - (struct tpm_output_header *)priv->buffer; + struct tpm_header *hdr = (struct tpm_header *)priv->buffer; u32 expected_len; int rc; diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 9086edc9066b..3b4e9672ff6c 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -26,8 +26,7 @@ #include <linux/wait.h> #include "tpm.h" -/* max. buffer size supported by our TPM */ -#define TPM_BUFSIZE 1260 +#define TPM_I2C_INFINEON_BUFSIZE 1260 /* max. number of iterations after I2C NAK */ #define MAX_COUNT 3 @@ -63,11 +62,13 @@ enum i2c_chip_type { UNKNOWN, }; -/* Structure to store I2C TPM specific stuff */ struct tpm_inf_dev { struct i2c_client *client; int locality; - u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ + /* In addition to the data itself, the buffer must fit the 7-bit I2C + * address and the direction bit. + */ + u8 buf[TPM_I2C_INFINEON_BUFSIZE + 1]; struct tpm_chip *chip; enum i2c_chip_type chip_type; unsigned int adapterlimit; @@ -219,7 +220,7 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, .buf = tpm_dev.buf }; - if (len > TPM_BUFSIZE) + if (len > TPM_I2C_INFINEON_BUFSIZE) return -EINVAL; if (!tpm_dev.client->adapter->algo->master_xfer) @@ -527,8 +528,8 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) u8 retries = 0; u8 sts = TPM_STS_GO; - if (len > TPM_BUFSIZE) - return -E2BIG; /* command is too long for our tpm, sorry */ + if (len > TPM_I2C_INFINEON_BUFSIZE) + return -E2BIG; if (request_locality(chip, 0) < 0) return -EBUSY; @@ -587,7 +588,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) /* go and do it */ iic_tpm_write(TPM_STS(tpm_dev.locality), &sts, 1); - return len; + return 0; out_err: tpm_tis_i2c_ready(chip); /* The TPM needs some time to clean up here, diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 217f7f1cbde8..315a3b4548f7 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -35,14 +35,12 @@ #include "tpm.h" /* I2C interface offsets */ -#define TPM_STS 0x00 -#define TPM_BURST_COUNT 0x01 -#define TPM_DATA_FIFO_W 0x20 -#define TPM_DATA_FIFO_R 0x40 -#define TPM_VID_DID_RID 0x60 -/* TPM command header size */ -#define TPM_HEADER_SIZE 10 -#define TPM_RETRY 5 +#define TPM_STS 0x00 +#define TPM_BURST_COUNT 0x01 +#define TPM_DATA_FIFO_W 0x20 +#define TPM_DATA_FIFO_R 0x40 +#define TPM_VID_DID_RID 0x60 +#define TPM_I2C_RETRIES 5 /* * I2C bus device maximum buffer size w/o counting I2C address or command * i.e. max size required for I2C write is 34 = addr, command, 32 bytes data @@ -292,7 +290,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) dev_err(dev, "%s() count < header size\n", __func__); return -EIO; } - for (retries = 0; retries < TPM_RETRY; retries++) { + for (retries = 0; retries < TPM_I2C_RETRIES; retries++) { if (retries > 0) { /* if this is not the first trial, set responseRetry */ i2c_nuvoton_write_status(client, @@ -467,7 +465,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) } dev_dbg(dev, "%s() -> %zd\n", __func__, len); - return len; + return 0; } static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status) diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 07b5a487d0c8..757ca45b39b8 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -139,14 +139,14 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) } /** - * tpm_ibmvtpm_send - Send tpm request - * + * tpm_ibmvtpm_send() - Send a TPM command * @chip: tpm chip struct * @buf: buffer contains data to send * @count: size of buffer * * Return: - * Number of bytes sent or < 0 on error. + * 0 on success, + * -errno on error */ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) { @@ -192,7 +192,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) rc = 0; ibmvtpm->tpm_processing_cmd = false; } else - rc = count; + rc = 0; spin_unlock(&ibmvtpm->rtce_lock); return rc; diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index d8f10047fbba..97f6d4fe0aee 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -354,7 +354,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) for (i = 0; i < count; i++) { wait_and_send(chip, buf[i]); } - return count; + return 0; } static void tpm_inf_cancel(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 5d6cce74cd3f..9bee3c5eb4bf 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -226,7 +226,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) } outb(NSC_COMMAND_EOC, priv->base + NSC_COMMAND); - return count; + return 0; } static void tpm_nsc_cancel(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 86dd8521feef..75e7a856177c 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -20,7 +20,8 @@ #include <linux/acpi.h> #include "tpm.h" -#define TPM_PPI_REVISION_ID 1 +#define TPM_PPI_REVISION_ID_1 1 +#define TPM_PPI_REVISION_ID_2 2 #define TPM_PPI_FN_VERSION 1 #define TPM_PPI_FN_SUBREQ 2 #define TPM_PPI_FN_GETREQ 3 @@ -28,7 +29,7 @@ #define TPM_PPI_FN_GETRSP 5 #define TPM_PPI_FN_SUBREQ2 7 #define TPM_PPI_FN_GETOPR 8 -#define PPI_TPM_REQ_MAX 22 +#define PPI_TPM_REQ_MAX 101 /* PPI 1.3 for TPM 2 */ #define PPI_VS_REQ_START 128 #define PPI_VS_REQ_END 255 @@ -36,14 +37,18 @@ static const guid_t tpm_ppi_guid = GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); +static bool tpm_ppi_req_has_parameter(u64 req) +{ + return req == 23; +} + static inline union acpi_object * tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, - union acpi_object *argv4) + union acpi_object *argv4, u64 rev) { BUG_ON(!ppi_handle); return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, - func, argv4, type); + rev, func, argv4, type); } static ssize_t tpm_show_ppi_version(struct device *dev, @@ -60,9 +65,14 @@ static ssize_t tpm_show_ppi_request(struct device *dev, ssize_t size = -EINVAL; union acpi_object *obj; struct tpm_chip *chip = to_tpm_chip(dev); + u64 rev = TPM_PPI_REVISION_ID_2; + u64 req; + + if (strcmp(chip->ppi_version, "1.2") < 0) + rev = TPM_PPI_REVISION_ID_1; obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, - ACPI_TYPE_PACKAGE, NULL); + ACPI_TYPE_PACKAGE, NULL, rev); if (!obj) return -ENXIO; @@ -72,7 +82,23 @@ static ssize_t tpm_show_ppi_request(struct device *dev, * error. The second is pending TPM operation requested by the OS, 0 * means none and >0 means operation value. */ - if (obj->package.count == 2 && + if (obj->package.count == 3 && + obj->package.elements[0].type == ACPI_TYPE_INTEGER && + obj->package.elements[1].type == ACPI_TYPE_INTEGER && + obj->package.elements[2].type == ACPI_TYPE_INTEGER) { + if (obj->package.elements[0].integer.value) + size = -EFAULT; + else { + req = obj->package.elements[1].integer.value; + if (tpm_ppi_req_has_parameter(req)) + size = scnprintf(buf, PAGE_SIZE, + "%llu %llu\n", req, + obj->package.elements[2].integer.value); + else + size = scnprintf(buf, PAGE_SIZE, + "%llu\n", req); + } + } else if (obj->package.count == 2 && obj->package.elements[0].type == ACPI_TYPE_INTEGER && obj->package.elements[1].type == ACPI_TYPE_INTEGER) { if (obj->package.elements[0].integer.value) @@ -94,9 +120,10 @@ static ssize_t tpm_store_ppi_request(struct device *dev, u32 req; u64 ret; int func = TPM_PPI_FN_SUBREQ; - union acpi_object *obj, tmp; - union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp); + union acpi_object *obj, tmp[2]; + union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(2, tmp); struct tpm_chip *chip = to_tpm_chip(dev); + u64 rev = TPM_PPI_REVISION_ID_1; /* * the function to submit TPM operation request to pre-os environment @@ -104,7 +131,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, * version 1.1 */ if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_SUBREQ2)) + TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_SUBREQ2)) func = TPM_PPI_FN_SUBREQ2; /* @@ -113,20 +140,29 @@ static ssize_t tpm_store_ppi_request(struct device *dev, * string/package type. For PPI version 1.0 and 1.1, use buffer type * for compatibility, and use package type since 1.2 according to spec. */ - if (strcmp(chip->ppi_version, "1.2") < 0) { + if (strcmp(chip->ppi_version, "1.3") == 0) { + if (sscanf(buf, "%llu %llu", &tmp[0].integer.value, + &tmp[1].integer.value) != 2) + goto ppi12; + rev = TPM_PPI_REVISION_ID_2; + tmp[0].type = ACPI_TYPE_INTEGER; + tmp[1].type = ACPI_TYPE_INTEGER; + } else if (strcmp(chip->ppi_version, "1.2") < 0) { if (sscanf(buf, "%d", &req) != 1) return -EINVAL; argv4.type = ACPI_TYPE_BUFFER; argv4.buffer.length = sizeof(req); argv4.buffer.pointer = (u8 *)&req; } else { - tmp.type = ACPI_TYPE_INTEGER; - if (sscanf(buf, "%llu", &tmp.integer.value) != 1) +ppi12: + argv4.package.count = 1; + tmp[0].type = ACPI_TYPE_INTEGER; + if (sscanf(buf, "%llu", &tmp[0].integer.value) != 1) return -EINVAL; } obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, - &argv4); + &argv4, rev); if (!obj) { return -ENXIO; } else { @@ -170,7 +206,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, if (strcmp(chip->ppi_version, "1.2") < 0) obj = &tmp; obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT, - ACPI_TYPE_INTEGER, obj); + ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID_1); if (!obj) { return -ENXIO; } else { @@ -196,7 +232,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev, struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, - ACPI_TYPE_PACKAGE, NULL); + ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); if (!obj) return -ENXIO; @@ -264,7 +300,7 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, "User not required", }; - if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID, + if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_GETOPR)) return -EPERM; @@ -272,7 +308,8 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, for (i = start; i <= end; i++) { tmp.integer.value = i; obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR, - ACPI_TYPE_INTEGER, &argv); + ACPI_TYPE_INTEGER, &argv, + TPM_PPI_REVISION_ID_1); if (!obj) { return -ENOMEM; } else { @@ -338,12 +375,13 @@ void tpm_add_ppi(struct tpm_chip *chip) return; if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION)) + TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_VERSION)) return; /* Cache PPI version string. */ obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, TPM_PPI_FN_VERSION, + TPM_PPI_REVISION_ID_1, + TPM_PPI_FN_VERSION, NULL, ACPI_TYPE_STRING); if (obj) { strlcpy(chip->ppi_version, obj->string.pointer, diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index bf7e49cfa643..b9f64684c3fb 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -481,7 +481,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) goto out_err; } } - return len; + return 0; out_err: tpm_tis_ready(chip); return rc; @@ -521,35 +521,38 @@ static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } }, }; -static bool tpm_tis_update_timeouts(struct tpm_chip *chip, +static void tpm_tis_update_timeouts(struct tpm_chip *chip, unsigned long *timeout_cap) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int i, rc; u32 did_vid; + chip->timeout_adjusted = false; + if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, true); rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid); - if (rc < 0) + if (rc < 0) { + dev_warn(&chip->dev, "%s: failed to read did_vid: %d\n", + __func__, rc); goto out; + } for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { if (vendor_timeout_overrides[i].did_vid != did_vid) continue; memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us, sizeof(vendor_timeout_overrides[i].timeout_us)); - rc = true; + chip->timeout_adjusted = true; } - rc = false; - out: if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); - return rc; + return; } /* @@ -913,7 +916,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, intmask &= ~TPM_GLOBAL_INT_ENABLE; tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); + rc = tpm_chip_start(chip); + if (rc) + goto out_err; rc = tpm2_probe(chip); + tpm_chip_stop(chip); if (rc) goto out_err; diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 87a0ce47f201..d74f3de74ae6 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -303,9 +303,9 @@ out: static int vtpm_proxy_is_driver_command(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_input_header *hdr = (struct tpm_input_header *)buf; + struct tpm_header *hdr = (struct tpm_header *)buf; - if (count < sizeof(struct tpm_input_header)) + if (count < sizeof(struct tpm_header)) return 0; if (chip->flags & TPM_CHIP_FLAG_TPM2) { @@ -335,7 +335,6 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip, static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) { struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); - int rc = 0; if (count > sizeof(proxy_dev->buffer)) { dev_err(&chip->dev, @@ -366,7 +365,7 @@ static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) wake_up_interruptible(&proxy_dev->wq); - return rc; + return 0; } static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip) @@ -402,7 +401,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) { struct tpm_buf buf; int rc; - const struct tpm_output_header *header; + const struct tpm_header *header; struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -417,9 +416,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) proxy_dev->state |= STATE_DRIVER_COMMAND; - rc = tpm_transmit_cmd(chip, NULL, buf.data, tpm_buf_length(&buf), 0, - TPM_TRANSMIT_NESTED, - "attempting to set locality"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality"); proxy_dev->state &= ~STATE_DRIVER_COMMAND; @@ -428,7 +425,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) goto out; } - header = (const struct tpm_output_header *)buf.data; + header = (const struct tpm_header *)buf.data; rc = be32_to_cpu(header->return_code); if (rc) locality = -1; diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index b150f87f38f5..4e2d00cb0d81 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -163,7 +163,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) wmb(); notify_remote_via_evtchn(priv->evtchn); - ordinal = be32_to_cpu(((struct tpm_input_header*)buf)->ordinal); + ordinal = be32_to_cpu(((struct tpm_header *)buf)->ordinal); duration = tpm_calc_ordinal_duration(chip, ordinal); if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration, @@ -173,7 +173,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) return -ETIME; } - return count; + return 0; } static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index d2f0bb5ba47e..e705aab9e38b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -290,6 +290,12 @@ config COMMON_CLK_BD718XX This driver supports ROHM BD71837 and ROHM BD71847 PMICs clock gates. +config COMMON_CLK_FIXED_MMIO + bool "Clock driver for Memory Mapped Fixed values" + depends on COMMON_CLK && OF + help + Support for Memory Mapped IO Fixed clocks + source "drivers/clk/actions/Kconfig" source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 8a9440a97500..1db133652f0c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o +obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o @@ -78,7 +79,7 @@ obj-$(CONFIG_ARCH_K3) += keystone/ obj-$(CONFIG_ARCH_KEYSTONE) += keystone/ obj-$(CONFIG_MACH_LOONGSON32) += loongson1/ obj-y += mediatek/ -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += meson/ +obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_MACH_PIC32) += microchip/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ diff --git a/drivers/clk/actions/Kconfig b/drivers/clk/actions/Kconfig index 04f0a6355726..5b45ca35757e 100644 --- a/drivers/clk/actions/Kconfig +++ b/drivers/clk/actions/Kconfig @@ -9,6 +9,11 @@ if CLK_ACTIONS # SoC Drivers +config CLK_OWL_S500 + bool "Support for the Actions Semi OWL S500 clocks" + depends on ARCH_ACTIONS || COMPILE_TEST + default ARCH_ACTIONS + config CLK_OWL_S700 bool "Support for the Actions Semi OWL S700 clocks" depends on (ARM64 && ARCH_ACTIONS) || COMPILE_TEST diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index ccfdf9781cef..a2588e55c790 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -10,5 +10,6 @@ clk-owl-y += owl-pll.o clk-owl-y += owl-reset.o # SoC support +obj-$(CONFIG_CLK_OWL_S500) += owl-s500.o obj-$(CONFIG_CLK_OWL_S700) += owl-s700.o obj-$(CONFIG_CLK_OWL_S900) += owl-s900.o diff --git a/drivers/clk/actions/owl-pll.c b/drivers/clk/actions/owl-pll.c index 058e06d7099f..02437bdedf4d 100644 --- a/drivers/clk/actions/owl-pll.c +++ b/drivers/clk/actions/owl-pll.c @@ -179,7 +179,7 @@ static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate, regmap_write(common->regmap, pll_hw->reg, reg); - udelay(PLL_STABILITY_WAIT_US); + udelay(pll_hw->delay); return 0; } diff --git a/drivers/clk/actions/owl-pll.h b/drivers/clk/actions/owl-pll.h index 0aae30abd5dc..6fb0d45bb088 100644 --- a/drivers/clk/actions/owl-pll.h +++ b/drivers/clk/actions/owl-pll.h @@ -13,6 +13,8 @@ #include "owl-common.h" +#define OWL_PLL_DEF_DELAY 50 + /* last entry should have rate = 0 */ struct clk_pll_table { unsigned int val; @@ -27,6 +29,7 @@ struct owl_pll_hw { u8 width; u8 min_mul; u8 max_mul; + u8 delay; const struct clk_pll_table *table; }; @@ -36,7 +39,7 @@ struct owl_pll { }; #define OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \ - _width, _min_mul, _max_mul, _table) \ + _width, _min_mul, _max_mul, _delay, _table) \ { \ .reg = _reg, \ .bfreq = _bfreq, \ @@ -45,6 +48,7 @@ struct owl_pll { .width = _width, \ .min_mul = _min_mul, \ .max_mul = _max_mul, \ + .delay = _delay, \ .table = _table, \ } @@ -52,8 +56,8 @@ struct owl_pll { _shift, _width, _min_mul, _max_mul, _table, _flags) \ struct owl_pll _struct = { \ .pll_hw = OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \ - _width, _min_mul, \ - _max_mul, _table), \ + _width, _min_mul, _max_mul, \ + OWL_PLL_DEF_DELAY, _table), \ .common = { \ .regmap = NULL, \ .hw.init = CLK_HW_INIT(_name, \ @@ -67,8 +71,23 @@ struct owl_pll { _shift, _width, _min_mul, _max_mul, _table, _flags) \ struct owl_pll _struct = { \ .pll_hw = OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \ - _width, _min_mul, \ - _max_mul, _table), \ + _width, _min_mul, _max_mul, \ + OWL_PLL_DEF_DELAY, _table), \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT_NO_PARENT(_name, \ + &owl_pll_ops, \ + _flags), \ + }, \ + } + +#define OWL_PLL_NO_PARENT_DELAY(_struct, _name, _reg, _bfreq, _bit_idx, \ + _shift, _width, _min_mul, _max_mul, _delay, _table, \ + _flags) \ + struct owl_pll _struct = { \ + .pll_hw = OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \ + _width, _min_mul, _max_mul, \ + _delay, _table), \ .common = { \ .regmap = NULL, \ .hw.init = CLK_HW_INIT_NO_PARENT(_name, \ @@ -78,7 +97,6 @@ struct owl_pll { } #define mul_mask(m) ((1 << ((m)->width)) - 1) -#define PLL_STABILITY_WAIT_US (50) static inline struct owl_pll *hw_to_owl_pll(const struct clk_hw *hw) { diff --git a/drivers/clk/actions/owl-s500.c b/drivers/clk/actions/owl-s500.c new file mode 100644 index 000000000000..e2007ac4d235 --- /dev/null +++ b/drivers/clk/actions/owl-s500.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Actions Semi Owl S500 SoC clock driver + * + * Copyright (c) 2014 Actions Semi Inc. + * Author: David Liu <liuwei@actions-semi.com> + * + * Copyright (c) 2018 Linaro Ltd. + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + * + * Copyright (c) 2018 LSI-TEC - Caninos Loucos + * Author: Edgar Bernardi Righi <edgar.righi@lsitec.org.br> + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "owl-common.h" +#include "owl-composite.h" +#include "owl-divider.h" +#include "owl-factor.h" +#include "owl-fixed-factor.h" +#include "owl-gate.h" +#include "owl-mux.h" +#include "owl-pll.h" + +#include <dt-bindings/clock/actions,s500-cmu.h> + +#define CMU_COREPLL (0x0000) +#define CMU_DEVPLL (0x0004) +#define CMU_DDRPLL (0x0008) +#define CMU_NANDPLL (0x000C) +#define CMU_DISPLAYPLL (0x0010) +#define CMU_AUDIOPLL (0x0014) +#define CMU_TVOUTPLL (0x0018) +#define CMU_BUSCLK (0x001C) +#define CMU_SENSORCLK (0x0020) +#define CMU_LCDCLK (0x0024) +#define CMU_DSICLK (0x0028) +#define CMU_CSICLK (0x002C) +#define CMU_DECLK (0x0030) +#define CMU_BISPCLK (0x0034) +#define CMU_BUSCLK1 (0x0038) +#define CMU_VDECLK (0x0040) +#define CMU_VCECLK (0x0044) +#define CMU_NANDCCLK (0x004C) +#define CMU_SD0CLK (0x0050) +#define CMU_SD1CLK (0x0054) +#define CMU_SD2CLK (0x0058) +#define CMU_UART0CLK (0x005C) +#define CMU_UART1CLK (0x0060) +#define CMU_UART2CLK (0x0064) +#define CMU_PWM4CLK (0x0068) +#define CMU_PWM5CLK (0x006C) +#define CMU_PWM0CLK (0x0070) +#define CMU_PWM1CLK (0x0074) +#define CMU_PWM2CLK (0x0078) +#define CMU_PWM3CLK (0x007C) +#define CMU_USBPLL (0x0080) +#define CMU_ETHERNETPLL (0x0084) +#define CMU_CVBSPLL (0x0088) +#define CMU_LENSCLK (0x008C) +#define CMU_GPU3DCLK (0x0090) +#define CMU_CORECTL (0x009C) +#define CMU_DEVCLKEN0 (0x00A0) +#define CMU_DEVCLKEN1 (0x00A4) +#define CMU_DEVRST0 (0x00A8) +#define CMU_DEVRST1 (0x00AC) +#define CMU_UART3CLK (0x00B0) +#define CMU_UART4CLK (0x00B4) +#define CMU_UART5CLK (0x00B8) +#define CMU_UART6CLK (0x00BC) +#define CMU_SSCLK (0x00C0) +#define CMU_DIGITALDEBUG (0x00D0) +#define CMU_ANALOGDEBUG (0x00D4) +#define CMU_COREPLLDEBUG (0x00D8) +#define CMU_DEVPLLDEBUG (0x00DC) +#define CMU_DDRPLLDEBUG (0x00E0) +#define CMU_NANDPLLDEBUG (0x00E4) +#define CMU_DISPLAYPLLDEBUG (0x00E8) +#define CMU_TVOUTPLLDEBUG (0x00EC) +#define CMU_DEEPCOLORPLLDEBUG (0x00F4) +#define CMU_AUDIOPLL_ETHPLLDEBUG (0x00F8) +#define CMU_CVBSPLLDEBUG (0x00FC) + +#define OWL_S500_COREPLL_DELAY (150) +#define OWL_S500_DDRPLL_DELAY (63) +#define OWL_S500_DEVPLL_DELAY (28) +#define OWL_S500_NANDPLL_DELAY (44) +#define OWL_S500_DISPLAYPLL_DELAY (57) +#define OWL_S500_ETHERNETPLL_DELAY (25) +#define OWL_S500_AUDIOPLL_DELAY (100) + +static const struct clk_pll_table clk_audio_pll_table[] = { + { 0, 45158400 }, { 1, 49152000 }, + { 0, 0 }, +}; + +/* pll clocks */ +static OWL_PLL_NO_PARENT_DELAY(ethernet_pll_clk, "ethernet_pll_clk", CMU_ETHERNETPLL, 500000000, 0, 0, 0, 0, 0, OWL_S500_ETHERNETPLL_DELAY, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT_DELAY(core_pll_clk, "core_pll_clk", CMU_COREPLL, 12000000, 9, 0, 8, 4, 134, OWL_S500_COREPLL_DELAY, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT_DELAY(ddr_pll_clk, "ddr_pll_clk", CMU_DDRPLL, 12000000, 8, 0, 8, 1, 67, OWL_S500_DDRPLL_DELAY, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT_DELAY(nand_pll_clk, "nand_pll_clk", CMU_NANDPLL, 6000000, 8, 0, 7, 2, 86, OWL_S500_NANDPLL_DELAY, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT_DELAY(display_pll_clk, "display_pll_clk", CMU_DISPLAYPLL, 6000000, 8, 0, 8, 2, 126, OWL_S500_DISPLAYPLL_DELAY, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT_DELAY(dev_pll_clk, "dev_pll_clk", CMU_DEVPLL, 6000000, 8, 0, 7, 8, 126, OWL_S500_DEVPLL_DELAY, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT_DELAY(audio_pll_clk, "audio_pll_clk", CMU_AUDIOPLL, 0, 4, 0, 1, 0, 0, OWL_S500_AUDIOPLL_DELAY, clk_audio_pll_table, CLK_IGNORE_UNUSED); + +static const char * const dev_clk_mux_p[] = { "hosc", "dev_pll_clk" }; +static const char * const bisp_clk_mux_p[] = { "display_pll_clk", "dev_clk" }; +static const char * const sensor_clk_mux_p[] = { "hosc", "bisp_clk" }; +static const char * const sd_clk_mux_p[] = { "dev_clk", "nand_pll_clk" }; +static const char * const pwm_clk_mux_p[] = { "losc", "hosc" }; +static const char * const ahbprediv_clk_mux_p[] = { "dev_clk", "display_pll_clk", "nand_pll_clk", "ddr_pll_clk" }; +static const char * const uart_clk_mux_p[] = { "hosc", "dev_pll_clk" }; +static const char * const de_clk_mux_p[] = { "display_pll_clk", "dev_clk" }; +static const char * const i2s_clk_mux_p[] = { "audio_pll_clk" }; +static const char * const hde_clk_mux_p[] = { "dev_clk", "display_pll_clk", "nand_pll_clk", "ddr_pll_clk" }; +static const char * const nand_clk_mux_p[] = { "nand_pll_clk", "display_pll_clk", "dev_clk", "ddr_pll_clk" }; + +static struct clk_factor_table sd_factor_table[] = { + /* bit0 ~ 4 */ + { 0, 1, 1 }, { 1, 1, 2 }, { 2, 1, 3 }, { 3, 1, 4 }, + { 4, 1, 5 }, { 5, 1, 6 }, { 6, 1, 7 }, { 7, 1, 8 }, + { 8, 1, 9 }, { 9, 1, 10 }, { 10, 1, 11 }, { 11, 1, 12 }, + { 12, 1, 13 }, { 13, 1, 14 }, { 14, 1, 15 }, { 15, 1, 16 }, + { 16, 1, 17 }, { 17, 1, 18 }, { 18, 1, 19 }, { 19, 1, 20 }, + { 20, 1, 21 }, { 21, 1, 22 }, { 22, 1, 23 }, { 23, 1, 24 }, + { 24, 1, 25 }, { 25, 1, 26 }, { 26, 1, 27 }, { 27, 1, 28 }, + { 28, 1, 29 }, { 29, 1, 30 }, { 30, 1, 31 }, { 31, 1, 32 }, + + /* bit8: /128 */ + { 256, 1, 1 * 128 }, { 257, 1, 2 * 128 }, { 258, 1, 3 * 128 }, { 259, 1, 4 * 128 }, + { 260, 1, 5 * 128 }, { 261, 1, 6 * 128 }, { 262, 1, 7 * 128 }, { 263, 1, 8 * 128 }, + { 264, 1, 9 * 128 }, { 265, 1, 10 * 128 }, { 266, 1, 11 * 128 }, { 267, 1, 12 * 128 }, + { 268, 1, 13 * 128 }, { 269, 1, 14 * 128 }, { 270, 1, 15 * 128 }, { 271, 1, 16 * 128 }, + { 272, 1, 17 * 128 }, { 273, 1, 18 * 128 }, { 274, 1, 19 * 128 }, { 275, 1, 20 * 128 }, + { 276, 1, 21 * 128 }, { 277, 1, 22 * 128 }, { 278, 1, 23 * 128 }, { 279, 1, 24 * 128 }, + { 280, 1, 25 * 128 }, { 281, 1, 26 * 128 }, { 282, 1, 27 * 128 }, { 283, 1, 28 * 128 }, + { 284, 1, 29 * 128 }, { 285, 1, 30 * 128 }, { 286, 1, 31 * 128 }, { 287, 1, 32 * 128 }, + { 0, 0, 0 }, +}; + +static struct clk_factor_table bisp_factor_table[] = { + { 0, 1, 1 }, { 1, 1, 2 }, { 2, 1, 3 }, { 3, 1, 4 }, + { 4, 1, 5 }, { 5, 1, 6 }, { 6, 1, 7 }, { 7, 1, 8 }, + { 0, 0, 0 }, +}; + +static struct clk_factor_table ahb_factor_table[] = { + { 1, 1, 2 }, { 2, 1, 3 }, + { 0, 0, 0 }, +}; + +static struct clk_div_table rmii_ref_div_table[] = { + { 0, 4 }, { 1, 10 }, + { 0, 0 }, +}; + +static struct clk_div_table i2s_div_table[] = { + { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, + { 4, 6 }, { 5, 8 }, { 6, 12 }, { 7, 16 }, + { 8, 24 }, + { 0, 0 }, +}; + +static struct clk_div_table nand_div_table[] = { + { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 6 }, + { 4, 8 }, { 5, 10 }, { 6, 12 }, { 7, 14 }, + { 8, 16 }, { 9, 18 }, { 10, 20 }, { 11, 22 }, + { 0, 0 }, +}; + +/* mux clock */ +static OWL_MUX(dev_clk, "dev_clk", dev_clk_mux_p, CMU_DEVPLL, 12, 1, CLK_SET_RATE_PARENT); +static OWL_MUX(ahbprediv_clk, "ahbprediv_clk", ahbprediv_clk_mux_p, CMU_BUSCLK1, 8, 3, CLK_SET_RATE_PARENT); + +/* gate clocks */ +static OWL_GATE(spi0_clk, "spi0_clk", "ahb_clk", CMU_DEVCLKEN1, 10, 0, CLK_IGNORE_UNUSED); +static OWL_GATE(spi1_clk, "spi1_clk", "ahb_clk", CMU_DEVCLKEN1, 11, 0, CLK_IGNORE_UNUSED); +static OWL_GATE(spi2_clk, "spi2_clk", "ahb_clk", CMU_DEVCLKEN1, 12, 0, CLK_IGNORE_UNUSED); +static OWL_GATE(spi3_clk, "spi3_clk", "ahb_clk", CMU_DEVCLKEN1, 13, 0, CLK_IGNORE_UNUSED); +static OWL_GATE(timer_clk, "timer_clk", "hosc", CMU_DEVCLKEN1, 27, 0, 0); +static OWL_GATE(hdmi_clk, "hdmi_clk", "hosc", CMU_DEVCLKEN1, 3, 0, 0); + +/* divider clocks */ +static OWL_DIVIDER(h_clk, "h_clk", "ahbprevdiv_clk", CMU_BUSCLK1, 12, 2, NULL, 0, 0); +static OWL_DIVIDER(rmii_ref_clk, "rmii_ref_clk", "ethernet_pll_clk", CMU_ETHERNETPLL, 1, 1, rmii_ref_div_table, 0, 0); + +/* factor clocks */ +static OWL_FACTOR(ahb_clk, "ahb_clk", "h_clk", CMU_BUSCLK1, 2, 2, ahb_factor_table, 0, 0); +static OWL_FACTOR(de1_clk, "de_clk1", "de_clk", CMU_DECLK, 0, 3, bisp_factor_table, 0, 0); +static OWL_FACTOR(de2_clk, "de_clk2", "de_clk", CMU_DECLK, 4, 3, bisp_factor_table, 0, 0); + +/* composite clocks */ +static OWL_COMP_FACTOR(vce_clk, "vce_clk", hde_clk_mux_p, + OWL_MUX_HW(CMU_VCECLK, 4, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 26, 0), + OWL_FACTOR_HW(CMU_VCECLK, 0, 3, 0, bisp_factor_table), + 0); + +static OWL_COMP_FACTOR(vde_clk, "vde_clk", hde_clk_mux_p, + OWL_MUX_HW(CMU_VDECLK, 4, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 25, 0), + OWL_FACTOR_HW(CMU_VDECLK, 0, 3, 0, bisp_factor_table), + 0); + +static OWL_COMP_FACTOR(bisp_clk, "bisp_clk", bisp_clk_mux_p, + OWL_MUX_HW(CMU_BISPCLK, 4, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 14, 0), + OWL_FACTOR_HW(CMU_BISPCLK, 0, 3, 0, bisp_factor_table), + 0); + +static OWL_COMP_FACTOR(sensor0_clk, "sensor0_clk", sensor_clk_mux_p, + OWL_MUX_HW(CMU_SENSORCLK, 4, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 14, 0), + OWL_FACTOR_HW(CMU_SENSORCLK, 0, 3, 0, bisp_factor_table), + CLK_IGNORE_UNUSED); + +static OWL_COMP_FACTOR(sensor1_clk, "sensor1_clk", sensor_clk_mux_p, + OWL_MUX_HW(CMU_SENSORCLK, 4, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 14, 0), + OWL_FACTOR_HW(CMU_SENSORCLK, 8, 3, 0, bisp_factor_table), + CLK_IGNORE_UNUSED); + +static OWL_COMP_FACTOR(sd0_clk, "sd0_clk", sd_clk_mux_p, + OWL_MUX_HW(CMU_SD0CLK, 9, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 5, 0), + OWL_FACTOR_HW(CMU_SD0CLK, 0, 9, 0, sd_factor_table), + 0); + +static OWL_COMP_FACTOR(sd1_clk, "sd1_clk", sd_clk_mux_p, + OWL_MUX_HW(CMU_SD1CLK, 9, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 6, 0), + OWL_FACTOR_HW(CMU_SD1CLK, 0, 9, 0, sd_factor_table), + 0); + +static OWL_COMP_FACTOR(sd2_clk, "sd2_clk", sd_clk_mux_p, + OWL_MUX_HW(CMU_SD2CLK, 9, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 7, 0), + OWL_FACTOR_HW(CMU_SD2CLK, 0, 9, 0, sd_factor_table), + 0); + +static OWL_COMP_DIV(pwm0_clk, "pwm0_clk", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM0CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 23, 0), + OWL_DIVIDER_HW(CMU_PWM0CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(pwm1_clk, "pwm1_clk", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM1CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 24, 0), + OWL_DIVIDER_HW(CMU_PWM1CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(pwm2_clk, "pwm2_clk", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM2CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 25, 0), + OWL_DIVIDER_HW(CMU_PWM2CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(pwm3_clk, "pwm3_clk", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM3CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 26, 0), + OWL_DIVIDER_HW(CMU_PWM3CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(pwm4_clk, "pwm4_clk", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM4CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 11, 0), + OWL_DIVIDER_HW(CMU_PWM4CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(pwm5_clk, "pwm5_clk", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM5CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 0, 0), + OWL_DIVIDER_HW(CMU_PWM5CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_PASS(de_clk, "de_clk", de_clk_mux_p, + OWL_MUX_HW(CMU_DECLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 8, 0), + 0); + +static OWL_COMP_FIXED_FACTOR(i2c0_clk, "i2c0_clk", "ethernet_pll_clk", + OWL_GATE_HW(CMU_DEVCLKEN1, 14, 0), + 1, 5, 0); + +static OWL_COMP_FIXED_FACTOR(i2c1_clk, "i2c1_clk", "ethernet_pll_clk", + OWL_GATE_HW(CMU_DEVCLKEN1, 15, 0), + 1, 5, 0); + +static OWL_COMP_FIXED_FACTOR(i2c2_clk, "i2c2_clk", "ethernet_pll_clk", + OWL_GATE_HW(CMU_DEVCLKEN1, 30, 0), + 1, 5, 0); + +static OWL_COMP_FIXED_FACTOR(i2c3_clk, "i2c3_clk", "ethernet_pll_clk", + OWL_GATE_HW(CMU_DEVCLKEN1, 31, 0), + 1, 5, 0); + +static OWL_COMP_DIV(uart0_clk, "uart0_clk", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART0CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 6, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(uart1_clk, "uart1_clk", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART1CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 7, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(uart2_clk, "uart2_clk", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART2CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 8, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(uart3_clk, "uart3_clk", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART3CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 19, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(uart4_clk, "uart4_clk", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART4CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 20, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(uart5_clk, "uart5_clk", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART5CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 21, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(uart6_clk, "uart6_clk", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART6CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 18, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(i2srx_clk, "i2srx_clk", i2s_clk_mux_p, + OWL_MUX_HW(CMU_AUDIOPLL, 24, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 21, 0), + OWL_DIVIDER_HW(CMU_AUDIOPLL, 20, 4, 0, i2s_div_table), + 0); + +static OWL_COMP_DIV(i2stx_clk, "i2stx_clk", i2s_clk_mux_p, + OWL_MUX_HW(CMU_AUDIOPLL, 24, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 20, 0), + OWL_DIVIDER_HW(CMU_AUDIOPLL, 16, 4, 0, i2s_div_table), + 0); + +static OWL_COMP_DIV(hdmia_clk, "hdmia_clk", i2s_clk_mux_p, + OWL_MUX_HW(CMU_AUDIOPLL, 24, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 22, 0), + OWL_DIVIDER_HW(CMU_AUDIOPLL, 24, 4, 0, i2s_div_table), + 0); + +static OWL_COMP_DIV(spdif_clk, "spdif_clk", i2s_clk_mux_p, + OWL_MUX_HW(CMU_AUDIOPLL, 24, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 23, 0), + OWL_DIVIDER_HW(CMU_AUDIOPLL, 28, 4, 0, i2s_div_table), + 0); + +static OWL_COMP_DIV(nand_clk, "nand_clk", nand_clk_mux_p, + OWL_MUX_HW(CMU_NANDCCLK, 8, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 4, 0), + OWL_DIVIDER_HW(CMU_NANDCCLK, 0, 3, 0, nand_div_table), + CLK_SET_RATE_PARENT); + +static OWL_COMP_DIV(ecc_clk, "ecc_clk", nand_clk_mux_p, + OWL_MUX_HW(CMU_NANDCCLK, 8, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 4, 0), + OWL_DIVIDER_HW(CMU_NANDCCLK, 4, 3, 0, nand_div_table), + CLK_SET_RATE_PARENT); + +static struct owl_clk_common *s500_clks[] = { + ðernet_pll_clk.common, + &core_pll_clk.common, + &ddr_pll_clk.common, + &dev_pll_clk.common, + &nand_pll_clk.common, + &audio_pll_clk.common, + &display_pll_clk.common, + &dev_clk.common, + &timer_clk.common, + &i2c0_clk.common, + &i2c1_clk.common, + &i2c2_clk.common, + &i2c3_clk.common, + &uart0_clk.common, + &uart1_clk.common, + &uart2_clk.common, + &uart3_clk.common, + &uart4_clk.common, + &uart5_clk.common, + &uart6_clk.common, + &pwm0_clk.common, + &pwm1_clk.common, + &pwm2_clk.common, + &pwm3_clk.common, + &pwm4_clk.common, + &pwm5_clk.common, + &sensor0_clk.common, + &sensor1_clk.common, + &sd0_clk.common, + &sd1_clk.common, + &sd2_clk.common, + &bisp_clk.common, + &ahb_clk.common, + &ahbprediv_clk.common, + &h_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &spi3_clk.common, + &rmii_ref_clk.common, + &de_clk.common, + &de1_clk.common, + &de2_clk.common, + &i2srx_clk.common, + &i2stx_clk.common, + &hdmia_clk.common, + &hdmi_clk.common, + &vce_clk.common, + &vde_clk.common, + &spdif_clk.common, + &nand_clk.common, + &ecc_clk.common, +}; + +static struct clk_hw_onecell_data s500_hw_clks = { + .hws = { + [CLK_ETHERNET_PLL] = ðernet_pll_clk.common.hw, + [CLK_CORE_PLL] = &core_pll_clk.common.hw, + [CLK_DDR_PLL] = &ddr_pll_clk.common.hw, + [CLK_NAND_PLL] = &nand_pll_clk.common.hw, + [CLK_DISPLAY_PLL] = &display_pll_clk.common.hw, + [CLK_DEV_PLL] = &dev_pll_clk.common.hw, + [CLK_AUDIO_PLL] = &audio_pll_clk.common.hw, + [CLK_TIMER] = &timer_clk.common.hw, + [CLK_DEV] = &dev_clk.common.hw, + [CLK_DE] = &de_clk.common.hw, + [CLK_DE1] = &de1_clk.common.hw, + [CLK_DE2] = &de2_clk.common.hw, + [CLK_I2C0] = &i2c0_clk.common.hw, + [CLK_I2C1] = &i2c1_clk.common.hw, + [CLK_I2C2] = &i2c2_clk.common.hw, + [CLK_I2C3] = &i2c3_clk.common.hw, + [CLK_I2SRX] = &i2srx_clk.common.hw, + [CLK_I2STX] = &i2stx_clk.common.hw, + [CLK_UART0] = &uart0_clk.common.hw, + [CLK_UART1] = &uart1_clk.common.hw, + [CLK_UART2] = &uart2_clk.common.hw, + [CLK_UART3] = &uart3_clk.common.hw, + [CLK_UART4] = &uart4_clk.common.hw, + [CLK_UART5] = &uart5_clk.common.hw, + [CLK_UART6] = &uart6_clk.common.hw, + [CLK_PWM0] = &pwm0_clk.common.hw, + [CLK_PWM1] = &pwm1_clk.common.hw, + [CLK_PWM2] = &pwm2_clk.common.hw, + [CLK_PWM3] = &pwm3_clk.common.hw, + [CLK_PWM4] = &pwm4_clk.common.hw, + [CLK_PWM5] = &pwm5_clk.common.hw, + [CLK_SENSOR0] = &sensor0_clk.common.hw, + [CLK_SENSOR1] = &sensor1_clk.common.hw, + [CLK_SD0] = &sd0_clk.common.hw, + [CLK_SD1] = &sd1_clk.common.hw, + [CLK_SD2] = &sd2_clk.common.hw, + [CLK_BISP] = &bisp_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_AHB] = &ahb_clk.common.hw, + [CLK_H] = &h_clk.common.hw, + [CLK_AHBPREDIV] = &ahbprediv_clk.common.hw, + [CLK_RMII_REF] = &rmii_ref_clk.common.hw, + [CLK_HDMI_AUDIO] = &hdmia_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_VDE] = &vde_clk.common.hw, + [CLK_VCE] = &vce_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_ECC] = &ecc_clk.common.hw, + }, + .num = CLK_NR_CLKS, +}; + +static struct owl_clk_desc s500_clk_desc = { + .clks = s500_clks, + .num_clks = ARRAY_SIZE(s500_clks), + + .hw_clks = &s500_hw_clks, +}; + +static int s500_clk_probe(struct platform_device *pdev) +{ + struct owl_clk_desc *desc; + + desc = &s500_clk_desc; + owl_clk_regmap_init(pdev, desc); + + return owl_clk_probe(&pdev->dev, desc->hw_clks); +} + +static const struct of_device_id s500_clk_of_match[] = { + { .compatible = "actions,s500-cmu", }, + { /* sentinel */ } +}; + +static struct platform_driver s500_clk_driver = { + .probe = s500_clk_probe, + .driver = { + .name = "s500-cmu", + .of_match_table = s500_clk_of_match, + }, +}; + +static int __init s500_clk_init(void) +{ + return platform_driver_register(&s500_clk_driver); +} +core_initcall(s500_clk_init); diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c index 36d77146a3bd..3cc4a82f4e9f 100644 --- a/drivers/clk/at91/clk-audio-pll.c +++ b/drivers/clk/at91/clk-audio-pll.c @@ -340,7 +340,12 @@ static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate, pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate, *parent_rate); - for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) { + if (!rate) + return 0; + + best_parent_rate = clk_round_rate(pclk->clk, 1); + div = max(best_parent_rate / rate, 1UL); + for (; div <= AUDIO_PLL_QDPMC_MAX; div++) { best_parent_rate = clk_round_rate(pclk->clk, rate * div); tmp_rate = best_parent_rate / div; tmp_diff = abs(rate - tmp_rate); @@ -350,6 +355,8 @@ static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate, best_rate = tmp_rate; best_diff = tmp_diff; tmp_qd = div; + if (!best_diff) + break; /* got exact match */ } } diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 5bc68b9c5498..89d6f3736dbf 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -132,11 +132,8 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; unsigned long div = parent_rate / rate; - unsigned int pckr; int shift = 0; - regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr); - if (!div) return -EINVAL; diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c index cd0ef7274fdb..1f70cb164b06 100644 --- a/drivers/clk/at91/sama5d2.c +++ b/drivers/clk/at91/sama5d2.c @@ -241,13 +241,14 @@ static void __init sama5d2_pmc_setup(struct device_node *np) parent_names[2] = "plladivck"; parent_names[3] = "utmick"; parent_names[4] = "masterck"; + parent_names[5] = "audiopll_pmcck"; for (i = 0; i < 3; i++) { char name[6]; snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, - parent_names, 5, i, + parent_names, 6, i, &at91sam9x5_programmable_layout); if (IS_ERR(hw)) goto err_free; diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c index 2c04396402ab..c36c47bdba02 100644 --- a/drivers/clk/clk-clps711x.c +++ b/drivers/clk/clk-clps711x.c @@ -44,21 +44,21 @@ struct clps711x_clk { struct clk_hw_onecell_data clk_data; }; -static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, - u32 fref) +static void __init clps711x_clk_init_dt(struct device_node *np) { - u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi; + u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi, fref = 0; struct clps711x_clk *clps711x_clk; - unsigned i; + void __iomem *base; + + WARN_ON(of_property_read_u32(np, "startup-frequency", &fref)); - if (!base) - return ERR_PTR(-ENOMEM); + base = of_iomap(np, 0); + BUG_ON(!base); clps711x_clk = kzalloc(struct_size(clps711x_clk, clk_data.hws, CLPS711X_CLK_MAX), GFP_KERNEL); - if (!clps711x_clk) - return ERR_PTR(-ENOMEM); + BUG_ON(!clps711x_clk); spin_lock_init(&clps711x_clk->lock); @@ -137,52 +137,13 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, clk_hw_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10); clps711x_clk->clk_data.hws[CLPS711X_CLK_TICK] = clk_hw_register_fixed_rate(NULL, "tick", NULL, 0, 64); - for (i = 0; i < CLPS711X_CLK_MAX; i++) - if (IS_ERR(clps711x_clk->clk_data.hws[i])) + for (tmp = 0; tmp < CLPS711X_CLK_MAX; tmp++) + if (IS_ERR(clps711x_clk->clk_data.hws[tmp])) pr_err("clk %i: register failed with %ld\n", - i, PTR_ERR(clps711x_clk->clk_data.hws[i])); - - return clps711x_clk; -} - -void __init clps711x_clk_init(void __iomem *base) -{ - struct clps711x_clk *clps711x_clk; - - clps711x_clk = _clps711x_clk_init(base, 73728000); - - BUG_ON(IS_ERR(clps711x_clk)); - - /* Clocksource */ - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1], - NULL, "clps711x-timer.0"); - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2], - NULL, "clps711x-timer.1"); - - /* Drivers */ - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM], - NULL, "clps711x-pwm"); - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART], - NULL, "clps711x-uart.0"); - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART], - NULL, "clps711x-uart.1"); -} - -#ifdef CONFIG_OF -static void __init clps711x_clk_init_dt(struct device_node *np) -{ - void __iomem *base = of_iomap(np, 0); - struct clps711x_clk *clps711x_clk; - u32 fref = 0; - - WARN_ON(of_property_read_u32(np, "startup-frequency", &fref)); - - clps711x_clk = _clps711x_clk_init(base, fref); - BUG_ON(IS_ERR(clps711x_clk)); + tmp, PTR_ERR(clps711x_clk->clk_data.hws[tmp])); clps711x_clk->clk_data.num = CLPS711X_CLK_MAX; of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clps711x_clk->clk_data); } CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt); -#endif diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index c9a86156ced8..daa1fc8fba53 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -29,6 +29,17 @@ struct clk *devm_clk_get(struct device *dev, const char *id) } EXPORT_SYMBOL(devm_clk_get); +struct clk *devm_clk_get_optional(struct device *dev, const char *id) +{ + struct clk *clk = devm_clk_get(dev, id); + + if (clk == ERR_PTR(-ENOENT)) + return NULL; + + return clk; +} +EXPORT_SYMBOL(devm_clk_get_optional); + struct clk_bulk_devres { struct clk_bulk_data *clks; int num_clks; diff --git a/drivers/clk/clk-fixed-mmio.c b/drivers/clk/clk-fixed-mmio.c new file mode 100644 index 000000000000..d1a97d971183 --- /dev/null +++ b/drivers/clk/clk-fixed-mmio.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Memory Mapped IO Fixed clock driver + * + * Copyright (C) 2018 Cadence Design Systems, Inc. + * + * Authors: + * Jan Kotas <jank@cadence.com> + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +static struct clk_hw *fixed_mmio_clk_setup(struct device_node *node) +{ + struct clk_hw *clk; + const char *clk_name = node->name; + void __iomem *base; + u32 freq; + int ret; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%pOFn: failed to map address\n", node); + return ERR_PTR(-EIO); + } + + freq = readl(base); + iounmap(base); + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0, freq); + if (IS_ERR(clk)) { + pr_err("%pOFn: failed to register fixed rate clock\n", node); + return clk; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, clk); + if (ret) { + pr_err("%pOFn: failed to add clock provider\n", node); + clk_hw_unregister(clk); + clk = ERR_PTR(ret); + } + + return clk; +} + +static void __init of_fixed_mmio_clk_setup(struct device_node *node) +{ + fixed_mmio_clk_setup(node); +} +CLK_OF_DECLARE(fixed_mmio_clk, "fixed-mmio-clock", of_fixed_mmio_clk_setup); + +/** + * This is not executed when of_fixed_mmio_clk_setup succeeded. + */ +static int of_fixed_mmio_clk_probe(struct platform_device *pdev) +{ + struct clk_hw *clk; + + clk = fixed_mmio_clk_setup(pdev->dev.of_node); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + platform_set_drvdata(pdev, clk); + + return 0; +} + +static int of_fixed_mmio_clk_remove(struct platform_device *pdev) +{ + struct clk_hw *clk = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + clk_hw_unregister_fixed_rate(clk); + + return 0; +} + +static const struct of_device_id of_fixed_mmio_clk_ids[] = { + { .compatible = "fixed-mmio-clock" }, + { } +}; +MODULE_DEVICE_TABLE(of, of_fixed_mmio_clk_ids); + +static struct platform_driver of_fixed_mmio_clk_driver = { + .driver = { + .name = "of_fixed_mmio_clk", + .of_match_table = of_fixed_mmio_clk_ids, + }, + .probe = of_fixed_mmio_clk_probe, + .remove = of_fixed_mmio_clk_remove, +}; +module_platform_driver(of_fixed_mmio_clk_driver); + +MODULE_AUTHOR("Jan Kotas <jank@cadence.com>"); +MODULE_DESCRIPTION("Memory Mapped IO Fixed clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 545dceec0bbf..fdfe2e423d15 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -79,7 +79,7 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long m, n; u64 ret; - if (!rate || rate >= *parent_rate) + if (!rate || (!clk_hw_can_set_rate_parent(hw) && rate >= *parent_rate)) return *parent_rate; if (fd->approximation) diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c index 25eed3e0251f..c2f07f0d077c 100644 --- a/drivers/clk/clk-gpio.c +++ b/drivers/clk/clk-gpio.c @@ -58,6 +58,35 @@ const struct clk_ops clk_gpio_gate_ops = { }; EXPORT_SYMBOL_GPL(clk_gpio_gate_ops); +static int clk_sleeping_gpio_gate_prepare(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + gpiod_set_value_cansleep(clk->gpiod, 1); + + return 0; +} + +static void clk_sleeping_gpio_gate_unprepare(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + gpiod_set_value_cansleep(clk->gpiod, 0); +} + +static int clk_sleeping_gpio_gate_is_prepared(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + return gpiod_get_value_cansleep(clk->gpiod); +} + +static const struct clk_ops clk_sleeping_gpio_gate_ops = { + .prepare = clk_sleeping_gpio_gate_prepare, + .unprepare = clk_sleeping_gpio_gate_unprepare, + .is_prepared = clk_sleeping_gpio_gate_is_prepared, +}; + /** * DOC: basic clock multiplexer which can be controlled with a gpio output * Traits of this clock: @@ -144,10 +173,16 @@ struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name, const char *parent_name, struct gpio_desc *gpiod, unsigned long flags) { + const struct clk_ops *ops; + + if (gpiod_cansleep(gpiod)) + ops = &clk_sleeping_gpio_gate_ops; + else + ops = &clk_gpio_gate_ops; + return clk_register_gpio(dev, name, (parent_name ? &parent_name : NULL), - (parent_name ? 1 : 0), gpiod, flags, - &clk_gpio_gate_ops); + (parent_name ? 1 : 0), gpiod, flags, ops); } EXPORT_SYMBOL_GPL(clk_hw_register_gpio_gate); diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 727ed8e1bb72..8e4581004695 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -293,6 +293,7 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk /* Map system registers */ srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs"); hb_clk->reg = of_iomap(srnp, 0); + of_node_put(srnp); BUG_ON(!hb_clk->reg); hb_clk->reg += reg; diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 22c937644c93..3727d5472450 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -235,8 +235,9 @@ static int max77686_clk_probe(struct platform_device *pdev) return ret; } - ret = clk_hw_register_clkdev(&max_clk_data->hw, - max_clk_data->clk_idata.name, NULL); + ret = devm_clk_hw_register_clkdev(dev, &max_clk_data->hw, + max_clk_data->clk_idata.name, + NULL); if (ret < 0) { dev_err(dev, "Failed to clkdev register: %d\n", ret); return ret; @@ -244,8 +245,8 @@ static int max77686_clk_probe(struct platform_device *pdev) } if (parent->of_node) { - ret = of_clk_add_hw_provider(parent->of_node, of_clk_max77686_get, - drv_data); + ret = devm_of_clk_add_hw_provider(dev, of_clk_max77686_get, + drv_data); if (ret < 0) { dev_err(dev, "Failed to register OF clock provider: %d\n", @@ -261,27 +262,11 @@ static int max77686_clk_probe(struct platform_device *pdev) 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); if (ret < 0) { dev_err(dev, "Failed to config low-jitter: %d\n", ret); - goto remove_of_clk_provider; + return ret; } } return 0; - -remove_of_clk_provider: - if (parent->of_node) - of_clk_del_provider(parent->of_node); - - return ret; -} - -static int max77686_clk_remove(struct platform_device *pdev) -{ - struct device *parent = pdev->dev.parent; - - if (parent->of_node) - of_clk_del_provider(parent->of_node); - - return 0; } static const struct platform_device_id max77686_clk_id[] = { @@ -297,7 +282,6 @@ static struct platform_driver max77686_clk_driver = { .name = "max77686-clk", }, .probe = max77686_clk_probe, - .remove = max77686_clk_remove, .id_table = max77686_clk_id, }; diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 5baa9e051110..1212a9be7e80 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -1148,8 +1148,8 @@ static void __init create_one_pll(struct clockgen *cg, int idx) pll->div[i].clk = clk; ret = clk_register_clkdev(clk, pll->div[i].name, NULL); if (ret != 0) - pr_err("%s: %s: register to lookup table failed %ld\n", - __func__, pll->div[i].name, PTR_ERR(clk)); + pr_err("%s: %s: register to lookup table failed %d\n", + __func__, pll->div[i].name, ret); } } @@ -1389,6 +1389,7 @@ static void __init clockgen_init(struct device_node *np) pr_err("%s: Couldn't map %pOF regs\n", __func__, guts); } + of_node_put(guts); } } diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c index 6a31f7f434ce..a0ae8dc16909 100644 --- a/drivers/clk/clk-stm32mp1.c +++ b/drivers/clk/clk-stm32mp1.c @@ -121,7 +121,7 @@ static const char * const cpu_src[] = { }; static const char * const axi_src[] = { - "ck_hsi", "ck_hse", "pll2_p", "pll3_p" + "ck_hsi", "ck_hse", "pll2_p" }; static const char * const per_src[] = { @@ -225,19 +225,19 @@ static const char * const usart6_src[] = { }; static const char * const fdcan_src[] = { - "ck_hse", "pll3_q", "pll4_q" + "ck_hse", "pll3_q", "pll4_q", "pll4_r" }; static const char * const sai_src[] = { - "pll4_q", "pll3_q", "i2s_ckin", "ck_per" + "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "pll3_r" }; static const char * const sai2_src[] = { - "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb" + "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb", "pll3_r" }; static const char * const adc12_src[] = { - "pll4_q", "ck_per" + "pll4_r", "ck_per", "pll3_q" }; static const char * const dsi_src[] = { @@ -269,7 +269,7 @@ static const struct clk_div_table axi_div_table[] = { static const struct clk_div_table mcu_div_table[] = { { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, { 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 }, - { 8, 512 }, { 9, 512 }, { 10, 512}, { 11, 512 }, + { 8, 256 }, { 9, 512 }, { 10, 512}, { 11, 512 }, { 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 }, { 0 }, }; @@ -1286,10 +1286,11 @@ _clk_stm32_register_composite(struct device *dev, MGATE_MP1(_id, _name, _parent, _flags, _mgate) #define KCLK(_id, _name, _parents, _flags, _mgate, _mmux)\ - COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE | _flags,\ - _MGATE_MP1(_mgate),\ - _MMUX(_mmux),\ - _NO_DIV) + COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE |\ + CLK_SET_RATE_NO_REPARENT | _flags,\ + _MGATE_MP1(_mgate),\ + _MMUX(_mmux),\ + _NO_DIV) enum { G_SAI1, @@ -1655,12 +1656,14 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = { static const struct clock_config stm32mp1_clock_cfg[] = { /* Oscillator divider */ - DIV(NO_ID, "clk-hsi-div", "clk-hsi", 0, RCC_HSICFGR, 0, 2, - CLK_DIVIDER_READ_ONLY), + DIV(NO_ID, "clk-hsi-div", "clk-hsi", CLK_DIVIDER_POWER_OF_TWO, + RCC_HSICFGR, 0, 2, CLK_DIVIDER_READ_ONLY), /* External / Internal Oscillators */ GATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0), - GATE_MP1(CK_CSI, "ck_csi", "clk-csi", 0, RCC_OCENSETR, 4, 0), + /* ck_csi is used by IO compensation and should be critical */ + GATE_MP1(CK_CSI, "ck_csi", "clk-csi", CLK_IS_CRITICAL, + RCC_OCENSETR, 4, 0), GATE_MP1(CK_HSI, "ck_hsi", "clk-hsi-div", 0, RCC_OCENSETR, 0, 0), GATE(CK_LSI, "ck_lsi", "clk-lsi", 0, RCC_RDLSICR, 0, 0), GATE(CK_LSE, "ck_lse", "clk-lse", 0, RCC_BDCR, 0, 0), @@ -1952,14 +1955,14 @@ static const struct clock_config stm32mp1_clock_cfg[] = { MGATE_MP1(GPU_K, "gpu_k", "pll2_q", 0, G_GPU), MGATE_MP1(DAC12_K, "dac12_k", "ck_lsi", 0, G_DAC12), - COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE, + COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE | + CLK_SET_RATE_NO_REPARENT, _NO_GATE, _MMUX(M_ETHCK), - _DIV(RCC_ETHCKSELR, 4, 4, CLK_DIVIDER_ALLOW_ZERO, NULL)), + _DIV(RCC_ETHCKSELR, 4, 4, 0, NULL)), /* RTC clock */ - DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7, - CLK_DIVIDER_ALLOW_ZERO), + DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 6, 0), COMPOSITE(RTC, "ck_rtc", rtc_src, CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_PARENT, diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index ea846f77750b..0cad5748bf0e 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -41,6 +41,43 @@ static int twl6040_pdmclk_is_prepared(struct clk_hw *hw) return pdmclk->enabled; } +static int twl6040_pdmclk_reset_one_clock(struct twl6040_pdmclk *pdmclk, + unsigned int reg) +{ + const u8 reset_mask = TWL6040_HPLLRST; /* Same for HPPLL and LPPLL */ + int ret; + + ret = twl6040_set_bits(pdmclk->twl6040, reg, reset_mask); + if (ret < 0) + return ret; + + ret = twl6040_clear_bits(pdmclk->twl6040, reg, reset_mask); + if (ret < 0) + return ret; + + return 0; +} + +/* + * TWL6040A2 Phoenix Audio IC erratum #6: "PDM Clock Generation Issue At + * Cold Temperature". This affects cold boot and deeper idle states it + * seems. The workaround consists of resetting HPPLL and LPPLL. + */ +static int twl6040_pdmclk_quirk_reset_clocks(struct twl6040_pdmclk *pdmclk) +{ + int ret; + + ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_HPPLLCTL); + if (ret) + return ret; + + ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_LPPLLCTL); + if (ret) + return ret; + + return 0; +} + static int twl6040_pdmclk_prepare(struct clk_hw *hw) { struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk, @@ -48,8 +85,20 @@ static int twl6040_pdmclk_prepare(struct clk_hw *hw) int ret; ret = twl6040_power(pdmclk->twl6040, 1); - if (!ret) - pdmclk->enabled = 1; + if (ret) + return ret; + + ret = twl6040_pdmclk_quirk_reset_clocks(pdmclk); + if (ret) + goto out_err; + + pdmclk->enabled = 1; + + return 0; + +out_err: + dev_err(pdmclk->dev, "%s: error %i\n", __func__, ret); + twl6040_power(pdmclk->twl6040, 0); return ret; } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index d2477a5058ac..96053a96fe2f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -57,6 +57,7 @@ struct clk_core { struct clk_core *new_child; unsigned long flags; bool orphan; + bool rpm_enabled; unsigned int enable_count; unsigned int prepare_count; unsigned int protect_count; @@ -81,6 +82,7 @@ struct clk_core { struct clk { struct clk_core *core; + struct device *dev; const char *dev_id; const char *con_id; unsigned long min_rate; @@ -92,9 +94,9 @@ struct clk { /*** runtime pm ***/ static int clk_pm_runtime_get(struct clk_core *core) { - int ret = 0; + int ret; - if (!core->dev) + if (!core->rpm_enabled) return 0; ret = pm_runtime_get_sync(core->dev); @@ -103,7 +105,7 @@ static int clk_pm_runtime_get(struct clk_core *core) static void clk_pm_runtime_put(struct clk_core *core) { - if (!core->dev) + if (!core->rpm_enabled) return; pm_runtime_put_sync(core->dev); @@ -223,7 +225,7 @@ static bool clk_core_is_enabled(struct clk_core *core) * taking enable spinlock, but the below check is needed if one tries * to call it from other places. */ - if (core->dev) { + if (core->rpm_enabled) { pm_runtime_get_noresume(core->dev); if (!pm_runtime_active(core->dev)) { ret = false; @@ -233,7 +235,7 @@ static bool clk_core_is_enabled(struct clk_core *core) ret = core->ops->is_enabled(core->hw); done: - if (core->dev) + if (core->rpm_enabled) pm_runtime_put(core->dev); return ret; @@ -394,16 +396,19 @@ bool clk_hw_is_prepared(const struct clk_hw *hw) { return clk_core_is_prepared(hw->core); } +EXPORT_SYMBOL_GPL(clk_hw_is_prepared); bool clk_hw_rate_is_protected(const struct clk_hw *hw) { return clk_core_rate_is_protected(hw->core); } +EXPORT_SYMBOL_GPL(clk_hw_rate_is_protected); bool clk_hw_is_enabled(const struct clk_hw *hw) { return clk_core_is_enabled(hw->core); } +EXPORT_SYMBOL_GPL(clk_hw_is_enabled); bool __clk_is_enabled(struct clk *clk) { @@ -3209,43 +3214,106 @@ unlock: return ret; } -struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id, +/** + * clk_core_link_consumer - Add a clk consumer to the list of consumers in a clk_core + * @core: clk to add consumer to + * @clk: consumer to link to a clk + */ +static void clk_core_link_consumer(struct clk_core *core, struct clk *clk) +{ + clk_prepare_lock(); + hlist_add_head(&clk->clks_node, &core->clks); + clk_prepare_unlock(); +} + +/** + * clk_core_unlink_consumer - Remove a clk consumer from the list of consumers in a clk_core + * @clk: consumer to unlink + */ +static void clk_core_unlink_consumer(struct clk *clk) +{ + lockdep_assert_held(&prepare_lock); + hlist_del(&clk->clks_node); +} + +/** + * alloc_clk - Allocate a clk consumer, but leave it unlinked to the clk_core + * @core: clk to allocate a consumer for + * @dev_id: string describing device name + * @con_id: connection ID string on device + * + * Returns: clk consumer left unlinked from the consumer list + */ +static struct clk *alloc_clk(struct clk_core *core, const char *dev_id, const char *con_id) { struct clk *clk; - /* This is to allow this function to be chained to others */ - if (IS_ERR_OR_NULL(hw)) - return ERR_CAST(hw); - clk = kzalloc(sizeof(*clk), GFP_KERNEL); if (!clk) return ERR_PTR(-ENOMEM); - clk->core = hw->core; + clk->core = core; clk->dev_id = dev_id; clk->con_id = kstrdup_const(con_id, GFP_KERNEL); clk->max_rate = ULONG_MAX; - clk_prepare_lock(); - hlist_add_head(&clk->clks_node, &hw->core->clks); - clk_prepare_unlock(); - return clk; } -/* keep in sync with __clk_put */ -void __clk_free_clk(struct clk *clk) +/** + * free_clk - Free a clk consumer + * @clk: clk consumer to free + * + * Note, this assumes the clk has been unlinked from the clk_core consumer + * list. + */ +static void free_clk(struct clk *clk) { - clk_prepare_lock(); - hlist_del(&clk->clks_node); - clk_prepare_unlock(); - kfree_const(clk->con_id); kfree(clk); } /** + * clk_hw_create_clk: Allocate and link a clk consumer to a clk_core given + * a clk_hw + * @dev: clk consumer device + * @hw: clk_hw associated with the clk being consumed + * @dev_id: string describing device name + * @con_id: connection ID string on device + * + * This is the main function used to create a clk pointer for use by clk + * consumers. It connects a consumer to the clk_core and clk_hw structures + * used by the framework and clk provider respectively. + */ +struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw, + const char *dev_id, const char *con_id) +{ + struct clk *clk; + struct clk_core *core; + + /* This is to allow this function to be chained to others */ + if (IS_ERR_OR_NULL(hw)) + return ERR_CAST(hw); + + core = hw->core; + clk = alloc_clk(core, dev_id, con_id); + if (IS_ERR(clk)) + return clk; + clk->dev = dev; + + if (!try_module_get(core->owner)) { + free_clk(clk); + return ERR_PTR(-ENOENT); + } + + kref_get(&core->ref); + clk_core_link_consumer(core, clk); + + return clk; +} + +/** * clk_register - allocate a new clock, register it and return an opaque cookie * @dev: device that is registering this clock * @hw: link to hardware-specific clock data @@ -3280,7 +3348,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) core->ops = hw->init->ops; if (dev && pm_runtime_enabled(dev)) - core->dev = dev; + core->rpm_enabled = true; + core->dev = dev; if (dev && dev->driver) core->owner = dev->driver->owner; core->hw = hw; @@ -3320,17 +3389,27 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) INIT_HLIST_HEAD(&core->clks); - hw->clk = __clk_create_clk(hw, NULL, NULL); + /* + * Don't call clk_hw_create_clk() here because that would pin the + * provider module to itself and prevent it from ever being removed. + */ + hw->clk = alloc_clk(core, NULL, NULL); if (IS_ERR(hw->clk)) { ret = PTR_ERR(hw->clk); goto fail_parents; } + clk_core_link_consumer(hw->core, hw->clk); + ret = __clk_core_init(core); if (!ret) return hw->clk; - __clk_free_clk(hw->clk); + clk_prepare_lock(); + clk_core_unlink_consumer(hw->clk); + clk_prepare_unlock(); + + free_clk(hw->clk); hw->clk = NULL; fail_parents: @@ -3601,20 +3680,7 @@ EXPORT_SYMBOL_GPL(devm_clk_hw_unregister); /* * clkdev helpers */ -int __clk_get(struct clk *clk) -{ - struct clk_core *core = !clk ? NULL : clk->core; - - if (core) { - if (!try_module_get(core->owner)) - return 0; - kref_get(&core->ref); - } - return 1; -} - -/* keep in sync with __clk_free_clk */ void __clk_put(struct clk *clk) { struct module *owner; @@ -3648,8 +3714,7 @@ void __clk_put(struct clk *clk) module_put(owner); - kfree_const(clk->con_id); - kfree(clk); + free_clk(clk); } /*** clk rate change notifiers ***/ @@ -4006,6 +4071,49 @@ void devm_of_clk_del_provider(struct device *dev) } EXPORT_SYMBOL(devm_of_clk_del_provider); +/* + * Beware the return values when np is valid, but no clock provider is found. + * If name == NULL, the function returns -ENOENT. + * If name != NULL, the function returns -EINVAL. This is because + * of_parse_phandle_with_args() is called even if of_property_match_string() + * returns an error. + */ +static int of_parse_clkspec(const struct device_node *np, int index, + const char *name, struct of_phandle_args *out_args) +{ + int ret = -ENOENT; + + /* Walk up the tree of devices looking for a clock property that matches */ + while (np) { + /* + * For named clocks, first look up the name in the + * "clock-names" property. If it cannot be found, then index + * will be an error code and of_parse_phandle_with_args() will + * return -EINVAL. + */ + if (name) + index = of_property_match_string(np, "clock-names", name); + ret = of_parse_phandle_with_args(np, "clocks", "#clock-cells", + index, out_args); + if (!ret) + break; + if (name && index >= 0) + break; + + /* + * No matching clock found on this node. If the parent node + * has a "clock-ranges" property, then we can try one of its + * clocks. + */ + np = np->parent; + if (np && !of_get_property(np, "clock-ranges", NULL)) + break; + index = 0; + } + + return ret; +} + static struct clk_hw * __of_clk_get_hw_from_provider(struct of_clk_provider *provider, struct of_phandle_args *clkspec) @@ -4021,36 +4129,26 @@ __of_clk_get_hw_from_provider(struct of_clk_provider *provider, return __clk_get_hw(clk); } -struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, - const char *dev_id, const char *con_id) +static struct clk_hw * +of_clk_get_hw_from_clkspec(struct of_phandle_args *clkspec) { struct of_clk_provider *provider; - struct clk *clk = ERR_PTR(-EPROBE_DEFER); - struct clk_hw *hw; + struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER); if (!clkspec) return ERR_PTR(-EINVAL); - /* Check if we have such a provider in our array */ mutex_lock(&of_clk_mutex); list_for_each_entry(provider, &of_clk_providers, link) { if (provider->node == clkspec->np) { hw = __of_clk_get_hw_from_provider(provider, clkspec); - clk = __clk_create_clk(hw, dev_id, con_id); - } - - if (!IS_ERR(clk)) { - if (!__clk_get(clk)) { - __clk_free_clk(clk); - clk = ERR_PTR(-ENOENT); - } - - break; + if (!IS_ERR(hw)) + break; } } mutex_unlock(&of_clk_mutex); - return clk; + return hw; } /** @@ -4063,10 +4161,62 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, */ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) { - return __of_clk_get_from_provider(clkspec, NULL, __func__); + struct clk_hw *hw = of_clk_get_hw_from_clkspec(clkspec); + + return clk_hw_create_clk(NULL, hw, NULL, __func__); } EXPORT_SYMBOL_GPL(of_clk_get_from_provider); +struct clk_hw *of_clk_get_hw(struct device_node *np, int index, + const char *con_id) +{ + int ret; + struct clk_hw *hw; + struct of_phandle_args clkspec; + + ret = of_parse_clkspec(np, index, con_id, &clkspec); + if (ret) + return ERR_PTR(ret); + + hw = of_clk_get_hw_from_clkspec(&clkspec); + of_node_put(clkspec.np); + + return hw; +} + +static struct clk *__of_clk_get(struct device_node *np, + int index, const char *dev_id, + const char *con_id) +{ + struct clk_hw *hw = of_clk_get_hw(np, index, con_id); + + return clk_hw_create_clk(NULL, hw, dev_id, con_id); +} + +struct clk *of_clk_get(struct device_node *np, int index) +{ + return __of_clk_get(np, index, np->full_name, NULL); +} +EXPORT_SYMBOL(of_clk_get); + +/** + * of_clk_get_by_name() - Parse and lookup a clock referenced by a device node + * @np: pointer to clock consumer node + * @name: name of consumer's clock input, or NULL for the first clock reference + * + * This function parses the clocks and clock-names properties, + * and uses them to look up the struct clk from the registered list of clock + * providers. + */ +struct clk *of_clk_get_by_name(struct device_node *np, const char *name) +{ + if (!np) + return ERR_PTR(-ENOENT); + + return __of_clk_get(np, 0, np->full_name, name); +} +EXPORT_SYMBOL(of_clk_get_by_name); + /** * of_clk_get_parent_count() - Count the number of clocks a device node has * @np: device node to count diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index b02f5e604e69..553f531cc232 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -5,31 +5,36 @@ */ struct clk_hw; +struct device; +struct of_phandle_args; #if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) -struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, - const char *dev_id, const char *con_id); +struct clk_hw *of_clk_get_hw(struct device_node *np, + int index, const char *con_id); +#else /* !CONFIG_COMMON_CLK || !CONFIG_OF */ +static inline struct clk_hw *of_clk_get_hw(struct device_node *np, + int index, const char *con_id) +{ + return ERR_PTR(-ENOENT); +} #endif #ifdef CONFIG_COMMON_CLK -struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id, - const char *con_id); -void __clk_free_clk(struct clk *clk); -int __clk_get(struct clk *clk); +struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw, + const char *dev_id, const char *con_id); void __clk_put(struct clk *clk); #else /* All these casts to avoid ifdefs in clkdev... */ static inline struct clk * -__clk_create_clk(struct clk_hw *hw, const char *dev_id, const char *con_id) +clk_hw_create_clk(struct device *dev, struct clk_hw *hw, const char *dev_id, + const char *con_id) { return (struct clk *)hw; } -static inline void __clk_free_clk(struct clk *clk) { } static struct clk_hw *__clk_get_hw(struct clk *clk) { return (struct clk_hw *)clk; } -static inline int __clk_get(struct clk *clk) { return 1; } static inline void __clk_put(struct clk *clk) { } #endif diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 9ab3db8b3988..8c4435c53f09 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -27,99 +27,6 @@ static LIST_HEAD(clocks); static DEFINE_MUTEX(clocks_mutex); -#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) -static struct clk *__of_clk_get(struct device_node *np, int index, - const char *dev_id, const char *con_id) -{ - struct of_phandle_args clkspec; - struct clk *clk; - int rc; - - rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, - &clkspec); - if (rc) - return ERR_PTR(rc); - - clk = __of_clk_get_from_provider(&clkspec, dev_id, con_id); - of_node_put(clkspec.np); - - return clk; -} - -struct clk *of_clk_get(struct device_node *np, int index) -{ - return __of_clk_get(np, index, np->full_name, NULL); -} -EXPORT_SYMBOL(of_clk_get); - -static struct clk *__of_clk_get_by_name(struct device_node *np, - const char *dev_id, - const char *name) -{ - struct clk *clk = ERR_PTR(-ENOENT); - - /* Walk up the tree of devices looking for a clock that matches */ - while (np) { - int index = 0; - - /* - * For named clocks, first look up the name in the - * "clock-names" property. If it cannot be found, then - * index will be an error code, and of_clk_get() will fail. - */ - if (name) - index = of_property_match_string(np, "clock-names", name); - clk = __of_clk_get(np, index, dev_id, name); - if (!IS_ERR(clk)) { - break; - } else if (name && index >= 0) { - if (PTR_ERR(clk) != -EPROBE_DEFER) - pr_err("ERROR: could not get clock %pOF:%s(%i)\n", - np, name ? name : "", index); - return clk; - } - - /* - * No matching clock found on this node. If the parent node - * has a "clock-ranges" property, then we can try one of its - * clocks. - */ - np = np->parent; - if (np && !of_get_property(np, "clock-ranges", NULL)) - break; - } - - return clk; -} - -/** - * of_clk_get_by_name() - Parse and lookup a clock referenced by a device node - * @np: pointer to clock consumer node - * @name: name of consumer's clock input, or NULL for the first clock reference - * - * This function parses the clocks and clock-names properties, - * and uses them to look up the struct clk from the registered list of clock - * providers. - */ -struct clk *of_clk_get_by_name(struct device_node *np, const char *name) -{ - if (!np) - return ERR_PTR(-ENOENT); - - return __of_clk_get_by_name(np, np->full_name, name); -} -EXPORT_SYMBOL(of_clk_get_by_name); - -#else /* defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) */ - -static struct clk *__of_clk_get_by_name(struct device_node *np, - const char *dev_id, - const char *name) -{ - return ERR_PTR(-ENOENT); -} -#endif - /* * Find the correct struct clk for the device and connection ID. * We do slightly fuzzy matching here: @@ -163,7 +70,8 @@ static struct clk_lookup *clk_find(const char *dev_id, const char *con_id) return cl; } -struct clk *clk_get_sys(const char *dev_id, const char *con_id) +static struct clk *__clk_get_sys(struct device *dev, const char *dev_id, + const char *con_id) { struct clk_lookup *cl; struct clk *clk = NULL; @@ -174,35 +82,33 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id) if (!cl) goto out; - clk = __clk_create_clk(cl->clk_hw, dev_id, con_id); + clk = clk_hw_create_clk(dev, cl->clk_hw, dev_id, con_id); if (IS_ERR(clk)) - goto out; - - if (!__clk_get(clk)) { - __clk_free_clk(clk); cl = NULL; - goto out; - } - out: mutex_unlock(&clocks_mutex); return cl ? clk : ERR_PTR(-ENOENT); } + +struct clk *clk_get_sys(const char *dev_id, const char *con_id) +{ + return __clk_get_sys(NULL, dev_id, con_id); +} EXPORT_SYMBOL(clk_get_sys); struct clk *clk_get(struct device *dev, const char *con_id) { const char *dev_id = dev ? dev_name(dev) : NULL; - struct clk *clk; + struct clk_hw *hw; if (dev && dev->of_node) { - clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id); - if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER) - return clk; + hw = of_clk_get_hw(dev->of_node, 0, con_id); + if (!IS_ERR(hw) || PTR_ERR(hw) == -EPROBE_DEFER) + return clk_hw_create_clk(dev, hw, dev_id, con_id); } - return clk_get_sys(dev_id, con_id); + return __clk_get_sys(dev, dev_id, con_id); } EXPORT_SYMBOL(clk_get); @@ -401,6 +307,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw, return cl; } +static int do_clk_register_clkdev(struct clk_hw *hw, + struct clk_lookup **cl, const char *con_id, const char *dev_id) +{ + if (IS_ERR(hw)) + return PTR_ERR(hw); + /* + * Since dev_id can be NULL, and NULL is handled specially, we must + * pass it as either a NULL format string, or with "%s". + */ + if (dev_id) + *cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); + else + *cl = __clk_register_clkdev(hw, con_id, NULL); + + return *cl ? 0 : -ENOMEM; +} + /** * clk_register_clkdev - register one clock lookup for a struct clk * @clk: struct clk to associate with all clk_lookups @@ -423,17 +346,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id, if (IS_ERR(clk)) return PTR_ERR(clk); - /* - * Since dev_id can be NULL, and NULL is handled specially, we must - * pass it as either a NULL format string, or with "%s". - */ - if (dev_id) - cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s", - dev_id); - else - cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL); - - return cl ? 0 : -ENOMEM; + return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id, + dev_id); } EXPORT_SYMBOL(clk_register_clkdev); @@ -456,18 +370,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, { struct clk_lookup *cl; - if (IS_ERR(hw)) - return PTR_ERR(hw); + return do_clk_register_clkdev(hw, &cl, con_id, dev_id); +} +EXPORT_SYMBOL(clk_hw_register_clkdev); - /* - * Since dev_id can be NULL, and NULL is handled specially, we must - * pass it as either a NULL format string, or with "%s". - */ - if (dev_id) - cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); - else - cl = __clk_register_clkdev(hw, con_id, NULL); +static void devm_clkdev_release(struct device *dev, void *res) +{ + clkdev_drop(*(struct clk_lookup **)res); +} + +static int devm_clk_match_clkdev(struct device *dev, void *res, void *data) +{ + struct clk_lookup **l = res; - return cl ? 0 : -ENOMEM; + return *l == data; } -EXPORT_SYMBOL(clk_hw_register_clkdev); + +/** + * devm_clk_release_clkdev - Resource managed clkdev lookup release + * @dev: device this lookup is bound + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * Drop the clkdev lookup created with devm_clk_hw_register_clkdev. + * Normally this function will not need to be called and the resource + * management code will ensure that the resource is freed. + */ +void devm_clk_release_clkdev(struct device *dev, const char *con_id, + const char *dev_id) +{ + struct clk_lookup *cl; + int rval; + + cl = clk_find(dev_id, con_id); + WARN_ON(!cl); + rval = devres_release(dev, devm_clkdev_release, + devm_clk_match_clkdev, cl); + WARN_ON(rval); +} +EXPORT_SYMBOL(devm_clk_release_clkdev); + +/** + * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw + * @dev: device this lookup is bound + * @hw: struct clk_hw to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * con_id or dev_id may be NULL as a wildcard, just as in the rest of + * clkdev. + * + * To make things easier for mass registration, we detect error clk_hws + * from a previous clk_hw_register_*() call, and return the error code for + * those. This is to permit this function to be called immediately + * after clk_hw_register_*(). + */ +int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, + const char *con_id, const char *dev_id) +{ + int rval = -ENOMEM; + struct clk_lookup **cl; + + cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL); + if (cl) { + rval = do_clk_register_clkdev(hw, cl, con_id, dev_id); + if (!rval) + devres_add(dev, cl); + else + devres_free(cl); + } + return rval; +} +EXPORT_SYMBOL(devm_clk_hw_register_clkdev); diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index 4aae31a23449..0eaf41848280 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -8,6 +8,12 @@ config MXC_CLK_SCU bool depends on IMX_SCU +config CLK_IMX8MM + bool "IMX8MM CCM Clock Driver" + depends on ARCH_MXC && ARM64 + help + Build the driver for i.MX8MM CCM Clock Driver + config CLK_IMX8MQ bool "IMX8MQ CCM Clock Driver" depends on ARCH_MXC && ARM64 diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index 73119fbfa547..0d5180fbe988 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -18,12 +18,14 @@ obj-$(CONFIG_MXC_CLK) += \ clk-pllv2.o \ clk-pllv3.o \ clk-pllv4.o \ - clk-sccg-pll.o + clk-sccg-pll.o \ + clk-pll14xx.o obj-$(CONFIG_MXC_CLK_SCU) += \ clk-scu.o \ clk-lpcg-scu.o +obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 527ade1d6933..574fac1a169f 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -123,7 +123,7 @@ static const struct clk_ops imx8m_clk_composite_divider_ops = { }; struct clk *imx8m_clk_composite_flags(const char *name, - const char **parent_names, + const char * const *parent_names, int num_parents, void __iomem *reg, unsigned long flags) { diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c index fc8e782d817b..e91c826bce70 100644 --- a/drivers/clk/imx/clk-imx51-imx53.c +++ b/drivers/clk/imx/clk-imx51-imx53.c @@ -428,6 +428,7 @@ static void __init mx51_clocks_init(struct device_node *np) clk[IMX5_CLK_ESDHC4_PER_GATE] = imx_clk_gate2("esdhc4_per_gate", "esdhc_d_sel", MXC_CCM_CCGR3, 14); clk[IMX5_CLK_USB_PHY_GATE] = imx_clk_gate2("usb_phy_gate", "usb_phy_sel", MXC_CCM_CCGR2, 0); clk[IMX5_CLK_HSI2C_GATE] = imx_clk_gate2("hsi2c_gate", "ipg", MXC_CCM_CCGR1, 22); + clk[IMX5_CLK_SCC2_IPG_GATE] = imx_clk_gate2("scc2_gate", "ipg", MXC_CCM_CCGR1, 30); clk[IMX5_CLK_MIPI_HSC1_GATE] = imx_clk_gate2_flags("mipi_hsc1_gate", "ipg", MXC_CCM_CCGR4, 6, CLK_IS_CRITICAL); clk[IMX5_CLK_MIPI_HSC2_GATE] = imx_clk_gate2_flags("mipi_hsc2_gate", "ipg", MXC_CCM_CCGR4, 8, CLK_IS_CRITICAL); clk[IMX5_CLK_MIPI_ESC_GATE] = imx_clk_gate2_flags("mipi_esc_gate", "ipg", MXC_CCM_CCGR4, 10, CLK_IS_CRITICAL); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 716eac3136b4..708e7c5590dd 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -471,6 +471,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); anatop_base = base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) { diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index 18527a335ace..91558b09bf9e 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -151,6 +151,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop"); base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); clks[IMX6SX_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); clks[IMX6SX_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 06c105d580a4..cfbd8d4edb85 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -404,6 +404,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop"); base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); clks[IMX7D_PLL_ARM_MAIN_SRC] = imx_clk_mux("pll_arm_main_src", base + 0x60, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); clks[IMX7D_PLL_DRAM_MAIN_SRC] = imx_clk_mux("pll_dram_main_src", base + 0x70, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c index 4e18f629f823..ce306631e844 100644 --- a/drivers/clk/imx/clk-imx7ulp.c +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -48,8 +48,8 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_SCG1_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SCG1_END), + GFP_KERNEL); if (!clk_data) return; @@ -136,8 +136,8 @@ static void __init imx7ulp_clk_pcc2_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_PCC2_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC2_END), + GFP_KERNEL); if (!clk_data) return; @@ -183,8 +183,8 @@ static void __init imx7ulp_clk_pcc3_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_PCC3_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC3_END), + GFP_KERNEL); if (!clk_data) return; @@ -228,8 +228,8 @@ static void __init imx7ulp_clk_smc1_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_SMC1_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SMC1_END), + GFP_KERNEL); if (!clk_data) return; diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c new file mode 100644 index 000000000000..1ef8438e3d6d --- /dev/null +++ b/drivers/clk/imx/clk-imx8mm.c @@ -0,0 +1,675 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP. + */ + +#include <dt-bindings/clock/imx8mm-clock.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include "clk.h" + +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_sai4; +static u32 share_count_sai5; +static u32 share_count_sai6; +static u32 share_count_dcss; +static u32 share_count_pdm; +static u32 share_count_nand; + +#define PLL_1416X_RATE(_rate, _m, _p, _s) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + } + +#define PLL_1443X_RATE(_rate, _m, _p, _s, _k) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + } + +static const struct imx_pll14xx_rate_table imx8mm_pll1416x_tbl[] = { + PLL_1416X_RATE(1800000000U, 225, 3, 0), + PLL_1416X_RATE(1600000000U, 200, 3, 0), + PLL_1416X_RATE(1200000000U, 300, 3, 1), + PLL_1416X_RATE(1000000000U, 250, 3, 1), + PLL_1416X_RATE(800000000U, 200, 3, 1), + PLL_1416X_RATE(750000000U, 250, 2, 2), + PLL_1416X_RATE(700000000U, 350, 3, 2), + PLL_1416X_RATE(600000000U, 300, 3, 2), +}; + +static const struct imx_pll14xx_rate_table imx8mm_audiopll_tbl[] = { + PLL_1443X_RATE(786432000U, 655, 5, 2, 23593), + PLL_1443X_RATE(722534400U, 301, 5, 1, 3670), +}; + +static const struct imx_pll14xx_rate_table imx8mm_videopll_tbl[] = { + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), + PLL_1443X_RATE(594000000U, 198, 2, 2, 0), +}; + +static const struct imx_pll14xx_rate_table imx8mm_drampll_tbl[] = { + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), +}; + +static struct imx_pll14xx_clk imx8mm_audio_pll __initdata = { + .type = PLL_1443X, + .rate_table = imx8mm_audiopll_tbl, + .rate_count = ARRAY_SIZE(imx8mm_audiopll_tbl), +}; + +static struct imx_pll14xx_clk imx8mm_video_pll __initdata = { + .type = PLL_1443X, + .rate_table = imx8mm_videopll_tbl, + .rate_count = ARRAY_SIZE(imx8mm_videopll_tbl), +}; + +static struct imx_pll14xx_clk imx8mm_dram_pll __initdata = { + .type = PLL_1443X, + .rate_table = imx8mm_drampll_tbl, + .rate_count = ARRAY_SIZE(imx8mm_drampll_tbl), +}; + +static struct imx_pll14xx_clk imx8mm_arm_pll __initdata = { + .type = PLL_1416X, + .rate_table = imx8mm_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), +}; + +static struct imx_pll14xx_clk imx8mm_gpu_pll __initdata = { + .type = PLL_1416X, + .rate_table = imx8mm_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), +}; + +static struct imx_pll14xx_clk imx8mm_vpu_pll __initdata = { + .type = PLL_1416X, + .rate_table = imx8mm_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), +}; + +static struct imx_pll14xx_clk imx8mm_sys_pll __initdata = { + .type = PLL_1416X, + .rate_table = imx8mm_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), +}; + +static const char *pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char *video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; +static const char *dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; +static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char *sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; +static const char *sys_pll2_bypass_sels[] = {"sys_pll2", "sys_pll2_ref_sel", }; +static const char *sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; + +/* CCM ROOT */ +static const char *imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", + "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_m4_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "sys_pll1_266m", + "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_vpu_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", + "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "vpu_pll_out", }; + +static const char *imx8mm_gpu3d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_gpu2d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", "sys_pll2_250m", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "sys_pll1_100m",}; + +static const char *imx8mm_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", + "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", + "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; + +static const char *imx8mm_vpu_bus_sels[] = {"osc_24m", "sys_pll1_800m", "vpu_pll_out", "audio_pll2_out", + "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_200m", "sys_pll1_100m", }; + +static const char *imx8mm_disp_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext4", }; + +static const char *imx8mm_disp_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", }; + +static const char *imx8mm_disp_rtrm_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_noc_apb_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll3_out", "sys_pll2_333m", "sys_pll2_200m", + "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", + "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll2_166m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", "sys_pll2_500m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll1_out", "sys_pll1_266m", }; + +static const char *imx8mm_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_vpu_g1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_vpu_g2_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_disp_dtrc_sels[] = {"osc_24m", "video_pll2_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", }; + +static const char *imx8mm_disp_dc8000_sels[] = {"osc_24m", "video_pll2_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", }; + +static const char *imx8mm_pcie1_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; + +static const char *imx8mm_pcie1_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "sys_pll1_400m", }; + +static const char *imx8mm_pcie1_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", + "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; + +static const char *imx8mm_dc_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char *imx8mm_lcdif_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char *imx8mm_sai1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", }; + +static const char *imx8mm_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_sai4_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", }; + +static const char *imx8mm_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_spdif2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", + "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; + +static const char *imx8mm_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "video_pll1_out", }; + +static const char *imx8mm_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", + "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", "sys_pll1_400m", + "audio_pll2_out", "sys_pll3_out", "sys_pll2_250m", "video_pll1_out", }; + +static const char *imx8mm_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; + +static const char *imx8mm_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys3_pll2_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_gpt1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", "sys_pll1_40m", + "video_pll1_out", "sys_pll1_800m", "audio_pll1_out", "clk_ext1" }; + +static const char *imx8mm_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", + "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; + +static const char *imx8mm_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "vpu_pll_out", "sys_pll3_out", "sys_pll2_200m", + "sys_pll1_266m", "sys_pll2_500m", "sys_pll1_100m", }; + +static const char *imx8mm_dsi_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_dsi_phy_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_dsi_dbi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_clk", "sys_pll1_100m", }; + +static const char *imx8mm_csi1_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi1_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi1_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_csi2_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi2_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_pcie2_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; + +static const char *imx8mm_pcie2_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", + "clk_ext2", "clk_ext3", "clk_ext4", "sys_pll1_400m", }; + +static const char *imx8mm_pcie2_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", + "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; + +static const char *imx8mm_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_pdm_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_vpu_h1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "audio_pll2_clk", "sys_pll2_125m", "sys_pll3_clk", "audio_pll1_out", }; + +static const char *imx8mm_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; + +static const char *imx8mm_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "osc_27m", "sys_pll1_200m", "audio_pll2_clk", + "vpu_pll", "sys_pll1_80m", }; + +static struct clk *clks[IMX8MM_CLK_END]; +static struct clk_onecell_data clk_data; + +static struct clk ** const uart_clks[] __initconst = { + &clks[IMX8MM_CLK_UART1_ROOT], + &clks[IMX8MM_CLK_UART2_ROOT], + &clks[IMX8MM_CLK_UART3_ROOT], + &clks[IMX8MM_CLK_UART4_ROOT], + NULL +}; + +static int __init imx8mm_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + int ret; + + clks[IMX8MM_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clks[IMX8MM_CLK_24M] = of_clk_get_by_name(ccm_node, "osc_24m"); + clks[IMX8MM_CLK_32K] = of_clk_get_by_name(ccm_node, "osc_32k"); + clks[IMX8MM_CLK_EXT1] = of_clk_get_by_name(ccm_node, "clk_ext1"); + clks[IMX8MM_CLK_EXT2] = of_clk_get_by_name(ccm_node, "clk_ext2"); + clks[IMX8MM_CLK_EXT3] = of_clk_get_by_name(ccm_node, "clk_ext3"); + clks[IMX8MM_CLK_EXT4] = of_clk_get_by_name(ccm_node, "clk_ext4"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop"); + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return -ENOMEM; + + clks[IMX8MM_AUDIO_PLL1_REF_SEL] = imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_AUDIO_PLL2_REF_SEL] = imx_clk_mux("audio_pll2_ref_sel", base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_VIDEO_PLL1_REF_SEL] = imx_clk_mux("video_pll1_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_DRAM_PLL_REF_SEL] = imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_GPU_PLL_REF_SEL] = imx_clk_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_VPU_PLL_REF_SEL] = imx_clk_mux("vpu_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_ARM_PLL_REF_SEL] = imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_SYS_PLL1_REF_SEL] = imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_SYS_PLL2_REF_SEL] = imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + + clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx8mm_audio_pll); + clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx8mm_audio_pll); + clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx8mm_video_pll); + clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx8mm_dram_pll); + clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx8mm_gpu_pll); + clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx8mm_vpu_pll); + clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx8mm_arm_pll); + clks[IMX8MM_SYS_PLL1] = imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel", base + 0x94, &imx8mm_sys_pll); + clks[IMX8MM_SYS_PLL2] = imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel", base + 0x104, &imx8mm_sys_pll); + clks[IMX8MM_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx8mm_sys_pll); + + /* PLL bypass out */ + clks[IMX8MM_AUDIO_PLL1_BYPASS] = imx_clk_mux_flags("audio_pll1_bypass", base, 4, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_AUDIO_PLL2_BYPASS] = imx_clk_mux_flags("audio_pll2_bypass", base + 0x14, 4, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_VIDEO_PLL1_BYPASS] = imx_clk_mux_flags("video_pll1_bypass", base + 0x28, 4, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_DRAM_PLL_BYPASS] = imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_GPU_PLL_BYPASS] = imx_clk_mux_flags("gpu_pll_bypass", base + 0x64, 4, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_VPU_PLL_BYPASS] = imx_clk_mux_flags("vpu_pll_bypass", base + 0x74, 4, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_SYS_PLL1_BYPASS] = imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_SYS_PLL2_BYPASS] = imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_SYS_PLL3_BYPASS] = imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); + + /* unbypass all the plls */ + clk_set_parent(clks[IMX8MM_AUDIO_PLL1_BYPASS], clks[IMX8MM_AUDIO_PLL1]); + clk_set_parent(clks[IMX8MM_AUDIO_PLL2_BYPASS], clks[IMX8MM_AUDIO_PLL2]); + clk_set_parent(clks[IMX8MM_VIDEO_PLL1_BYPASS], clks[IMX8MM_VIDEO_PLL1]); + clk_set_parent(clks[IMX8MM_DRAM_PLL_BYPASS], clks[IMX8MM_DRAM_PLL]); + clk_set_parent(clks[IMX8MM_GPU_PLL_BYPASS], clks[IMX8MM_GPU_PLL]); + clk_set_parent(clks[IMX8MM_VPU_PLL_BYPASS], clks[IMX8MM_VPU_PLL]); + clk_set_parent(clks[IMX8MM_ARM_PLL_BYPASS], clks[IMX8MM_ARM_PLL]); + clk_set_parent(clks[IMX8MM_SYS_PLL1_BYPASS], clks[IMX8MM_SYS_PLL1]); + clk_set_parent(clks[IMX8MM_SYS_PLL2_BYPASS], clks[IMX8MM_SYS_PLL2]); + clk_set_parent(clks[IMX8MM_SYS_PLL3_BYPASS], clks[IMX8MM_SYS_PLL3]); + + /* PLL out gate */ + clks[IMX8MM_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base, 13); + clks[IMX8MM_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x14, 13); + clks[IMX8MM_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x28, 13); + clks[IMX8MM_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13); + clks[IMX8MM_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 13); + clks[IMX8MM_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 13); + clks[IMX8MM_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 13); + clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 13); + clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 13); + clks[IMX8MM_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 13); + + /* SYS PLL fixed output */ + clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20); + clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10); + clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8); + clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6); + clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5); + clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4); + clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3); + clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2); + clks[IMX8MM_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); + + clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20); + clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10); + clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8); + clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6); + clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5); + clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4); + clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3); + clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2); + clks[IMX8MM_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); + + np = ccm_node; + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return -ENOMEM; + + /* Core Slice */ + clks[IMX8MM_CLK_A53_SRC] = imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels)); + clks[IMX8MM_CLK_M4_SRC] = imx_clk_mux2("arm_m4_src", base + 0x8080, 24, 3, imx8mm_m4_sels, ARRAY_SIZE(imx8mm_m4_sels)); + clks[IMX8MM_CLK_VPU_SRC] = imx_clk_mux2("vpu_src", base + 0x8100, 24, 3, imx8mm_vpu_sels, ARRAY_SIZE(imx8mm_vpu_sels)); + clks[IMX8MM_CLK_GPU3D_SRC] = imx_clk_mux2("gpu3d_src", base + 0x8180, 24, 3, imx8mm_gpu3d_sels, ARRAY_SIZE(imx8mm_gpu3d_sels)); + clks[IMX8MM_CLK_GPU2D_SRC] = imx_clk_mux2("gpu2d_src", base + 0x8200, 24, 3, imx8mm_gpu2d_sels, ARRAY_SIZE(imx8mm_gpu2d_sels)); + clks[IMX8MM_CLK_A53_CG] = imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28); + clks[IMX8MM_CLK_M4_CG] = imx_clk_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28); + clks[IMX8MM_CLK_VPU_CG] = imx_clk_gate3("vpu_cg", "vpu_src", base + 0x8100, 28); + clks[IMX8MM_CLK_GPU3D_CG] = imx_clk_gate3("gpu3d_cg", "gpu3d_src", base + 0x8180, 28); + clks[IMX8MM_CLK_GPU2D_CG] = imx_clk_gate3("gpu2d_cg", "gpu2d_src", base + 0x8200, 28); + clks[IMX8MM_CLK_A53_DIV] = imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3); + clks[IMX8MM_CLK_M4_DIV] = imx_clk_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3); + clks[IMX8MM_CLK_VPU_DIV] = imx_clk_divider2("vpu_div", "vpu_cg", base + 0x8100, 0, 3); + clks[IMX8MM_CLK_GPU3D_DIV] = imx_clk_divider2("gpu3d_div", "gpu3d_cg", base + 0x8180, 0, 3); + clks[IMX8MM_CLK_GPU2D_DIV] = imx_clk_divider2("gpu2d_div", "gpu2d_cg", base + 0x8200, 0, 3); + + /* BUS */ + clks[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_composite_critical("main_axi", imx8mm_main_axi_sels, base + 0x8800); + clks[IMX8MM_CLK_ENET_AXI] = imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels, base + 0x8880); + clks[IMX8MM_CLK_NAND_USDHC_BUS] = imx8m_clk_composite_critical("nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900); + clks[IMX8MM_CLK_VPU_BUS] = imx8m_clk_composite("vpu_bus", imx8mm_vpu_bus_sels, base + 0x8980); + clks[IMX8MM_CLK_DISP_AXI] = imx8m_clk_composite("disp_axi", imx8mm_disp_axi_sels, base + 0x8a00); + clks[IMX8MM_CLK_DISP_APB] = imx8m_clk_composite("disp_apb", imx8mm_disp_apb_sels, base + 0x8a80); + clks[IMX8MM_CLK_DISP_RTRM] = imx8m_clk_composite("disp_rtrm", imx8mm_disp_rtrm_sels, base + 0x8b00); + clks[IMX8MM_CLK_USB_BUS] = imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80); + clks[IMX8MM_CLK_GPU_AXI] = imx8m_clk_composite("gpu_axi", imx8mm_gpu_axi_sels, base + 0x8c00); + clks[IMX8MM_CLK_GPU_AHB] = imx8m_clk_composite("gpu_ahb", imx8mm_gpu_ahb_sels, base + 0x8c80); + clks[IMX8MM_CLK_NOC] = imx8m_clk_composite_critical("noc", imx8mm_noc_sels, base + 0x8d00); + clks[IMX8MM_CLK_NOC_APB] = imx8m_clk_composite_critical("noc_apb", imx8mm_noc_apb_sels, base + 0x8d80); + + /* AHB */ + clks[IMX8MM_CLK_AHB] = imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels, base + 0x9000); + clks[IMX8MM_CLK_AUDIO_AHB] = imx8m_clk_composite("audio_ahb", imx8mm_audio_ahb_sels, base + 0x9100); + + /* IPG */ + clks[IMX8MM_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1); + clks[IMX8MM_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1); + + /* IP */ + clks[IMX8MM_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mm_dram_alt_sels, base + 0xa000); + clks[IMX8MM_CLK_DRAM_APB] = imx8m_clk_composite("dram_apb", imx8mm_dram_apb_sels, base + 0xa080); + clks[IMX8MM_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mm_vpu_g1_sels, base + 0xa100); + clks[IMX8MM_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mm_vpu_g2_sels, base + 0xa180); + clks[IMX8MM_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mm_disp_dtrc_sels, base + 0xa200); + clks[IMX8MM_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mm_disp_dc8000_sels, base + 0xa280); + clks[IMX8MM_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, base + 0xa300); + clks[IMX8MM_CLK_PCIE1_PHY] = imx8m_clk_composite("pcie1_phy", imx8mm_pcie1_phy_sels, base + 0xa380); + clks[IMX8MM_CLK_PCIE1_AUX] = imx8m_clk_composite("pcie1_aux", imx8mm_pcie1_aux_sels, base + 0xa400); + clks[IMX8MM_CLK_DC_PIXEL] = imx8m_clk_composite("dc_pixel", imx8mm_dc_pixel_sels, base + 0xa480); + clks[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_composite("lcdif_pixel", imx8mm_lcdif_pixel_sels, base + 0xa500); + clks[IMX8MM_CLK_SAI1] = imx8m_clk_composite("sai1", imx8mm_sai1_sels, base + 0xa580); + clks[IMX8MM_CLK_SAI2] = imx8m_clk_composite("sai2", imx8mm_sai2_sels, base + 0xa600); + clks[IMX8MM_CLK_SAI3] = imx8m_clk_composite("sai3", imx8mm_sai3_sels, base + 0xa680); + clks[IMX8MM_CLK_SAI4] = imx8m_clk_composite("sai4", imx8mm_sai4_sels, base + 0xa700); + clks[IMX8MM_CLK_SAI5] = imx8m_clk_composite("sai5", imx8mm_sai5_sels, base + 0xa780); + clks[IMX8MM_CLK_SAI6] = imx8m_clk_composite("sai6", imx8mm_sai6_sels, base + 0xa800); + clks[IMX8MM_CLK_SPDIF1] = imx8m_clk_composite("spdif1", imx8mm_spdif1_sels, base + 0xa880); + clks[IMX8MM_CLK_SPDIF2] = imx8m_clk_composite("spdif2", imx8mm_spdif2_sels, base + 0xa900); + clks[IMX8MM_CLK_ENET_REF] = imx8m_clk_composite("enet_ref", imx8mm_enet_ref_sels, base + 0xa980); + clks[IMX8MM_CLK_ENET_TIMER] = imx8m_clk_composite("enet_timer", imx8mm_enet_timer_sels, base + 0xaa00); + clks[IMX8MM_CLK_ENET_PHY_REF] = imx8m_clk_composite("enet_phy", imx8mm_enet_phy_sels, base + 0xaa80); + clks[IMX8MM_CLK_NAND] = imx8m_clk_composite("nand", imx8mm_nand_sels, base + 0xab00); + clks[IMX8MM_CLK_QSPI] = imx8m_clk_composite("qspi", imx8mm_qspi_sels, base + 0xab80); + clks[IMX8MM_CLK_USDHC1] = imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels, base + 0xac00); + clks[IMX8MM_CLK_USDHC2] = imx8m_clk_composite("usdhc2", imx8mm_usdhc2_sels, base + 0xac80); + clks[IMX8MM_CLK_I2C1] = imx8m_clk_composite("i2c1", imx8mm_i2c1_sels, base + 0xad00); + clks[IMX8MM_CLK_I2C2] = imx8m_clk_composite("i2c2", imx8mm_i2c2_sels, base + 0xad80); + clks[IMX8MM_CLK_I2C3] = imx8m_clk_composite("i2c3", imx8mm_i2c3_sels, base + 0xae00); + clks[IMX8MM_CLK_I2C4] = imx8m_clk_composite("i2c4", imx8mm_i2c4_sels, base + 0xae80); + clks[IMX8MM_CLK_UART1] = imx8m_clk_composite("uart1", imx8mm_uart1_sels, base + 0xaf00); + clks[IMX8MM_CLK_UART2] = imx8m_clk_composite("uart2", imx8mm_uart2_sels, base + 0xaf80); + clks[IMX8MM_CLK_UART3] = imx8m_clk_composite("uart3", imx8mm_uart3_sels, base + 0xb000); + clks[IMX8MM_CLK_UART4] = imx8m_clk_composite("uart4", imx8mm_uart4_sels, base + 0xb080); + clks[IMX8MM_CLK_USB_CORE_REF] = imx8m_clk_composite("usb_core_ref", imx8mm_usb_core_sels, base + 0xb100); + clks[IMX8MM_CLK_USB_PHY_REF] = imx8m_clk_composite("usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180); + clks[IMX8MM_CLK_ECSPI1] = imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280); + clks[IMX8MM_CLK_ECSPI2] = imx8m_clk_composite("ecspi2", imx8mm_ecspi2_sels, base + 0xb300); + clks[IMX8MM_CLK_PWM1] = imx8m_clk_composite("pwm1", imx8mm_pwm1_sels, base + 0xb380); + clks[IMX8MM_CLK_PWM2] = imx8m_clk_composite("pwm2", imx8mm_pwm2_sels, base + 0xb400); + clks[IMX8MM_CLK_PWM3] = imx8m_clk_composite("pwm3", imx8mm_pwm3_sels, base + 0xb480); + clks[IMX8MM_CLK_PWM4] = imx8m_clk_composite("pwm4", imx8mm_pwm4_sels, base + 0xb500); + clks[IMX8MM_CLK_GPT1] = imx8m_clk_composite("gpt1", imx8mm_gpt1_sels, base + 0xb580); + clks[IMX8MM_CLK_WDOG] = imx8m_clk_composite("wdog", imx8mm_wdog_sels, base + 0xb900); + clks[IMX8MM_CLK_WRCLK] = imx8m_clk_composite("wrclk", imx8mm_wrclk_sels, base + 0xb980); + clks[IMX8MM_CLK_CLKO1] = imx8m_clk_composite("clko1", imx8mm_clko1_sels, base + 0xba00); + clks[IMX8MM_CLK_DSI_CORE] = imx8m_clk_composite("dsi_core", imx8mm_dsi_core_sels, base + 0xbb00); + clks[IMX8MM_CLK_DSI_PHY_REF] = imx8m_clk_composite("dsi_phy_ref", imx8mm_dsi_phy_sels, base + 0xbb80); + clks[IMX8MM_CLK_DSI_DBI] = imx8m_clk_composite("dsi_dbi", imx8mm_dsi_dbi_sels, base + 0xbc00); + clks[IMX8MM_CLK_USDHC3] = imx8m_clk_composite("usdhc3", imx8mm_usdhc3_sels, base + 0xbc80); + clks[IMX8MM_CLK_CSI1_CORE] = imx8m_clk_composite("csi1_core", imx8mm_csi1_core_sels, base + 0xbd00); + clks[IMX8MM_CLK_CSI1_PHY_REF] = imx8m_clk_composite("csi1_phy_ref", imx8mm_csi1_phy_sels, base + 0xbd80); + clks[IMX8MM_CLK_CSI1_ESC] = imx8m_clk_composite("csi1_esc", imx8mm_csi1_esc_sels, base + 0xbe00); + clks[IMX8MM_CLK_CSI2_CORE] = imx8m_clk_composite("csi2_core", imx8mm_csi2_core_sels, base + 0xbe80); + clks[IMX8MM_CLK_CSI2_PHY_REF] = imx8m_clk_composite("csi2_phy_ref", imx8mm_csi2_phy_sels, base + 0xbf00); + clks[IMX8MM_CLK_CSI2_ESC] = imx8m_clk_composite("csi2_esc", imx8mm_csi2_esc_sels, base + 0xbf80); + clks[IMX8MM_CLK_PCIE2_CTRL] = imx8m_clk_composite("pcie2_ctrl", imx8mm_pcie2_ctrl_sels, base + 0xc000); + clks[IMX8MM_CLK_PCIE2_PHY] = imx8m_clk_composite("pcie2_phy", imx8mm_pcie2_phy_sels, base + 0xc080); + clks[IMX8MM_CLK_PCIE2_AUX] = imx8m_clk_composite("pcie2_aux", imx8mm_pcie2_aux_sels, base + 0xc100); + clks[IMX8MM_CLK_ECSPI3] = imx8m_clk_composite("ecspi3", imx8mm_ecspi3_sels, base + 0xc180); + clks[IMX8MM_CLK_PDM] = imx8m_clk_composite("pdm", imx8mm_pdm_sels, base + 0xc200); + clks[IMX8MM_CLK_VPU_H1] = imx8m_clk_composite("vpu_h1", imx8mm_vpu_h1_sels, base + 0xc280); + + /* CCGR */ + clks[IMX8MM_CLK_ECSPI1_ROOT] = imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0); + clks[IMX8MM_CLK_ECSPI2_ROOT] = imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0); + clks[IMX8MM_CLK_ECSPI3_ROOT] = imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0); + clks[IMX8MM_CLK_ENET1_ROOT] = imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0); + clks[IMX8MM_CLK_GPT1_ROOT] = imx_clk_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0); + clks[IMX8MM_CLK_I2C1_ROOT] = imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0); + clks[IMX8MM_CLK_I2C2_ROOT] = imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0); + clks[IMX8MM_CLK_I2C3_ROOT] = imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0); + clks[IMX8MM_CLK_I2C4_ROOT] = imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0); + clks[IMX8MM_CLK_MU_ROOT] = imx_clk_gate4("mu_root_clk", "ipg_root", base + 0x4210, 0); + clks[IMX8MM_CLK_OCOTP_ROOT] = imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0); + clks[IMX8MM_CLK_PCIE1_ROOT] = imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0); + clks[IMX8MM_CLK_PWM1_ROOT] = imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0); + clks[IMX8MM_CLK_PWM2_ROOT] = imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0); + clks[IMX8MM_CLK_PWM3_ROOT] = imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0); + clks[IMX8MM_CLK_PWM4_ROOT] = imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0); + clks[IMX8MM_CLK_QSPI_ROOT] = imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0); + clks[IMX8MM_CLK_NAND_ROOT] = imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand); + clks[IMX8MM_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand); + clks[IMX8MM_CLK_SAI1_ROOT] = imx_clk_gate2_shared2("sai1_root_clk", "sai1", base + 0x4330, 0, &share_count_sai1); + clks[IMX8MM_CLK_SAI1_IPG] = imx_clk_gate2_shared2("sai1_ipg_clk", "ipg_audio_root", base + 0x4330, 0, &share_count_sai1); + clks[IMX8MM_CLK_SAI2_ROOT] = imx_clk_gate2_shared2("sai2_root_clk", "sai2", base + 0x4340, 0, &share_count_sai2); + clks[IMX8MM_CLK_SAI2_IPG] = imx_clk_gate2_shared2("sai2_ipg_clk", "ipg_audio_root", base + 0x4340, 0, &share_count_sai2); + clks[IMX8MM_CLK_SAI3_ROOT] = imx_clk_gate2_shared2("sai3_root_clk", "sai3", base + 0x4350, 0, &share_count_sai3); + clks[IMX8MM_CLK_SAI3_IPG] = imx_clk_gate2_shared2("sai3_ipg_clk", "ipg_audio_root", base + 0x4350, 0, &share_count_sai3); + clks[IMX8MM_CLK_SAI4_ROOT] = imx_clk_gate2_shared2("sai4_root_clk", "sai4", base + 0x4360, 0, &share_count_sai4); + clks[IMX8MM_CLK_SAI4_IPG] = imx_clk_gate2_shared2("sai4_ipg_clk", "ipg_audio_root", base + 0x4360, 0, &share_count_sai4); + clks[IMX8MM_CLK_SAI5_ROOT] = imx_clk_gate2_shared2("sai5_root_clk", "sai5", base + 0x4370, 0, &share_count_sai5); + clks[IMX8MM_CLK_SAI5_IPG] = imx_clk_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", base + 0x4370, 0, &share_count_sai5); + clks[IMX8MM_CLK_SAI6_ROOT] = imx_clk_gate2_shared2("sai6_root_clk", "sai6", base + 0x4380, 0, &share_count_sai6); + clks[IMX8MM_CLK_SAI6_IPG] = imx_clk_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", base + 0x4380, 0, &share_count_sai6); + clks[IMX8MM_CLK_UART1_ROOT] = imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0); + clks[IMX8MM_CLK_UART2_ROOT] = imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0); + clks[IMX8MM_CLK_UART3_ROOT] = imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); + clks[IMX8MM_CLK_UART4_ROOT] = imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); + clks[IMX8MM_CLK_USB1_CTRL_ROOT] = imx_clk_gate4("usb1_ctrl_root_clk", "usb_core_ref", base + 0x44d0, 0); + clks[IMX8MM_CLK_GPU3D_ROOT] = imx_clk_gate4("gpu3d_root_clk", "gpu3d_div", base + 0x44f0, 0); + clks[IMX8MM_CLK_USDHC1_ROOT] = imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); + clks[IMX8MM_CLK_USDHC2_ROOT] = imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); + clks[IMX8MM_CLK_WDOG1_ROOT] = imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0); + clks[IMX8MM_CLK_WDOG2_ROOT] = imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0); + clks[IMX8MM_CLK_WDOG3_ROOT] = imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0); + clks[IMX8MM_CLK_VPU_G1_ROOT] = imx_clk_gate4("vpu_g1_root_clk", "vpu_g1", base + 0x4560, 0); + clks[IMX8MM_CLK_GPU_BUS_ROOT] = imx_clk_gate4("gpu_root_clk", "gpu_axi", base + 0x4570, 0); + clks[IMX8MM_CLK_VPU_H1_ROOT] = imx_clk_gate4("vpu_h1_root_clk", "vpu_h1", base + 0x4590, 0); + clks[IMX8MM_CLK_VPU_G2_ROOT] = imx_clk_gate4("vpu_g2_root_clk", "vpu_g2", base + 0x45a0, 0); + clks[IMX8MM_CLK_PDM_ROOT] = imx_clk_gate2_shared2("pdm_root_clk", "pdm", base + 0x45b0, 0, &share_count_pdm); + clks[IMX8MM_CLK_PDM_IPG] = imx_clk_gate2_shared2("pdm_ipg_clk", "ipg_audio_root", base + 0x45b0, 0, &share_count_pdm); + clks[IMX8MM_CLK_DISP_ROOT] = imx_clk_gate2_shared2("disp_root_clk", "disp_dc8000", base + 0x45d0, 0, &share_count_dcss); + clks[IMX8MM_CLK_DISP_AXI_ROOT] = imx_clk_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_dcss); + clks[IMX8MM_CLK_DISP_APB_ROOT] = imx_clk_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, &share_count_dcss); + clks[IMX8MM_CLK_DISP_RTRM_ROOT] = imx_clk_gate2_shared2("disp_rtrm_root_clk", "disp_rtrm", base + 0x45d0, 0, &share_count_dcss); + clks[IMX8MM_CLK_USDHC3_ROOT] = imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0); + clks[IMX8MM_CLK_TMU_ROOT] = imx_clk_gate4("tmu_root_clk", "ipg_root", base + 0x4620, 0); + clks[IMX8MM_CLK_VPU_DEC_ROOT] = imx_clk_gate4("vpu_dec_root_clk", "vpu_bus", base + 0x4630, 0); + clks[IMX8MM_CLK_SDMA1_ROOT] = imx_clk_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0); + clks[IMX8MM_CLK_SDMA2_ROOT] = imx_clk_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0); + clks[IMX8MM_CLK_SDMA3_ROOT] = imx_clk_gate4("sdma3_clk", "ipg_audio_root", base + 0x45f0, 0); + clks[IMX8MM_CLK_GPU2D_ROOT] = imx_clk_gate4("gpu2d_root_clk", "gpu2d_div", base + 0x4660, 0); + clks[IMX8MM_CLK_CSI1_ROOT] = imx_clk_gate4("csi1_root_clk", "csi1_core", base + 0x4650, 0); + + clks[IMX8MM_CLK_GPT_3M] = imx_clk_fixed_factor("gpt_3m", "osc_24m", 1, 8); + + clks[IMX8MM_CLK_DRAM_ALT_ROOT] = imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + clks[IMX8MM_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mm_dram_core_sels, ARRAY_SIZE(imx8mm_dram_core_sels), CLK_IS_CRITICAL); + + clks[IMX8MM_CLK_ARM] = imx_clk_cpu("arm", "arm_a53_div", + clks[IMX8MM_CLK_A53_DIV], + clks[IMX8MM_CLK_A53_SRC], + clks[IMX8MM_ARM_PLL_OUT], + clks[IMX8MM_CLK_24M]); + + imx_check_clocks(clks, ARRAY_SIZE(clks)); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + if (ret < 0) { + pr_err("failed to register clks for i.MX8MM\n"); + return -EINVAL; + } + + imx_register_uart_clocks(uart_clks); + + return 0; +} +CLK_OF_DECLARE_DRIVER(imx8mm, "fsl,imx8mm-ccm", imx8mm_clocks_init); diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 26b57f43ccc3..a9b3888aef0c 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -26,246 +26,246 @@ static u32 share_count_nand; static struct clk *clks[IMX8MQ_CLK_END]; -static const char *pll_ref_sels[] = { "osc_25m", "osc_27m", "dummy", "dummy", }; -static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; -static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; -static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; -static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; -static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; -static const char *video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; - -static const char *sys1_pll1_out_sels[] = {"sys1_pll1", "sys1_pll1_ref_sel", }; -static const char *sys2_pll1_out_sels[] = {"sys2_pll1", "sys1_pll1_ref_sel", }; -static const char *sys3_pll1_out_sels[] = {"sys3_pll1", "sys3_pll1_ref_sel", }; -static const char *dram_pll1_out_sels[] = {"dram_pll1", "dram_pll1_ref_sel", }; - -static const char *sys1_pll2_out_sels[] = {"sys1_pll2_div", "sys1_pll1_ref_sel", }; -static const char *sys2_pll2_out_sels[] = {"sys2_pll2_div", "sys2_pll1_ref_sel", }; -static const char *sys3_pll2_out_sels[] = {"sys3_pll2_div", "sys2_pll1_ref_sel", }; -static const char *dram_pll2_out_sels[] = {"dram_pll2_div", "dram_pll1_ref_sel", }; +static const char * const pll_ref_sels[] = { "osc_25m", "osc_27m", "dummy", "dummy", }; +static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; + +static const char * const sys1_pll_out_sels[] = {"sys1_pll1_ref_sel", }; +static const char * const sys2_pll_out_sels[] = {"sys1_pll1_ref_sel", "sys2_pll1_ref_sel", }; +static const char * const sys3_pll_out_sels[] = {"sys3_pll1_ref_sel", "sys2_pll1_ref_sel", }; +static const char * const dram_pll_out_sels[] = {"dram_pll1_ref_sel", }; /* CCM ROOT */ -static const char *imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", +static const char * const imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", "sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "sys3_pll2_out", }; -static const char *imx8mq_vpu_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", +static const char * const imx8mq_arm_m4_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_250m", "sys1_pll_266m", + "sys1_pll_800m", "audio_pll1_out", "video_pll1_out", "sys3_pll2_out", }; + +static const char * const imx8mq_vpu_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", "sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "vpu_pll_out", }; -static const char *imx8mq_gpu_core_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out", +static const char * const imx8mq_gpu_core_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char *imx8mq_gpu_shader_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out", +static const char * const imx8mq_gpu_shader_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char *imx8mq_main_axi_sels[] = {"osc_25m", "sys2_pll_333m", "sys1_pll_800m", "sys2_pll_250m", +static const char * const imx8mq_main_axi_sels[] = {"osc_25m", "sys2_pll_333m", "sys1_pll_800m", "sys2_pll_250m", "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "sys1_pll_100m",}; -static const char *imx8mq_enet_axi_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_250m", +static const char * const imx8mq_enet_axi_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_250m", "sys2_pll_200m", "audio_pll1_out", "video_pll1_out", "sys3_pll2_out", }; -static const char *imx8mq_nand_usdhc_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_200m", +static const char * const imx8mq_nand_usdhc_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_200m", "sys1_pll_133m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll1_out", }; -static const char *imx8mq_vpu_bus_sels[] = {"osc_25m", "sys1_pll_800m", "vpu_pll_out", "audio_pll2_out", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_200m", "sys1_pll_100m", }; +static const char * const imx8mq_vpu_bus_sels[] = {"osc_25m", "sys1_pll_800m", "vpu_pll_out", "audio_pll2_out", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_200m", "sys1_pll_100m", }; -static const char *imx8mq_disp_axi_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out", "sys1_pll_400m", "audio_pll2_out", "clk_ext1", "clk_ext4", }; +static const char * const imx8mq_disp_axi_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out", "sys1_pll_400m", "audio_pll2_out", "clk_ext1", "clk_ext4", }; -static const char *imx8mq_disp_apb_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out", +static const char * const imx8mq_disp_apb_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out", "sys1_pll_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", }; -static const char *imx8mq_disp_rtrm_sels[] = {"osc_25m", "sys1_pll_800m", "sys2_pll_200m", "sys1_pll_400m", +static const char * const imx8mq_disp_rtrm_sels[] = {"osc_25m", "sys1_pll_800m", "sys2_pll_200m", "sys1_pll_400m", "audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", }; -static const char *imx8mq_usb_bus_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m", +static const char * const imx8mq_usb_bus_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m", "sys2_pll_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char *imx8mq_gpu_axi_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m", +static const char * const imx8mq_gpu_axi_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char *imx8mq_gpu_ahb_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m", +static const char * const imx8mq_gpu_ahb_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char *imx8mq_noc_sels[] = {"osc_25m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_500m", +static const char * const imx8mq_noc_sels[] = {"osc_25m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char *imx8mq_noc_apb_sels[] = {"osc_25m", "sys1_pll_400m", "sys3_pll2_out", "sys2_pll_333m", "sys2_pll_200m", +static const char * const imx8mq_noc_apb_sels[] = {"osc_25m", "sys1_pll_400m", "sys3_pll2_out", "sys2_pll_333m", "sys2_pll_200m", "sys1_pll_800m", "audio_pll1_out", "video_pll1_out", }; -static const char *imx8mq_ahb_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m", +static const char * const imx8mq_ahb_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", }; -static const char *imx8mq_audio_ahb_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_1000m", +static const char * const imx8mq_audio_ahb_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_1000m", "sys2_pll_166m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", }; -static const char *imx8mq_dsi_ahb_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", +static const char * const imx8mq_dsi_ahb_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out"}; -static const char *imx8mq_dram_alt_sels[] = {"osc_25m", "sys1_pll_800m", "sys1_pll_100m", "sys2_pll_500m", +static const char * const imx8mq_dram_alt_sels[] = {"osc_25m", "sys1_pll_800m", "sys1_pll_100m", "sys2_pll_500m", "sys2_pll_250m", "sys1_pll_400m", "audio_pll1_out", "sys1_pll_266m", }; -static const char *imx8mq_dram_apb_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", +static const char * const imx8mq_dram_apb_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", }; -static const char *imx8mq_vpu_g1_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", }; +static const char * const imx8mq_vpu_g1_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", }; -static const char *imx8mq_vpu_g2_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", }; +static const char * const imx8mq_vpu_g2_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", }; -static const char *imx8mq_disp_dtrc_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", }; +static const char * const imx8mq_disp_dtrc_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", }; -static const char *imx8mq_disp_dc8000_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", }; +static const char * const imx8mq_disp_dc8000_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", }; -static const char *imx8mq_pcie1_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m", +static const char * const imx8mq_pcie1_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_500m", "sys2_pll_250m", "sys3_pll2_out", }; -static const char *imx8mq_pcie1_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", "clk_ext2", +static const char * const imx8mq_pcie1_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", }; -static const char *imx8mq_pcie1_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_500m", "sys3_pll2_out", +static const char * const imx8mq_pcie1_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_500m", "sys3_pll2_out", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", }; -static const char *imx8mq_dc_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", }; +static const char * const imx8mq_dc_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", }; -static const char *imx8mq_lcdif_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", }; +static const char * const imx8mq_lcdif_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", }; -static const char *imx8mq_sai1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", }; +static const char * const imx8mq_sai1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", }; -static const char *imx8mq_sai2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; +static const char * const imx8mq_sai2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; -static const char *imx8mq_sai3_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; +static const char * const imx8mq_sai3_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; -static const char *imx8mq_sai4_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", }; +static const char * const imx8mq_sai4_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", }; -static const char *imx8mq_sai5_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; +static const char * const imx8mq_sai5_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; -static const char *imx8mq_sai6_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; +static const char * const imx8mq_sai6_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; -static const char *imx8mq_spdif1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; +static const char * const imx8mq_spdif1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; -static const char *imx8mq_spdif2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; +static const char * const imx8mq_spdif2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; -static const char *imx8mq_enet_ref_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_500m", "sys2_pll_100m", +static const char * const imx8mq_enet_ref_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_500m", "sys2_pll_100m", "sys1_pll_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; -static const char *imx8mq_enet_timer_sels[] = {"osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", +static const char * const imx8mq_enet_timer_sels[] = {"osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll1_out", }; -static const char *imx8mq_enet_phy_sels[] = {"osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m", +static const char * const imx8mq_enet_phy_sels[] = {"osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char *imx8mq_nand_sels[] = {"osc_25m", "sys2_pll_500m", "audio_pll1_out", "sys1_pll_400m", +static const char * const imx8mq_nand_sels[] = {"osc_25m", "sys2_pll_500m", "audio_pll1_out", "sys1_pll_400m", "audio_pll2_out", "sys3_pll2_out", "sys2_pll_250m", "video_pll1_out", }; -static const char *imx8mq_qspi_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", +static const char * const imx8mq_qspi_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", "audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", }; -static const char *imx8mq_usdhc1_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", +static const char * const imx8mq_usdhc1_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", "audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", }; -static const char *imx8mq_usdhc2_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", +static const char * const imx8mq_usdhc2_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", "audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", }; -static const char *imx8mq_i2c1_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", +static const char * const imx8mq_i2c1_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys1_pll_133m", }; -static const char *imx8mq_i2c2_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", +static const char * const imx8mq_i2c2_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys1_pll_133m", }; -static const char *imx8mq_i2c3_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", +static const char * const imx8mq_i2c3_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys1_pll_133m", }; -static const char *imx8mq_i2c4_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", +static const char * const imx8mq_i2c4_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys1_pll_133m", }; -static const char *imx8mq_uart1_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", +static const char * const imx8mq_uart1_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", "sys3_pll2_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char *imx8mq_uart2_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", +static const char * const imx8mq_uart2_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", "sys3_pll2_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char *imx8mq_uart3_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", +static const char * const imx8mq_uart3_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", "sys3_pll2_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char *imx8mq_uart4_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", +static const char * const imx8mq_uart4_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", "sys3_pll2_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char *imx8mq_usb_core_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", +static const char * const imx8mq_usb_core_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", "sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char *imx8mq_usb_phy_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", +static const char * const imx8mq_usb_phy_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", "sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char *imx8mq_ecspi1_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", +static const char * const imx8mq_ecspi1_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", }; -static const char *imx8mq_ecspi2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", +static const char * const imx8mq_ecspi2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", }; -static const char *imx8mq_pwm1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", +static const char * const imx8mq_pwm1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", "sys3_pll2_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", }; -static const char *imx8mq_pwm2_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", +static const char * const imx8mq_pwm2_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", "sys3_pll2_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", }; -static const char *imx8mq_pwm3_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", +static const char * const imx8mq_pwm3_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", "sys3_pll2_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", }; -static const char *imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", +static const char * const imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", "sys3_pll2_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", }; -static const char *imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m", +static const char * const imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m", "sys1_pll_80m", "audio_pll1_out", "clk_ext1", }; -static const char *imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out", +static const char * const imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out", "sys2_pll_125m", "sys3_pll2_out", "sys1_pll_80m", "sys2_pll_166m", }; -static const char *imx8mq_wrclk_sels[] = {"osc_25m", "sys1_pll_40m", "vpu_pll_out", "sys3_pll2_out", "sys2_pll_200m", +static const char * const imx8mq_wrclk_sels[] = {"osc_25m", "sys1_pll_40m", "vpu_pll_out", "sys3_pll2_out", "sys2_pll_200m", "sys1_pll_266m", "sys2_pll_500m", "sys1_pll_100m", }; -static const char *imx8mq_dsi_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", +static const char * const imx8mq_dsi_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", }; -static const char *imx8mq_dsi_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", +static const char * const imx8mq_dsi_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; -static const char *imx8mq_dsi_dbi_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_100m", "sys1_pll_800m", +static const char * const imx8mq_dsi_dbi_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_100m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", }; -static const char *imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", +static const char * const imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", }; -static const char *imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", +static const char * const imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", }; -static const char *imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", +static const char * const imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; -static const char *imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", +static const char * const imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", }; -static const char *imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", +static const char * const imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", }; -static const char *imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", +static const char * const imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; -static const char *imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", +static const char * const imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", }; -static const char *imx8mq_pcie2_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m", +static const char * const imx8mq_pcie2_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_500m", "sys2_pll_333m", "sys3_pll2_out", }; -static const char *imx8mq_pcie2_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", +static const char * const imx8mq_pcie2_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "sys1_pll_400m", }; -static const char *imx8mq_pcie2_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_50m", "sys3_pll2_out", +static const char * const imx8mq_pcie2_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_50m", "sys3_pll2_out", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", }; -static const char *imx8mq_ecspi3_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", +static const char * const imx8mq_ecspi3_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", }; -static const char *imx8mq_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; +static const char * const imx8mq_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; -static const char *imx8mq_clko2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_400m", "sys2_pll_166m", "audio_pll1_out", - "video_pll1_out", "ckil", }; +static const char * const imx8mq_clko1_sels[] = {"osc_25m", "sys1_pll_800m", "osc_27m", "sys1_pll_200m", + "audio_pll2_out", "sys2_pll_500m", "vpu_pll_out", "sys1_pll_80m", }; +static const char * const imx8mq_clko2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_400m", "sys2_pll_166m", + "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", "ckil", }; static struct clk_onecell_data clk_data; @@ -308,10 +308,6 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_AUDIO_PLL1_REF_DIV] = imx_clk_divider("audio_pll1_ref_div", "audio_pll1_ref_sel", base + 0x0, 5, 6); clks[IMX8MQ_AUDIO_PLL2_REF_DIV] = imx_clk_divider("audio_pll2_ref_div", "audio_pll2_ref_sel", base + 0x8, 5, 6); clks[IMX8MQ_VIDEO_PLL1_REF_DIV] = imx_clk_divider("video_pll1_ref_div", "video_pll1_ref_sel", base + 0x10, 5, 6); - clks[IMX8MQ_SYS1_PLL1_REF_DIV] = imx_clk_divider("sys1_pll1_ref_div", "sys1_pll1_ref_sel", base + 0x38, 25, 3); - clks[IMX8MQ_SYS2_PLL1_REF_DIV] = imx_clk_divider("sys2_pll1_ref_div", "sys2_pll1_ref_sel", base + 0x44, 25, 3); - clks[IMX8MQ_SYS3_PLL1_REF_DIV] = imx_clk_divider("sys3_pll1_ref_div", "sys3_pll1_ref_sel", base + 0x50, 25, 3); - clks[IMX8MQ_DRAM_PLL1_REF_DIV] = imx_clk_divider("dram_pll1_ref_div", "dram_pll1_ref_sel", base + 0x68, 25, 3); clks[IMX8MQ_ARM_PLL] = imx_clk_frac_pll("arm_pll", "arm_pll_ref_div", base + 0x28); clks[IMX8MQ_GPU_PLL] = imx_clk_frac_pll("gpu_pll", "gpu_pll_ref_div", base + 0x18); @@ -319,43 +315,15 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_AUDIO_PLL1] = imx_clk_frac_pll("audio_pll1", "audio_pll1_ref_div", base + 0x0); clks[IMX8MQ_AUDIO_PLL2] = imx_clk_frac_pll("audio_pll2", "audio_pll2_ref_div", base + 0x8); clks[IMX8MQ_VIDEO_PLL1] = imx_clk_frac_pll("video_pll1", "video_pll1_ref_div", base + 0x10); - clks[IMX8MQ_SYS1_PLL1] = imx_clk_sccg_pll("sys1_pll1", "sys1_pll1_ref_div", base + 0x30, SCCG_PLL1); - clks[IMX8MQ_SYS2_PLL1] = imx_clk_sccg_pll("sys2_pll1", "sys2_pll1_ref_div", base + 0x3c, SCCG_PLL1); - clks[IMX8MQ_SYS3_PLL1] = imx_clk_sccg_pll("sys3_pll1", "sys3_pll1_ref_div", base + 0x48, SCCG_PLL1); - clks[IMX8MQ_DRAM_PLL1] = imx_clk_sccg_pll("dram_pll1", "dram_pll1_ref_div", base + 0x60, SCCG_PLL1); - - clks[IMX8MQ_SYS1_PLL2] = imx_clk_sccg_pll("sys1_pll2", "sys1_pll1_out_div", base + 0x30, SCCG_PLL2); - clks[IMX8MQ_SYS2_PLL2] = imx_clk_sccg_pll("sys2_pll2", "sys2_pll1_out_div", base + 0x3c, SCCG_PLL2); - clks[IMX8MQ_SYS3_PLL2] = imx_clk_sccg_pll("sys3_pll2", "sys3_pll1_out_div", base + 0x48, SCCG_PLL2); - clks[IMX8MQ_DRAM_PLL2] = imx_clk_sccg_pll("dram_pll2", "dram_pll1_out_div", base + 0x60, SCCG_PLL2); - - /* PLL divs */ - clks[IMX8MQ_SYS1_PLL1_OUT_DIV] = imx_clk_divider("sys1_pll1_out_div", "sys1_pll1_out", base + 0x38, 19, 6); - clks[IMX8MQ_SYS2_PLL1_OUT_DIV] = imx_clk_divider("sys2_pll1_out_div", "sys2_pll1_out", base + 0x44, 19, 6); - clks[IMX8MQ_SYS3_PLL1_OUT_DIV] = imx_clk_divider("sys3_pll1_out_div", "sys3_pll1_out", base + 0x50, 19, 6); - clks[IMX8MQ_DRAM_PLL1_OUT_DIV] = imx_clk_divider("dram_pll1_out_div", "dram_pll1_out", base + 0x68, 19, 6); - clks[IMX8MQ_SYS1_PLL2_DIV] = imx_clk_divider("sys1_pll2_div", "sys1_pll2", base + 0x38, 1, 6); - clks[IMX8MQ_SYS2_PLL2_DIV] = imx_clk_divider("sys2_pll2_div", "sys2_pll2", base + 0x44, 1, 6); - clks[IMX8MQ_SYS3_PLL2_DIV] = imx_clk_divider("sys3_pll2_div", "sys3_pll2", base + 0x50, 1, 6); - clks[IMX8MQ_DRAM_PLL2_DIV] = imx_clk_divider("dram_pll2_div", "dram_pll2", base + 0x68, 1, 6); /* PLL bypass out */ - clks[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_mux("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels)); + clks[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); clks[IMX8MQ_GPU_PLL_BYPASS] = imx_clk_mux("gpu_pll_bypass", base + 0x18, 14, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels)); clks[IMX8MQ_VPU_PLL_BYPASS] = imx_clk_mux("vpu_pll_bypass", base + 0x20, 14, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels)); clks[IMX8MQ_AUDIO_PLL1_BYPASS] = imx_clk_mux("audio_pll1_bypass", base + 0x0, 14, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels)); clks[IMX8MQ_AUDIO_PLL2_BYPASS] = imx_clk_mux("audio_pll2_bypass", base + 0x8, 14, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels)); clks[IMX8MQ_VIDEO_PLL1_BYPASS] = imx_clk_mux("video_pll1_bypass", base + 0x10, 14, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels)); - clks[IMX8MQ_SYS1_PLL1_OUT] = imx_clk_mux("sys1_pll1_out", base + 0x30, 5, 1, sys1_pll1_out_sels, ARRAY_SIZE(sys1_pll1_out_sels)); - clks[IMX8MQ_SYS2_PLL1_OUT] = imx_clk_mux("sys2_pll1_out", base + 0x3c, 5, 1, sys2_pll1_out_sels, ARRAY_SIZE(sys2_pll1_out_sels)); - clks[IMX8MQ_SYS3_PLL1_OUT] = imx_clk_mux("sys3_pll1_out", base + 0x48, 5, 1, sys3_pll1_out_sels, ARRAY_SIZE(sys3_pll1_out_sels)); - clks[IMX8MQ_DRAM_PLL1_OUT] = imx_clk_mux("dram_pll1_out", base + 0x60, 5, 1, dram_pll1_out_sels, ARRAY_SIZE(dram_pll1_out_sels)); - clks[IMX8MQ_SYS1_PLL2_OUT] = imx_clk_mux("sys1_pll2_out", base + 0x30, 4, 1, sys1_pll2_out_sels, ARRAY_SIZE(sys1_pll2_out_sels)); - clks[IMX8MQ_SYS2_PLL2_OUT] = imx_clk_mux("sys2_pll2_out", base + 0x3c, 4, 1, sys2_pll2_out_sels, ARRAY_SIZE(sys2_pll2_out_sels)); - clks[IMX8MQ_SYS3_PLL2_OUT] = imx_clk_mux("sys3_pll2_out", base + 0x48, 4, 1, sys3_pll2_out_sels, ARRAY_SIZE(sys3_pll2_out_sels)); - clks[IMX8MQ_DRAM_PLL2_OUT] = imx_clk_mux("dram_pll2_out", base + 0x60, 4, 1, dram_pll2_out_sels, ARRAY_SIZE(dram_pll2_out_sels)); - /* PLL OUT GATE */ clks[IMX8MQ_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x28, 21); clks[IMX8MQ_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x18, 21); @@ -363,11 +331,11 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base + 0x0, 21); clks[IMX8MQ_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x8, 21); clks[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21); - clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_gate("sys1_pll_out", "sys1_pll2_out", base + 0x30, 9); - clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_gate("sys2_pll_out", "sys2_pll2_out", base + 0x3c, 9); - clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_gate("sys3_pll_out", "sys3_pll2_out", base + 0x48, 9); - clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll2_out", base + 0x60, 9); + clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_sccg_pll("sys1_pll_out", sys1_pll_out_sels, ARRAY_SIZE(sys1_pll_out_sels), 0, 0, 0, base + 0x30, CLK_IS_CRITICAL); + clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_sccg_pll("sys2_pll_out", sys2_pll_out_sels, ARRAY_SIZE(sys2_pll_out_sels), 0, 0, 1, base + 0x3c, CLK_IS_CRITICAL); + clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_sccg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 1, base + 0x48, CLK_IS_CRITICAL); + clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_sccg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, CLK_IS_CRITICAL); /* SYS PLL fixed output */ clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20); clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10); @@ -396,15 +364,19 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) /* CORE */ clks[IMX8MQ_CLK_A53_SRC] = imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels)); + clks[IMX8MQ_CLK_M4_SRC] = imx_clk_mux2("arm_m4_src", base + 0x8080, 24, 3, imx8mq_arm_m4_sels, ARRAY_SIZE(imx8mq_arm_m4_sels)); clks[IMX8MQ_CLK_VPU_SRC] = imx_clk_mux2("vpu_src", base + 0x8100, 24, 3, imx8mq_vpu_sels, ARRAY_SIZE(imx8mq_vpu_sels)); clks[IMX8MQ_CLK_GPU_CORE_SRC] = imx_clk_mux2("gpu_core_src", base + 0x8180, 24, 3, imx8mq_gpu_core_sels, ARRAY_SIZE(imx8mq_gpu_core_sels)); clks[IMX8MQ_CLK_GPU_SHADER_SRC] = imx_clk_mux2("gpu_shader_src", base + 0x8200, 24, 3, imx8mq_gpu_shader_sels, ARRAY_SIZE(imx8mq_gpu_shader_sels)); + clks[IMX8MQ_CLK_A53_CG] = imx_clk_gate3_flags("arm_a53_cg", "arm_a53_src", base + 0x8000, 28, CLK_IS_CRITICAL); + clks[IMX8MQ_CLK_M4_CG] = imx_clk_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28); clks[IMX8MQ_CLK_VPU_CG] = imx_clk_gate3("vpu_cg", "vpu_src", base + 0x8100, 28); clks[IMX8MQ_CLK_GPU_CORE_CG] = imx_clk_gate3("gpu_core_cg", "gpu_core_src", base + 0x8180, 28); clks[IMX8MQ_CLK_GPU_SHADER_CG] = imx_clk_gate3("gpu_shader_cg", "gpu_shader_src", base + 0x8200, 28); clks[IMX8MQ_CLK_A53_DIV] = imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3); + clks[IMX8MQ_CLK_M4_DIV] = imx_clk_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3); clks[IMX8MQ_CLK_VPU_DIV] = imx_clk_divider2("vpu_div", "vpu_cg", base + 0x8100, 0, 3); clks[IMX8MQ_CLK_GPU_CORE_DIV] = imx_clk_divider2("gpu_core_div", "gpu_core_cg", base + 0x8180, 0, 3); clks[IMX8MQ_CLK_GPU_SHADER_DIV] = imx_clk_divider2("gpu_shader_div", "gpu_shader_cg", base + 0x8200, 0, 3); @@ -479,6 +451,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_CLK_GPT1] = imx8m_clk_composite("gpt1", imx8mq_gpt1_sels, base + 0xb580); clks[IMX8MQ_CLK_WDOG] = imx8m_clk_composite("wdog", imx8mq_wdog_sels, base + 0xb900); clks[IMX8MQ_CLK_WRCLK] = imx8m_clk_composite("wrclk", imx8mq_wrclk_sels, base + 0xb980); + clks[IMX8MQ_CLK_CLKO1] = imx8m_clk_composite("clko1", imx8mq_clko1_sels, base + 0xba00); clks[IMX8MQ_CLK_CLKO2] = imx8m_clk_composite("clko2", imx8mq_clko2_sels, base + 0xba80); clks[IMX8MQ_CLK_DSI_CORE] = imx8m_clk_composite("dsi_core", imx8mq_dsi_core_sels, base + 0xbb00); clks[IMX8MQ_CLK_DSI_PHY_REF] = imx8m_clk_composite("dsi_phy_ref", imx8mq_dsi_phy_sels, base + 0xbb80); @@ -500,6 +473,11 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_CLK_ECSPI2_ROOT] = imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0); clks[IMX8MQ_CLK_ECSPI3_ROOT] = imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0); clks[IMX8MQ_CLK_ENET1_ROOT] = imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0); + clks[IMX8MQ_CLK_GPIO1_ROOT] = imx_clk_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0); + clks[IMX8MQ_CLK_GPIO2_ROOT] = imx_clk_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0); + clks[IMX8MQ_CLK_GPIO3_ROOT] = imx_clk_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0); + clks[IMX8MQ_CLK_GPIO4_ROOT] = imx_clk_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0); + clks[IMX8MQ_CLK_GPIO5_ROOT] = imx_clk_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0); clks[IMX8MQ_CLK_GPT1_ROOT] = imx_clk_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0); clks[IMX8MQ_CLK_I2C1_ROOT] = imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0); clks[IMX8MQ_CLK_I2C2_ROOT] = imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0); @@ -558,6 +536,12 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_GPT_3M_CLK] = imx_clk_fixed_factor("gpt_3m", "osc_25m", 1, 8); clks[IMX8MQ_CLK_DRAM_ALT_ROOT] = imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + clks[IMX8MQ_CLK_ARM] = imx_clk_cpu("arm", "arm_a53_div", + clks[IMX8MQ_CLK_A53_DIV], + clks[IMX8MQ_CLK_A53_SRC], + clks[IMX8MQ_ARM_PLL_OUT], + clks[IMX8MQ_SYS1_PLL_800M]); + for (i = 0; i < IMX8MQ_CLK_END; i++) if (IS_ERR(clks[i])) pr_err("i.MX8mq clk %u register failed with %ld\n", diff --git a/drivers/clk/imx/clk-imx8qxp.c b/drivers/clk/imx/clk-imx8qxp.c index 83e2ef96d81d..5e2903efc488 100644 --- a/drivers/clk/imx/clk-imx8qxp.c +++ b/drivers/clk/imx/clk-imx8qxp.c @@ -138,6 +138,7 @@ static int imx8qxp_clk_probe(struct platform_device *pdev) } static const struct of_device_id imx8qxp_match[] = { + { .compatible = "fsl,scu-clk", }, { .compatible = "fsl,imx8qxp-clk", }, { /* sentinel */ } }; diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c new file mode 100644 index 000000000000..1acfa3e3cfb4 --- /dev/null +++ b/drivers/clk/imx/clk-pll14xx.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP. + */ + +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> +#include <linux/jiffies.h> + +#include "clk.h" + +#define GNRL_CTL 0x0 +#define DIV_CTL 0x4 +#define LOCK_STATUS BIT(31) +#define LOCK_SEL_MASK BIT(29) +#define CLKE_MASK BIT(11) +#define RST_MASK BIT(9) +#define BYPASS_MASK BIT(4) +#define MDIV_SHIFT 12 +#define MDIV_MASK GENMASK(21, 12) +#define PDIV_SHIFT 4 +#define PDIV_MASK GENMASK(9, 4) +#define SDIV_SHIFT 0 +#define SDIV_MASK GENMASK(2, 0) +#define KDIV_SHIFT 0 +#define KDIV_MASK GENMASK(15, 0) + +#define LOCK_TIMEOUT_US 10000 + +struct clk_pll14xx { + struct clk_hw hw; + void __iomem *base; + enum imx_pll14xx_type type; + const struct imx_pll14xx_rate_table *rate_table; + int rate_count; +}; + +#define to_clk_pll14xx(_hw) container_of(_hw, struct clk_pll14xx, hw) + +static const struct imx_pll14xx_rate_table *imx_get_pll_settings( + struct clk_pll14xx *pll, unsigned long rate) +{ + const struct imx_pll14xx_rate_table *rate_table = pll->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) + if (rate == rate_table[i].rate) + return &rate_table[i]; + + return NULL; +} + +static long clk_pll14xx_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + const struct imx_pll14xx_rate_table *rate_table = pll->rate_table; + int i; + + /* Assumming rate_table is in descending order */ + for (i = 0; i < pll->rate_count; i++) + if (rate >= rate_table[i].rate) + return rate_table[i].rate; + + /* return minimum supported value */ + return rate_table[i - 1].rate; +} + +static unsigned long clk_pll1416x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 mdiv, pdiv, sdiv, pll_gnrl, pll_div; + u64 fvco = parent_rate; + + pll_gnrl = readl_relaxed(pll->base); + pll_div = readl_relaxed(pll->base + 4); + mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT; + pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT; + sdiv = (pll_div & SDIV_MASK) >> SDIV_SHIFT; + + fvco *= mdiv; + do_div(fvco, pdiv << sdiv); + + return fvco; +} + +static unsigned long clk_pll1443x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 mdiv, pdiv, sdiv, pll_gnrl, pll_div_ctl0, pll_div_ctl1; + short int kdiv; + u64 fvco = parent_rate; + + pll_gnrl = readl_relaxed(pll->base); + pll_div_ctl0 = readl_relaxed(pll->base + 4); + pll_div_ctl1 = readl_relaxed(pll->base + 8); + mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT; + pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT; + sdiv = (pll_div_ctl0 & SDIV_MASK) >> SDIV_SHIFT; + kdiv = pll_div_ctl1 & KDIV_MASK; + + /* fvco = (m * 65536 + k) * Fin / (p * 65536) */ + fvco *= (mdiv * 65536 + kdiv); + pdiv *= 65536; + + do_div(fvco, pdiv << sdiv); + + return fvco; +} + +static inline bool clk_pll1416x_mp_change(const struct imx_pll14xx_rate_table *rate, + u32 pll_div) +{ + u32 old_mdiv, old_pdiv; + + old_mdiv = (pll_div >> MDIV_SHIFT) & MDIV_MASK; + old_pdiv = (pll_div >> PDIV_SHIFT) & PDIV_MASK; + + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv; +} + +static inline bool clk_pll1443x_mpk_change(const struct imx_pll14xx_rate_table *rate, + u32 pll_div_ctl0, u32 pll_div_ctl1) +{ + u32 old_mdiv, old_pdiv, old_kdiv; + + old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK; + old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK; + old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK; + + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || + rate->kdiv != old_kdiv; +} + +static inline bool clk_pll1443x_mp_change(const struct imx_pll14xx_rate_table *rate, + u32 pll_div_ctl0, u32 pll_div_ctl1) +{ + u32 old_mdiv, old_pdiv, old_kdiv; + + old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK; + old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK; + old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK; + + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || + rate->kdiv != old_kdiv; +} + +static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll) +{ + u32 val; + + return readl_poll_timeout(pll->base, val, val & LOCK_TIMEOUT_US, 0, + LOCK_TIMEOUT_US); +} + +static int clk_pll1416x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + const struct imx_pll14xx_rate_table *rate; + u32 tmp, div_val; + int ret; + + rate = imx_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk_hw_get_name(hw)); + return -EINVAL; + } + + tmp = readl_relaxed(pll->base + 4); + + if (!clk_pll1416x_mp_change(rate, tmp)) { + tmp &= ~(SDIV_MASK) << SDIV_SHIFT; + tmp |= rate->sdiv << SDIV_SHIFT; + writel_relaxed(tmp, pll->base + 4); + + return 0; + } + + /* Bypass clock and set lock to pll output lock */ + tmp = readl_relaxed(pll->base); + tmp |= LOCK_SEL_MASK; + writel_relaxed(tmp, pll->base); + + /* Enable RST */ + tmp &= ~RST_MASK; + writel_relaxed(tmp, pll->base); + + div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) | + (rate->sdiv << SDIV_SHIFT); + writel_relaxed(div_val, pll->base + 0x4); + + /* + * According to SPEC, t3 - t2 need to be greater than + * 1us and 1/FREF, respectively. + * FREF is FIN / Prediv, the prediv is [1, 63], so choose + * 3us. + */ + udelay(3); + + /* Disable RST */ + tmp |= RST_MASK; + writel_relaxed(tmp, pll->base); + + /* Wait Lock */ + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + /* Bypass */ + tmp &= ~BYPASS_MASK; + writel_relaxed(tmp, pll->base); + + return 0; +} + +static int clk_pll1443x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + const struct imx_pll14xx_rate_table *rate; + u32 tmp, div_val; + int ret; + + rate = imx_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk_hw_get_name(hw)); + return -EINVAL; + } + + tmp = readl_relaxed(pll->base + 4); + div_val = readl_relaxed(pll->base + 8); + + if (!clk_pll1443x_mpk_change(rate, tmp, div_val)) { + tmp &= ~(SDIV_MASK) << SDIV_SHIFT; + tmp |= rate->sdiv << SDIV_SHIFT; + writel_relaxed(tmp, pll->base + 4); + + return 0; + } + + /* Enable RST */ + tmp = readl_relaxed(pll->base); + tmp &= ~RST_MASK; + writel_relaxed(tmp, pll->base); + + div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) | + (rate->sdiv << SDIV_SHIFT); + writel_relaxed(div_val, pll->base + 0x4); + writel_relaxed(rate->kdiv << KDIV_SHIFT, pll->base + 0x8); + + /* + * According to SPEC, t3 - t2 need to be greater than + * 1us and 1/FREF, respectively. + * FREF is FIN / Prediv, the prediv is [1, 63], so choose + * 3us. + */ + udelay(3); + + /* Disable RST */ + tmp |= RST_MASK; + writel_relaxed(tmp, pll->base); + + /* Wait Lock*/ + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + /* Bypass */ + tmp &= ~BYPASS_MASK; + writel_relaxed(tmp, pll->base); + + return 0; +} + +static int clk_pll14xx_prepare(struct clk_hw *hw) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 val; + + /* + * RESETB = 1 from 0, PLL starts its normal + * operation after lock time + */ + val = readl_relaxed(pll->base + GNRL_CTL); + val |= RST_MASK; + writel_relaxed(val, pll->base + GNRL_CTL); + + return clk_pll14xx_wait_lock(pll); +} + +static int clk_pll14xx_is_prepared(struct clk_hw *hw) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 val; + + val = readl_relaxed(pll->base + GNRL_CTL); + + return (val & RST_MASK) ? 1 : 0; +} + +static void clk_pll14xx_unprepare(struct clk_hw *hw) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 val; + + /* + * Set RST to 0, power down mode is enabled and + * every digital block is reset + */ + val = readl_relaxed(pll->base + GNRL_CTL); + val &= ~RST_MASK; + writel_relaxed(val, pll->base + GNRL_CTL); +} + +static const struct clk_ops clk_pll1416x_ops = { + .prepare = clk_pll14xx_prepare, + .unprepare = clk_pll14xx_unprepare, + .is_prepared = clk_pll14xx_is_prepared, + .recalc_rate = clk_pll1416x_recalc_rate, + .round_rate = clk_pll14xx_round_rate, + .set_rate = clk_pll1416x_set_rate, +}; + +static const struct clk_ops clk_pll1416x_min_ops = { + .recalc_rate = clk_pll1416x_recalc_rate, +}; + +static const struct clk_ops clk_pll1443x_ops = { + .prepare = clk_pll14xx_prepare, + .unprepare = clk_pll14xx_unprepare, + .is_prepared = clk_pll14xx_is_prepared, + .recalc_rate = clk_pll1443x_recalc_rate, + .round_rate = clk_pll14xx_round_rate, + .set_rate = clk_pll1443x_set_rate, +}; + +struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_pll14xx_clk *pll_clk) +{ + struct clk_pll14xx *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = pll_clk->flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + switch (pll_clk->type) { + case PLL_1416X: + if (!pll->rate_table) + init.ops = &clk_pll1416x_min_ops; + else + init.ops = &clk_pll1416x_ops; + break; + case PLL_1443X: + init.ops = &clk_pll1443x_ops; + break; + default: + pr_err("%s: Unknown pll type for pll clk %s\n", + __func__, name); + }; + + pll->base = base; + pll->hw.init = &init; + pll->type = pll_clk->type; + pll->rate_table = pll_clk->rate_table; + pll->rate_count = pll_clk->rate_count; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll %s %lu\n", + __func__, name, PTR_ERR(clk)); + kfree(pll); + } + + return clk; +} diff --git a/drivers/clk/imx/clk-sccg-pll.c b/drivers/clk/imx/clk-sccg-pll.c index ee7752bace89..9dfd03a95557 100644 --- a/drivers/clk/imx/clk-sccg-pll.c +++ b/drivers/clk/imx/clk-sccg-pll.c @@ -25,87 +25,292 @@ #define PLL_DIVF2_MASK GENMASK(12, 7) #define PLL_DIVR1_MASK GENMASK(27, 25) #define PLL_DIVR2_MASK GENMASK(24, 19) +#define PLL_DIVQ_MASK GENMASK(6, 1) #define PLL_REF_MASK GENMASK(2, 0) #define PLL_LOCK_MASK BIT(31) #define PLL_PD_MASK BIT(7) -#define OSC_25M 25000000 -#define OSC_27M 27000000 +/* These are the specification limits for the SSCG PLL */ +#define PLL_REF_MIN_FREQ 25000000UL +#define PLL_REF_MAX_FREQ 235000000UL -#define PLL_SCCG_LOCK_TIMEOUT 70 +#define PLL_STAGE1_MIN_FREQ 1600000000UL +#define PLL_STAGE1_MAX_FREQ 2400000000UL + +#define PLL_STAGE1_REF_MIN_FREQ 25000000UL +#define PLL_STAGE1_REF_MAX_FREQ 54000000UL + +#define PLL_STAGE2_MIN_FREQ 1200000000UL +#define PLL_STAGE2_MAX_FREQ 2400000000UL + +#define PLL_STAGE2_REF_MIN_FREQ 54000000UL +#define PLL_STAGE2_REF_MAX_FREQ 75000000UL + +#define PLL_OUT_MIN_FREQ 20000000UL +#define PLL_OUT_MAX_FREQ 1200000000UL + +#define PLL_DIVR1_MAX 7 +#define PLL_DIVR2_MAX 63 +#define PLL_DIVF1_MAX 63 +#define PLL_DIVF2_MAX 63 +#define PLL_DIVQ_MAX 63 + +#define PLL_BYPASS_NONE 0x0 +#define PLL_BYPASS1 0x2 +#define PLL_BYPASS2 0x1 + +#define SSCG_PLL_BYPASS1_MASK BIT(5) +#define SSCG_PLL_BYPASS2_MASK BIT(4) +#define SSCG_PLL_BYPASS_MASK GENMASK(5, 4) + +#define PLL_SCCG_LOCK_TIMEOUT 70 + +struct clk_sccg_pll_setup { + int divr1, divf1; + int divr2, divf2; + int divq; + int bypass; + + uint64_t vco1; + uint64_t vco2; + uint64_t fout; + uint64_t ref; + uint64_t ref_div1; + uint64_t ref_div2; + uint64_t fout_request; + int fout_error; +}; struct clk_sccg_pll { struct clk_hw hw; - void __iomem *base; + const struct clk_ops ops; + + void __iomem *base; + + struct clk_sccg_pll_setup setup; + + u8 parent; + u8 bypass1; + u8 bypass2; }; #define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw) -static int clk_pll_wait_lock(struct clk_sccg_pll *pll) +static int clk_sccg_pll_wait_lock(struct clk_sccg_pll *pll) { u32 val; - return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, 0, - PLL_SCCG_LOCK_TIMEOUT); + val = readl_relaxed(pll->base + PLL_CFG0); + + /* don't wait for lock if all plls are bypassed */ + if (!(val & SSCG_PLL_BYPASS2_MASK)) + return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, + 0, PLL_SCCG_LOCK_TIMEOUT); + + return 0; } -static int clk_pll1_is_prepared(struct clk_hw *hw) +static int clk_sccg_pll2_check_match(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup) { - struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); - u32 val; + int new_diff = temp_setup->fout - temp_setup->fout_request; + int diff = temp_setup->fout_error; - val = readl_relaxed(pll->base + PLL_CFG0); - return (val & PLL_PD_MASK) ? 0 : 1; + if (abs(diff) > abs(new_diff)) { + temp_setup->fout_error = new_diff; + memcpy(setup, temp_setup, sizeof(struct clk_sccg_pll_setup)); + + if (temp_setup->fout_request == temp_setup->fout) + return 0; + } + return -1; } -static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) +static int clk_sccg_divq_lookup(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup) { - struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); - u32 val, divf; + int ret = -EINVAL; + + for (temp_setup->divq = 0; temp_setup->divq <= PLL_DIVQ_MAX; + temp_setup->divq++) { + temp_setup->vco2 = temp_setup->vco1; + do_div(temp_setup->vco2, temp_setup->divr2 + 1); + temp_setup->vco2 *= 2; + temp_setup->vco2 *= temp_setup->divf2 + 1; + if (temp_setup->vco2 >= PLL_STAGE2_MIN_FREQ && + temp_setup->vco2 <= PLL_STAGE2_MAX_FREQ) { + temp_setup->fout = temp_setup->vco2; + do_div(temp_setup->fout, 2 * (temp_setup->divq + 1)); + + ret = clk_sccg_pll2_check_match(setup, temp_setup); + if (!ret) { + temp_setup->bypass = PLL_BYPASS1; + return ret; + } + } + } - val = readl_relaxed(pll->base + PLL_CFG2); - divf = FIELD_GET(PLL_DIVF1_MASK, val); + return ret; +} + +static int clk_sccg_divf2_lookup(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup) +{ + int ret = -EINVAL; + + for (temp_setup->divf2 = 0; temp_setup->divf2 <= PLL_DIVF2_MAX; + temp_setup->divf2++) { + ret = clk_sccg_divq_lookup(setup, temp_setup); + if (!ret) + return ret; + } - return parent_rate * 2 * (divf + 1); + return ret; } -static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_sccg_divr2_lookup(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup) { - unsigned long parent_rate = *prate; - u32 div; + int ret = -EINVAL; + + for (temp_setup->divr2 = 0; temp_setup->divr2 <= PLL_DIVR2_MAX; + temp_setup->divr2++) { + temp_setup->ref_div2 = temp_setup->vco1; + do_div(temp_setup->ref_div2, temp_setup->divr2 + 1); + if (temp_setup->ref_div2 >= PLL_STAGE2_REF_MIN_FREQ && + temp_setup->ref_div2 <= PLL_STAGE2_REF_MAX_FREQ) { + ret = clk_sccg_divf2_lookup(setup, temp_setup); + if (!ret) + return ret; + } + } + + return ret; +} + +static int clk_sccg_pll2_find_setup(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup, + uint64_t ref) +{ + + int ret = -EINVAL; - if (!parent_rate) - return 0; + if (ref < PLL_STAGE1_MIN_FREQ || ref > PLL_STAGE1_MAX_FREQ) + return ret; - div = rate / (parent_rate * 2); + temp_setup->vco1 = ref; - return parent_rate * div * 2; + ret = clk_sccg_divr2_lookup(setup, temp_setup); + return ret; } -static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +static int clk_sccg_divf1_lookup(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup) { - struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); - u32 val; - u32 divf; + int ret = -EINVAL; - if (!parent_rate) - return -EINVAL; + for (temp_setup->divf1 = 0; temp_setup->divf1 <= PLL_DIVF1_MAX; + temp_setup->divf1++) { + uint64_t vco1 = temp_setup->ref; - divf = rate / (parent_rate * 2); + do_div(vco1, temp_setup->divr1 + 1); + vco1 *= 2; + vco1 *= temp_setup->divf1 + 1; - val = readl_relaxed(pll->base + PLL_CFG2); - val &= ~PLL_DIVF1_MASK; - val |= FIELD_PREP(PLL_DIVF1_MASK, divf - 1); - writel_relaxed(val, pll->base + PLL_CFG2); + ret = clk_sccg_pll2_find_setup(setup, temp_setup, vco1); + if (!ret) { + temp_setup->bypass = PLL_BYPASS_NONE; + return ret; + } + } + + return ret; +} + +static int clk_sccg_divr1_lookup(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup) +{ + int ret = -EINVAL; + + for (temp_setup->divr1 = 0; temp_setup->divr1 <= PLL_DIVR1_MAX; + temp_setup->divr1++) { + temp_setup->ref_div1 = temp_setup->ref; + do_div(temp_setup->ref_div1, temp_setup->divr1 + 1); + if (temp_setup->ref_div1 >= PLL_STAGE1_REF_MIN_FREQ && + temp_setup->ref_div1 <= PLL_STAGE1_REF_MAX_FREQ) { + ret = clk_sccg_divf1_lookup(setup, temp_setup); + if (!ret) + return ret; + } + } + + return ret; +} + +static int clk_sccg_pll1_find_setup(struct clk_sccg_pll_setup *setup, + struct clk_sccg_pll_setup *temp_setup, + uint64_t ref) +{ + + int ret = -EINVAL; + + if (ref < PLL_REF_MIN_FREQ || ref > PLL_REF_MAX_FREQ) + return ret; + + temp_setup->ref = ref; + + ret = clk_sccg_divr1_lookup(setup, temp_setup); + + return ret; +} + +static int clk_sccg_pll_find_setup(struct clk_sccg_pll_setup *setup, + uint64_t prate, + uint64_t rate, int try_bypass) +{ + struct clk_sccg_pll_setup temp_setup; + int ret = -EINVAL; + + memset(&temp_setup, 0, sizeof(struct clk_sccg_pll_setup)); + memset(setup, 0, sizeof(struct clk_sccg_pll_setup)); + + temp_setup.fout_error = PLL_OUT_MAX_FREQ; + temp_setup.fout_request = rate; + + switch (try_bypass) { - return clk_pll_wait_lock(pll); + case PLL_BYPASS2: + if (prate == rate) { + setup->bypass = PLL_BYPASS2; + setup->fout = rate; + ret = 0; + } + break; + + case PLL_BYPASS1: + ret = clk_sccg_pll2_find_setup(setup, &temp_setup, prate); + break; + + case PLL_BYPASS_NONE: + ret = clk_sccg_pll1_find_setup(setup, &temp_setup, prate); + break; + } + + return ret; +} + + +static int clk_sccg_pll_is_prepared(struct clk_hw *hw) +{ + struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + + u32 val = readl_relaxed(pll->base + PLL_CFG0); + + return (val & PLL_PD_MASK) ? 0 : 1; } -static int clk_pll1_prepare(struct clk_hw *hw) +static int clk_sccg_pll_prepare(struct clk_hw *hw) { struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); u32 val; @@ -114,10 +319,10 @@ static int clk_pll1_prepare(struct clk_hw *hw) val &= ~PLL_PD_MASK; writel_relaxed(val, pll->base + PLL_CFG0); - return clk_pll_wait_lock(pll); + return clk_sccg_pll_wait_lock(pll); } -static void clk_pll1_unprepare(struct clk_hw *hw) +static void clk_sccg_pll_unprepare(struct clk_hw *hw) { struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); u32 val; @@ -125,121 +330,208 @@ static void clk_pll1_unprepare(struct clk_hw *hw) val = readl_relaxed(pll->base + PLL_CFG0); val |= PLL_PD_MASK; writel_relaxed(val, pll->base + PLL_CFG0); - } -static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw, +static unsigned long clk_sccg_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); - u32 val, ref, divr1, divf1, divr2, divf2; + u32 val, divr1, divf1, divr2, divf2, divq; u64 temp64; - val = readl_relaxed(pll->base + PLL_CFG0); - switch (FIELD_GET(PLL_REF_MASK, val)) { - case 0: - ref = OSC_25M; - break; - case 1: - ref = OSC_27M; - break; - default: - ref = OSC_25M; - break; - } - val = readl_relaxed(pll->base + PLL_CFG2); divr1 = FIELD_GET(PLL_DIVR1_MASK, val); divr2 = FIELD_GET(PLL_DIVR2_MASK, val); divf1 = FIELD_GET(PLL_DIVF1_MASK, val); divf2 = FIELD_GET(PLL_DIVF2_MASK, val); - - temp64 = ref * 2; - temp64 *= (divf1 + 1) * (divf2 + 1); - - do_div(temp64, (divr1 + 1) * (divr2 + 1)); + divq = FIELD_GET(PLL_DIVQ_MASK, val); + + temp64 = parent_rate; + + val = clk_readl(pll->base + PLL_CFG0); + if (val & SSCG_PLL_BYPASS2_MASK) { + temp64 = parent_rate; + } else if (val & SSCG_PLL_BYPASS1_MASK) { + temp64 *= divf2; + do_div(temp64, (divr2 + 1) * (divq + 1)); + } else { + temp64 *= 2; + temp64 *= (divf1 + 1) * (divf2 + 1); + do_div(temp64, (divr1 + 1) * (divr2 + 1) * (divq + 1)); + } return temp64; } -static long clk_pll2_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_sccg_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { - u32 div; - unsigned long parent_rate = *prate; + struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + struct clk_sccg_pll_setup *setup = &pll->setup; + u32 val; - if (!parent_rate) - return 0; + /* set bypass here too since the parent might be the same */ + val = clk_readl(pll->base + PLL_CFG0); + val &= ~SSCG_PLL_BYPASS_MASK; + val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, setup->bypass); + clk_writel(val, pll->base + PLL_CFG0); - div = rate / parent_rate; + val = readl_relaxed(pll->base + PLL_CFG2); + val &= ~(PLL_DIVF1_MASK | PLL_DIVF2_MASK); + val &= ~(PLL_DIVR1_MASK | PLL_DIVR2_MASK | PLL_DIVQ_MASK); + val |= FIELD_PREP(PLL_DIVF1_MASK, setup->divf1); + val |= FIELD_PREP(PLL_DIVF2_MASK, setup->divf2); + val |= FIELD_PREP(PLL_DIVR1_MASK, setup->divr1); + val |= FIELD_PREP(PLL_DIVR2_MASK, setup->divr2); + val |= FIELD_PREP(PLL_DIVQ_MASK, setup->divq); + writel_relaxed(val, pll->base + PLL_CFG2); - return parent_rate * div; + return clk_sccg_pll_wait_lock(pll); } -static int clk_pll2_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +static u8 clk_sccg_pll_get_parent(struct clk_hw *hw) { + struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); u32 val; - u32 divf; + u8 ret = pll->parent; + + val = clk_readl(pll->base + PLL_CFG0); + if (val & SSCG_PLL_BYPASS2_MASK) + ret = pll->bypass2; + else if (val & SSCG_PLL_BYPASS1_MASK) + ret = pll->bypass1; + return ret; +} + +static int clk_sccg_pll_set_parent(struct clk_hw *hw, u8 index) +{ struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + u32 val; - if (!parent_rate) - return -EINVAL; + val = clk_readl(pll->base + PLL_CFG0); + val &= ~SSCG_PLL_BYPASS_MASK; + val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, pll->setup.bypass); + clk_writel(val, pll->base + PLL_CFG0); - divf = rate / parent_rate; + return clk_sccg_pll_wait_lock(pll); +} - val = readl_relaxed(pll->base + PLL_CFG2); - val &= ~PLL_DIVF2_MASK; - val |= FIELD_PREP(PLL_DIVF2_MASK, divf - 1); - writel_relaxed(val, pll->base + PLL_CFG2); +static int __clk_sccg_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req, + uint64_t min, + uint64_t max, + uint64_t rate, + int bypass) +{ + struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + struct clk_sccg_pll_setup *setup = &pll->setup; + struct clk_hw *parent_hw = NULL; + int bypass_parent_index; + int ret = -EINVAL; + + req->max_rate = max; + req->min_rate = min; + + switch (bypass) { + case PLL_BYPASS2: + bypass_parent_index = pll->bypass2; + break; + case PLL_BYPASS1: + bypass_parent_index = pll->bypass1; + break; + default: + bypass_parent_index = pll->parent; + break; + } + + parent_hw = clk_hw_get_parent_by_index(hw, bypass_parent_index); + ret = __clk_determine_rate(parent_hw, req); + if (!ret) { + ret = clk_sccg_pll_find_setup(setup, req->rate, + rate, bypass); + } + + req->best_parent_hw = parent_hw; + req->best_parent_rate = req->rate; + req->rate = setup->fout; - return clk_pll_wait_lock(pll); + return ret; } -static const struct clk_ops clk_sccg_pll1_ops = { - .is_prepared = clk_pll1_is_prepared, - .recalc_rate = clk_pll1_recalc_rate, - .round_rate = clk_pll1_round_rate, - .set_rate = clk_pll1_set_rate, -}; +static int clk_sccg_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); + struct clk_sccg_pll_setup *setup = &pll->setup; + uint64_t rate = req->rate; + uint64_t min = req->min_rate; + uint64_t max = req->max_rate; + int ret = -EINVAL; + + if (rate < PLL_OUT_MIN_FREQ || rate > PLL_OUT_MAX_FREQ) + return ret; + + ret = __clk_sccg_pll_determine_rate(hw, req, req->rate, req->rate, + rate, PLL_BYPASS2); + if (!ret) + return ret; + + ret = __clk_sccg_pll_determine_rate(hw, req, PLL_STAGE1_REF_MIN_FREQ, + PLL_STAGE1_REF_MAX_FREQ, rate, + PLL_BYPASS1); + if (!ret) + return ret; + + ret = __clk_sccg_pll_determine_rate(hw, req, PLL_REF_MIN_FREQ, + PLL_REF_MAX_FREQ, rate, + PLL_BYPASS_NONE); + if (!ret) + return ret; + + if (setup->fout >= min && setup->fout <= max) + ret = 0; + + return ret; +} -static const struct clk_ops clk_sccg_pll2_ops = { - .prepare = clk_pll1_prepare, - .unprepare = clk_pll1_unprepare, - .recalc_rate = clk_pll2_recalc_rate, - .round_rate = clk_pll2_round_rate, - .set_rate = clk_pll2_set_rate, +static const struct clk_ops clk_sccg_pll_ops = { + .prepare = clk_sccg_pll_prepare, + .unprepare = clk_sccg_pll_unprepare, + .is_prepared = clk_sccg_pll_is_prepared, + .recalc_rate = clk_sccg_pll_recalc_rate, + .set_rate = clk_sccg_pll_set_rate, + .set_parent = clk_sccg_pll_set_parent, + .get_parent = clk_sccg_pll_get_parent, + .determine_rate = clk_sccg_pll_determine_rate, }; struct clk *imx_clk_sccg_pll(const char *name, - const char *parent_name, + const char * const *parent_names, + u8 num_parents, + u8 parent, u8 bypass1, u8 bypass2, void __iomem *base, - enum imx_sccg_pll_type pll_type) + unsigned long flags) { struct clk_sccg_pll *pll; struct clk_init_data init; struct clk_hw *hw; int ret; - switch (pll_type) { - case SCCG_PLL1: - init.ops = &clk_sccg_pll1_ops; - break; - case SCCG_PLL2: - init.ops = &clk_sccg_pll2_ops; - break; - default: - return ERR_PTR(-EINVAL); - } - pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) return ERR_PTR(-ENOMEM); + pll->parent = parent; + pll->bypass1 = bypass1; + pll->bypass2 = bypass2; + + pll->base = base; init.name = name; - init.flags = 0; - init.parent_names = &parent_name; - init.num_parents = 1; + init.ops = &clk_sccg_pll_ops; + + init.flags = flags; + init.parent_names = parent_names; + init.num_parents = num_parents; pll->base = base; pll->hw.init = &init; diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index 7ccf7edfe11c..fbef740704d0 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -4,12 +4,17 @@ * Dong Aisheng <aisheng.dong@nxp.com> */ +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/arm-smccc.h> #include <linux/clk-provider.h> #include <linux/err.h> #include <linux/slab.h> #include "clk-scu.h" +#define IMX_SIP_CPUFREQ 0xC2000001 +#define IMX_SIP_SET_CPUFREQ 0x00 + static struct imx_sc_ipc *ccm_ipc_handle; /* @@ -66,6 +71,41 @@ struct imx_sc_msg_get_clock_rate { }; /* + * struct imx_sc_msg_get_clock_parent - clock get parent protocol + * @hdr: SCU protocol header + * @req: get parent request protocol + * @resp: get parent response protocol + * + * This structure describes the SCU protocol of clock get parent + */ +struct imx_sc_msg_get_clock_parent { + struct imx_sc_rpc_msg hdr; + union { + struct req_get_clock_parent { + __le16 resource; + u8 clk; + } __packed req; + struct resp_get_clock_parent { + u8 parent; + } resp; + } data; +}; + +/* + * struct imx_sc_msg_set_clock_parent - clock set parent protocol + * @hdr: SCU protocol header + * @req: set parent request protocol + * + * This structure describes the SCU protocol of clock set parent + */ +struct imx_sc_msg_set_clock_parent { + struct imx_sc_rpc_msg hdr; + __le16 resource; + u8 clk; + u8 parent; +} __packed; + +/* * struct imx_sc_msg_req_clock_enable - clock gate protocol * @hdr: SCU protocol header * @resource: clock resource to gate @@ -145,6 +185,25 @@ static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate, return rate; } +static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct arm_smccc_res res; + unsigned long cluster_id; + + if (clk->rsrc_id == IMX_SC_R_A35) + cluster_id = 0; + else + return -EINVAL; + + /* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */ + arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ, + cluster_id, rate, 0, 0, 0, 0, &res); + + return 0; +} + /* * clk_scu_set_rate - Set rate for a SCU clock * @hw: clock to change rate for @@ -173,6 +232,49 @@ static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate, return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); } +static u8 clk_scu_get_parent(struct clk_hw *hw) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_get_clock_parent msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT; + hdr->size = 2; + + msg.data.req.resource = cpu_to_le16(clk->rsrc_id); + msg.data.req.clk = clk->clk_type; + + ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); + if (ret) { + pr_err("%s: failed to get clock parent %d\n", + clk_hw_get_name(hw), ret); + return 0; + } + + return msg.data.resp.parent; +} + +static int clk_scu_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_set_clock_parent msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT; + hdr->size = 2; + + msg.resource = cpu_to_le16(clk->rsrc_id); + msg.clk = clk->clk_type; + msg.parent = index; + + return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); +} + static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource, u8 clk, bool enable, bool autog) { @@ -228,11 +330,22 @@ static const struct clk_ops clk_scu_ops = { .recalc_rate = clk_scu_recalc_rate, .round_rate = clk_scu_round_rate, .set_rate = clk_scu_set_rate, + .get_parent = clk_scu_get_parent, + .set_parent = clk_scu_set_parent, + .prepare = clk_scu_prepare, + .unprepare = clk_scu_unprepare, +}; + +static const struct clk_ops clk_scu_cpu_ops = { + .recalc_rate = clk_scu_recalc_rate, + .round_rate = clk_scu_round_rate, + .set_rate = clk_scu_atf_set_cpu_rate, .prepare = clk_scu_prepare, .unprepare = clk_scu_unprepare, }; -struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type) +struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents, + int num_parents, u32 rsrc_id, u8 clk_type) { struct clk_init_data init; struct clk_scu *clk; @@ -248,7 +361,13 @@ struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type) init.name = name; init.ops = &clk_scu_ops; - init.num_parents = 0; + if (rsrc_id == IMX_SC_R_A35) + init.ops = &clk_scu_cpu_ops; + else + init.ops = &clk_scu_ops; + init.parent_names = parents; + init.num_parents = num_parents; + /* * Note on MX8, the clocks are tightly coupled with power domain * that once the power domain is off, the clock status may be diff --git a/drivers/clk/imx/clk-scu.h b/drivers/clk/imx/clk-scu.h index 52c1746ec988..2bcfaf06a458 100644 --- a/drivers/clk/imx/clk-scu.h +++ b/drivers/clk/imx/clk-scu.h @@ -10,7 +10,21 @@ #include <linux/firmware/imx/sci.h> int imx_clk_scu_init(void); -struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type); + +struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents, + int num_parents, u32 rsrc_id, u8 clk_type); + +static inline struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, + u8 clk_type) +{ + return __imx_clk_scu(name, NULL, 0, rsrc_id, clk_type); +} + +static inline struct clk_hw *imx_clk_scu2(const char *name, const char * const *parents, + int num_parents, u32 rsrc_id, u8 clk_type) +{ + return __imx_clk_scu(name, parents, num_parents, rsrc_id, clk_type); +} struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 6dae54325a91..a334667c450a 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -203,6 +203,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop"); anatop_base = of_iomap(np, 0); BUG_ON(!anatop_base); + of_node_put(np); np = ccm_node; ccm_base = of_iomap(np, 0); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 028312de21b8..5748ec8673e4 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -27,6 +27,30 @@ enum imx_sccg_pll_type { SCCG_PLL2, }; +enum imx_pll14xx_type { + PLL_1416X, + PLL_1443X, +}; + +/* NOTE: Rate table should be kept sorted in descending order. */ +struct imx_pll14xx_rate_table { + unsigned int rate; + unsigned int pdiv; + unsigned int mdiv; + unsigned int sdiv; + unsigned int kdiv; +}; + +struct imx_pll14xx_clk { + enum imx_pll14xx_type type; + const struct imx_pll14xx_rate_table *rate_table; + int rate_count; + int flags; +}; + +struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, + void __iomem *base, const struct imx_pll14xx_clk *pll_clk); + struct clk *imx_clk_pllv1(enum imx_pllv1_type type, const char *name, const char *parent, void __iomem *base); @@ -36,9 +60,12 @@ struct clk *imx_clk_pllv2(const char *name, const char *parent, struct clk *imx_clk_frac_pll(const char *name, const char *parent_name, void __iomem *base); -struct clk *imx_clk_sccg_pll(const char *name, const char *parent_name, - void __iomem *base, - enum imx_sccg_pll_type pll_type); +struct clk *imx_clk_sccg_pll(const char *name, + const char * const *parent_names, + u8 num_parents, + u8 parent, u8 bypass1, u8 bypass2, + void __iomem *base, + unsigned long flags); enum imx_pllv3_type { IMX_PLLV3_GENERIC, @@ -329,7 +356,8 @@ static inline struct clk *imx_clk_mux_flags(const char *name, } static inline struct clk *imx_clk_mux2_flags(const char *name, - void __iomem *reg, u8 shift, u8 width, const char **parents, + void __iomem *reg, u8 shift, u8 width, + const char * const *parents, int num_parents, unsigned long flags) { return clk_register_mux(NULL, name, parents, num_parents, @@ -354,7 +382,7 @@ struct clk *imx_clk_cpu(const char *name, const char *parent_name, struct clk *step); struct clk *imx8m_clk_composite_flags(const char *name, - const char **parent_names, + const char * const *parent_names, int num_parents, void __iomem *reg, unsigned long flags); diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c index 5ef7d9ba2195..510b685212d3 100644 --- a/drivers/clk/ingenic/cgu.c +++ b/drivers/clk/ingenic/cgu.c @@ -83,7 +83,7 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) const struct ingenic_cgu_clk_info *clk_info; const struct ingenic_cgu_pll_info *pll_info; unsigned m, n, od_enc, od; - bool bypass, enable; + bool bypass; unsigned long flags; u32 ctl; @@ -103,7 +103,6 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) od_enc &= GENMASK(pll_info->od_bits - 1, 0); bypass = !pll_info->no_bypass_bit && !!(ctl & BIT(pll_info->bypass_bit)); - enable = !!(ctl & BIT(pll_info->enable_bit)); if (bypass) return parent_rate; @@ -426,16 +425,16 @@ ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate, struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); struct ingenic_cgu *cgu = ingenic_clk->cgu; const struct ingenic_cgu_clk_info *clk_info; - long rate = *parent_rate; + unsigned int div = 1; clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_DIV) - rate /= ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); + div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); else if (clk_info->type & CGU_CLK_FIXDIV) - rate /= clk_info->fixdiv.div; + div = clk_info->fixdiv.div; - return rate; + return DIV_ROUND_UP(*parent_rate, div); } static int @@ -455,7 +454,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, if (clk_info->type & CGU_CLK_DIV) { div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate); - rate = parent_rate / div; + rate = DIV_ROUND_UP(parent_rate, div); if (rate != req_rate) return -EINVAL; diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h index 502bcbb61b04..e12716d8ce3c 100644 --- a/drivers/clk/ingenic/cgu.h +++ b/drivers/clk/ingenic/cgu.h @@ -80,7 +80,7 @@ struct ingenic_cgu_mux_info { * @reg: offset of the divider control register within the CGU * @shift: number of bits to left shift the divide value by (ie. the index of * the lowest bit of the divide value within its control register) - * @div: number of bits to divide the divider value by (i.e. if the + * @div: number to divide the divider value by (i.e. if the * effective divider value is the value written to the register * multiplied by some constant) * @bits: the size of the divide value in bits diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c index 4479c102e899..b86edd328249 100644 --- a/drivers/clk/ingenic/jz4740-cgu.c +++ b/drivers/clk/ingenic/jz4740-cgu.c @@ -165,7 +165,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, .mux = { CGU_REG_CPCCR, 29, 1 }, .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, - .gate = { CGU_REG_SCR, 6 }, + .gate = { CGU_REG_SCR, 6, true }, }, /* Gate-only clocks */ diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c index 934bf0e45e26..9628d4e7690b 100644 --- a/drivers/clk/mediatek/clk-gate.c +++ b/drivers/clk/mediatek/clk-gate.c @@ -157,7 +157,8 @@ struct clk *mtk_clk_register_gate( int clr_ofs, int sta_ofs, u8 bit, - const struct clk_ops *ops) + const struct clk_ops *ops, + unsigned long flags) { struct mtk_clk_gate *cg; struct clk *clk; @@ -172,6 +173,7 @@ struct clk *mtk_clk_register_gate( init.parent_names = parent_name ? &parent_name : NULL; init.num_parents = parent_name ? 1 : 0; init.ops = ops; + init.flags = flags; cg->regmap = regmap; cg->set_ofs = set_ofs; diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h index 72ef89b3ad7b..9f766dfe1d57 100644 --- a/drivers/clk/mediatek/clk-gate.h +++ b/drivers/clk/mediatek/clk-gate.h @@ -47,6 +47,7 @@ struct clk *mtk_clk_register_gate( int clr_ofs, int sta_ofs, u8 bit, - const struct clk_ops *ops); + const struct clk_ops *ops, + unsigned long flags); #endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c index ab6ab07f53e6..905a2316f6a7 100644 --- a/drivers/clk/mediatek/clk-mt2701.c +++ b/drivers/clk/mediatek/clk-mt2701.c @@ -535,8 +535,8 @@ static const struct mtk_composite top_muxes[] = { 0x0080, 8, 2, 15), MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x0080, 16, 3, 23), - MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, - 0x0080, 24, 2, 31), + MUX_GATE_FLAGS_2(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, + 0x0080, 24, 2, 31, 0, CLK_MUX_ROUND_CLOSEST), MUX_GATE(CLK_TOP_TVE_SEL, "tve_sel", tve_parents, 0x0090, 0, 3, 7), diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c index 991d4093726e..b09cb3d99f66 100644 --- a/drivers/clk/mediatek/clk-mt2712.c +++ b/drivers/clk/mediatek/clk-mt2712.c @@ -223,6 +223,8 @@ static const struct mtk_fixed_factor top_divs[] = { 4), FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1_ck", 1, 3), + FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2_ck", 1, + 3), }; static const char * const axi_parents[] = { @@ -594,7 +596,8 @@ static const char * const a1sys_hp_parents[] = { "apll1_ck", "apll1_d2", "apll1_d4", - "apll1_d8" + "apll1_d8", + "apll1_d3" }; static const char * const a2sys_hp_parents[] = { @@ -602,7 +605,8 @@ static const char * const a2sys_hp_parents[] = { "apll2_ck", "apll2_d2", "apll2_d4", - "apll2_d8" + "apll2_d8", + "apll2_d3" }; static const char * const asm_l_parents[] = { @@ -1463,7 +1467,6 @@ static struct platform_driver clk_mt2712_drv = { .probe = clk_mt2712_probe, .driver = { .name = "clk-mt2712", - .owner = THIS_MODULE, .of_match_table = of_match_clk_mt2712, }, }; diff --git a/drivers/clk/mediatek/clk-mt6797.c b/drivers/clk/mediatek/clk-mt6797.c index 5702bc974ed9..c2b46b184b9a 100644 --- a/drivers/clk/mediatek/clk-mt6797.c +++ b/drivers/clk/mediatek/clk-mt6797.c @@ -324,6 +324,10 @@ static const char * const anc_md32_parents[] = { "univpll_d5", }; +/* + * Clock mux ddrphycfg is needed by the DRAM controller. We mark it as + * critical as otherwise the system will hang after boot. + */ static const struct mtk_composite top_muxes[] = { MUX(CLK_TOP_MUX_ULPOSC_AXI_CK_MUX_PRE, "ulposc_axi_ck_mux_pre", ulposc_axi_ck_mux_pre_parents, 0x0040, 3, 1), @@ -331,8 +335,8 @@ static const struct mtk_composite top_muxes[] = { ulposc_axi_ck_mux_parents, 0x0040, 2, 1), MUX(CLK_TOP_MUX_AXI, "axi_sel", axi_parents, 0x0040, 0, 2), - MUX(CLK_TOP_MUX_DDRPHYCFG, "ddrphycfg_sel", ddrphycfg_parents, - 0x0040, 16, 2), + MUX_FLAGS(CLK_TOP_MUX_DDRPHYCFG, "ddrphycfg_sel", ddrphycfg_parents, + 0x0040, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), MUX(CLK_TOP_MUX_MM, "mm_sel", mm_parents, 0x0040, 24, 2), MUX_GATE(CLK_TOP_MUX_PWM, "pwm_sel", pwm_parents, 0x0050, 0, 3, 7), @@ -424,33 +428,45 @@ static const struct mtk_gate_regs infra2_cg_regs = { .sta_ofs = 0x00b0, }; -#define GATE_ICG0(_id, _name, _parent, _shift) { \ - .id = _id, \ - .name = _name, \ - .parent_name = _parent, \ - .regs = &infra0_cg_regs, \ - .shift = _shift, \ - .ops = &mtk_clk_gate_ops_setclr, \ +#define GATE_ICG0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ } -#define GATE_ICG1(_id, _name, _parent, _shift) { \ - .id = _id, \ - .name = _name, \ - .parent_name = _parent, \ - .regs = &infra1_cg_regs, \ - .shift = _shift, \ - .ops = &mtk_clk_gate_ops_setclr, \ +#define GATE_ICG1(_id, _name, _parent, _shift) \ + GATE_ICG1_FLAGS(_id, _name, _parent, _shift, 0) + +#define GATE_ICG1_FLAGS(_id, _name, _parent, _shift, _flags) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + .flags = _flags, \ } -#define GATE_ICG2(_id, _name, _parent, _shift) { \ - .id = _id, \ - .name = _name, \ - .parent_name = _parent, \ - .regs = &infra2_cg_regs, \ - .shift = _shift, \ - .ops = &mtk_clk_gate_ops_setclr, \ +#define GATE_ICG2(_id, _name, _parent, _shift) \ + GATE_ICG2_FLAGS(_id, _name, _parent, _shift, 0) + +#define GATE_ICG2_FLAGS(_id, _name, _parent, _shift, _flags) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra2_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + .flags = _flags, \ } +/* + * Clock gates dramc and dramc_b are needed by the DRAM controller. + * We mark them as critical as otherwise the system will hang after boot. + */ static const struct mtk_gate infra_clks[] = { GATE_ICG0(CLK_INFRA_PMIC_TMR, "infra_pmic_tmr", "ulposc", 0), GATE_ICG0(CLK_INFRA_PMIC_AP, "infra_pmic_ap", "pmicspi_sel", 1), @@ -505,7 +521,8 @@ static const struct mtk_gate infra_clks[] = { GATE_ICG1(CLK_INFRA_CCIF_AP, "infra_ccif_ap", "axi_sel", 23), GATE_ICG1(CLK_INFRA_AUDIO, "infra_audio", "axi_sel", 25), GATE_ICG1(CLK_INFRA_CCIF_MD, "infra_ccif_md", "axi_sel", 26), - GATE_ICG1(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m", "clk26m", 31), + GATE_ICG1_FLAGS(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m", + "clk26m", 31, CLK_IS_CRITICAL), GATE_ICG2(CLK_INFRA_I2C4, "infra_i2c4", "axi_sel", 0), GATE_ICG2(CLK_INFRA_I2C_APPM, "infra_i2c_appm", "axi_sel", 1), GATE_ICG2(CLK_INFRA_I2C_GPUPM, "infra_i2c_gpupm", "axi_sel", 2), @@ -516,7 +533,8 @@ static const struct mtk_gate infra_clks[] = { GATE_ICG2(CLK_INFRA_I2C5, "infra_i2c5", "axi_sel", 7), GATE_ICG2(CLK_INFRA_SYS_CIRQ, "infra_sys_cirq", "axi_sel", 8), GATE_ICG2(CLK_INFRA_SPI1, "infra_spi1", "spi_sel", 10), - GATE_ICG2(CLK_INFRA_DRAMC_B_F26M, "infra_dramc_b_f26m", "clk26m", 11), + GATE_ICG2_FLAGS(CLK_INFRA_DRAMC_B_F26M, "infra_dramc_b_f26m", + "clk26m", 11, CLK_IS_CRITICAL), GATE_ICG2(CLK_INFRA_ANC_MD32, "infra_anc_md32", "anc_md32_sel", 12), GATE_ICG2(CLK_INFRA_ANC_MD32_32K, "infra_anc_md32_32k", "clk26m", 13), GATE_ICG2(CLK_INFRA_DVFS_SPM1, "infra_dvfs_spm1", "axi_sel", 15), diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c index 96c292c3e440..deedeb3ea33b 100644 --- a/drivers/clk/mediatek/clk-mt8173.c +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -533,7 +533,7 @@ static const char * const ca53_parents[] __initconst = { "univpll" }; -static const char * const ca57_parents[] __initconst = { +static const char * const ca72_parents[] __initconst = { "clk26m", "armca15pll", "mainpll", @@ -542,7 +542,7 @@ static const char * const ca57_parents[] __initconst = { static const struct mtk_composite cpu_muxes[] __initconst = { MUX(CLK_INFRA_CA53SEL, "infra_ca53_sel", ca53_parents, 0x0000, 0, 2), - MUX(CLK_INFRA_CA57SEL, "infra_ca57_sel", ca57_parents, 0x0000, 2, 2), + MUX(CLK_INFRA_CA72SEL, "infra_ca72_sel", ca72_parents, 0x0000, 2, 2), }; static const struct mtk_composite top_muxes[] __initconst = { diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index 9c0ae4278a94..5531dd2e496d 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -130,7 +130,7 @@ int mtk_clk_register_gates(struct device_node *node, gate->regs->set_ofs, gate->regs->clr_ofs, gate->regs->sta_ofs, - gate->shift, gate->ops); + gate->shift, gate->ops, gate->flags); if (IS_ERR(clk)) { pr_err("Failed to register clk %s: %ld\n", @@ -167,7 +167,7 @@ struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, mux->mask = BIT(mc->mux_width) - 1; mux->shift = mc->mux_shift; mux->lock = lock; - + mux->flags = mc->mux_flags; mux_hw = &mux->hw; mux_ops = &clk_mux_ops; diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index f83c2bbb677e..fb27b5bf30d9 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -81,15 +81,13 @@ struct mtk_composite { signed char divider_shift; signed char divider_width; + u8 mux_flags; + signed char num_parents; }; -/* - * In case the rate change propagation to parent clocks is undesirable, - * this macro allows to specify the clock flags manually. - */ -#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \ - _gate, _flags) { \ +#define MUX_GATE_FLAGS_2(_id, _name, _parents, _reg, _shift, \ + _width, _gate, _flags, _muxflags) { \ .id = _id, \ .name = _name, \ .mux_reg = _reg, \ @@ -101,9 +99,19 @@ struct mtk_composite { .parent_names = _parents, \ .num_parents = ARRAY_SIZE(_parents), \ .flags = _flags, \ + .mux_flags = _muxflags, \ } /* + * In case the rate change propagation to parent clocks is undesirable, + * this macro allows to specify the clock flags manually. + */ +#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \ + _gate, _flags) \ + MUX_GATE_FLAGS_2(_id, _name, _parents, _reg, \ + _shift, _width, _gate, _flags, 0) + +/* * Unless necessary, all MUX_GATE clocks propagate rate changes to their * parent clock by default. */ @@ -111,7 +119,11 @@ struct mtk_composite { MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \ _gate, CLK_SET_RATE_PARENT) -#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ +#define MUX(_id, _name, _parents, _reg, _shift, _width) \ + MUX_FLAGS(_id, _name, _parents, _reg, \ + _shift, _width, CLK_SET_RATE_PARENT) + +#define MUX_FLAGS(_id, _name, _parents, _reg, _shift, _width, _flags) { \ .id = _id, \ .name = _name, \ .mux_reg = _reg, \ @@ -121,7 +133,7 @@ struct mtk_composite { .divider_shift = -1, \ .parent_names = _parents, \ .num_parents = ARRAY_SIZE(_parents), \ - .flags = CLK_SET_RATE_PARENT, \ + .flags = _flags, \ } #define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, \ @@ -158,6 +170,7 @@ struct mtk_gate { const struct mtk_gate_regs *regs; int shift; const struct clk_ops *ops; + unsigned long flags; }; int mtk_clk_register_gates(struct device_node *node, diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index efaa70f682b4..3858747f5438 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -1,27 +1,52 @@ -config COMMON_CLK_AMLOGIC - bool - depends on ARCH_MESON || COMPILE_TEST - select COMMON_CLK_REGMAP_MESON +config COMMON_CLK_MESON_INPUT + tristate -config COMMON_CLK_AMLOGIC_AUDIO - bool - depends on ARCH_MESON || COMPILE_TEST - select COMMON_CLK_AMLOGIC +config COMMON_CLK_MESON_REGMAP + tristate + select REGMAP -config COMMON_CLK_MESON_AO - bool - depends on OF - depends on ARCH_MESON || COMPILE_TEST - select COMMON_CLK_REGMAP_MESON +config COMMON_CLK_MESON_DUALDIV + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_MPLL + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_PHASE + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_PLL + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_SCLK_DIV + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_VID_PLL_DIV + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_AO_CLKC + tristate + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_INPUT select RESET_CONTROLLER -config COMMON_CLK_REGMAP_MESON - bool - select REGMAP +config COMMON_CLK_MESON_EE_CLKC + tristate + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_INPUT config COMMON_CLK_MESON8B bool - select COMMON_CLK_AMLOGIC + depends on ARCH_MESON + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select MFD_SYSCON select RESET_CONTROLLER help Support for the clock controller on AmLogic S802 (Meson8), @@ -30,8 +55,14 @@ config COMMON_CLK_MESON8B config COMMON_CLK_GXBB bool - select COMMON_CLK_AMLOGIC - select COMMON_CLK_MESON_AO + depends on ARCH_MESON + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_DUALDIV + select COMMON_CLK_MESON_VID_PLL_DIV + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_AO_CLKC + select COMMON_CLK_MESON_EE_CLKC select MFD_SYSCON help Support for the clock controller on AmLogic S905 devices, aka gxbb. @@ -39,8 +70,13 @@ config COMMON_CLK_GXBB config COMMON_CLK_AXG bool - select COMMON_CLK_AMLOGIC - select COMMON_CLK_MESON_AO + depends on ARCH_MESON + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_DUALDIV + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_AO_CLKC + select COMMON_CLK_MESON_EE_CLKC select MFD_SYSCON help Support for the clock controller on AmLogic A113D devices, aka axg. @@ -48,9 +84,26 @@ config COMMON_CLK_AXG config COMMON_CLK_AXG_AUDIO tristate "Meson AXG Audio Clock Controller Driver" - depends on COMMON_CLK_AXG - select COMMON_CLK_AMLOGIC_AUDIO - select MFD_SYSCON + depends on ARCH_MESON + select COMMON_CLK_MESON_INPUT + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_PHASE + select COMMON_CLK_MESON_SCLK_DIV + select REGMAP_MMIO help Support for the audio clock controller on AmLogic A113D devices, aka axg, Say Y if you want audio subsystem to work. + +config COMMON_CLK_G12A + bool + depends on ARCH_MESON + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_DUALDIV + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_AO_CLKC + select COMMON_CLK_MESON_EE_CLKC + select MFD_SYSCON + help + Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 + devices, aka g12a. Say Y if you want peripherals to work. diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index a849aa809825..021fc290e749 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -1,13 +1,20 @@ -# -# Makefile for Meson specific clk -# +# Amlogic clock drivers -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o -obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o -obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o +obj-$(CONFIG_COMMON_CLK_MESON_AO_CLKC) += meson-aoclk.o +obj-$(CONFIG_COMMON_CLK_MESON_DUALDIV) += clk-dualdiv.o +obj-$(CONFIG_COMMON_CLK_MESON_EE_CLKC) += meson-eeclk.o +obj-$(CONFIG_COMMON_CLK_MESON_INPUT) += clk-input.o +obj-$(CONFIG_COMMON_CLK_MESON_MPLL) += clk-mpll.o +obj-$(CONFIG_COMMON_CLK_MESON_PHASE) += clk-phase.o +obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o +obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o +obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o +obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o + +# Amlogic Clock controllers + +obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o +obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o +obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o +obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o -obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o -obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o -obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o -obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o diff --git a/drivers/clk/meson/axg-aoclk.c b/drivers/clk/meson/axg-aoclk.c index 29e088542387..0086f31288eb 100644 --- a/drivers/clk/meson/axg-aoclk.c +++ b/drivers/clk/meson/axg-aoclk.c @@ -12,10 +12,27 @@ #include <linux/platform_device.h> #include <linux/reset-controller.h> #include <linux/mfd/syscon.h> -#include "clk-regmap.h" #include "meson-aoclk.h" #include "axg-aoclk.h" +#include "clk-regmap.h" +#include "clk-dualdiv.h" + +#define IN_PREFIX "ao-in-" + +/* + * AO Configuration Clock registers offsets + * Register offsets from the data sheet must be multiplied by 4. + */ +#define AO_RTI_PWR_CNTL_REG1 0x0C +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_OSCIN_CNTL 0x58 +#define AO_CRT_CLK_CNTL1 0x68 +#define AO_SAR_CLK 0x90 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + #define AXG_AO_GATE(_name, _bit) \ static struct clk_regmap axg_aoclk_##_name = { \ .data = &(struct clk_regmap_gate_data) { \ @@ -25,7 +42,7 @@ static struct clk_regmap axg_aoclk_##_name = { \ .hw.init = &(struct clk_init_data) { \ .name = "axg_ao_" #_name, \ .ops = &clk_regmap_gate_ops, \ - .parent_names = (const char *[]){ "clk81" }, \ + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \ .num_parents = 1, \ .flags = CLK_IGNORE_UNUSED, \ }, \ @@ -39,17 +56,141 @@ AXG_AO_GATE(uart2, 5); AXG_AO_GATE(ir_blaster, 6); AXG_AO_GATE(saradc, 7); +static struct clk_regmap axg_aoclk_cts_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTI_PWR_CNTL_REG0, + .bit_idx = 14, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_oscin", + .ops = &clk_regmap_gate_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap axg_aoclk_32k_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_oscin" }, + .num_parents = 1, + }, +}; + +static const struct meson_clk_dualdiv_param axg_32k_div_table[] = { + { + .dual = 1, + .n1 = 733, + .m1 = 8, + .n2 = 732, + .m2 = 11, + }, {} +}; + +static struct clk_regmap axg_aoclk_32k_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 28, + .width = 1, + }, + .table = axg_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "axg_ao_32k_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap axg_aoclk_32k_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTC_ALT_CLK_CNTL1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "axg_ao_32k_div", + "axg_ao_32k_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap axg_aoclk_32k = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "axg_ao_32k_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap axg_aoclk_cts_rtc_oscin = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 10, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_cts_rtc_oscin", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "axg_ao_32k", + IN_PREFIX "ext_32k-0" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + static struct clk_regmap axg_aoclk_clk81 = { .data = &(struct clk_regmap_mux_data) { .offset = AO_RTI_PWR_CNTL_REG0, .mask = 0x1, .shift = 8, + .flags = CLK_MUX_ROUND_CLOSEST, }, .hw.init = &(struct clk_init_data){ .name = "axg_ao_clk81", .ops = &clk_regmap_mux_ro_ops, - .parent_names = (const char *[]){ "clk81", "ao_alt_xtal"}, + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk", + "axg_ao_cts_rtc_oscin"}, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -62,7 +203,8 @@ static struct clk_regmap axg_aoclk_saradc_mux = { .hw.init = &(struct clk_init_data){ .name = "axg_ao_saradc_mux", .ops = &clk_regmap_mux_ops, - .parent_names = (const char *[]){ "xtal", "axg_ao_clk81" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal", + "axg_ao_clk81" }, .num_parents = 2, }, }; @@ -106,17 +248,23 @@ static const unsigned int axg_aoclk_reset[] = { }; static struct clk_regmap *axg_aoclk_regmap[] = { - [CLKID_AO_REMOTE] = &axg_aoclk_remote, - [CLKID_AO_I2C_MASTER] = &axg_aoclk_i2c_master, - [CLKID_AO_I2C_SLAVE] = &axg_aoclk_i2c_slave, - [CLKID_AO_UART1] = &axg_aoclk_uart1, - [CLKID_AO_UART2] = &axg_aoclk_uart2, - [CLKID_AO_IR_BLASTER] = &axg_aoclk_ir_blaster, - [CLKID_AO_SAR_ADC] = &axg_aoclk_saradc, - [CLKID_AO_CLK81] = &axg_aoclk_clk81, - [CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux, - [CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div, - [CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate, + &axg_aoclk_remote, + &axg_aoclk_i2c_master, + &axg_aoclk_i2c_slave, + &axg_aoclk_uart1, + &axg_aoclk_uart2, + &axg_aoclk_ir_blaster, + &axg_aoclk_saradc, + &axg_aoclk_cts_oscin, + &axg_aoclk_32k_pre, + &axg_aoclk_32k_div, + &axg_aoclk_32k_sel, + &axg_aoclk_32k, + &axg_aoclk_cts_rtc_oscin, + &axg_aoclk_clk81, + &axg_aoclk_saradc_mux, + &axg_aoclk_saradc_div, + &axg_aoclk_saradc_gate, }; static const struct clk_hw_onecell_data axg_aoclk_onecell_data = { @@ -132,10 +280,22 @@ static const struct clk_hw_onecell_data axg_aoclk_onecell_data = { [CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux.hw, [CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div.hw, [CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate.hw, + [CLKID_AO_CTS_OSCIN] = &axg_aoclk_cts_oscin.hw, + [CLKID_AO_32K_PRE] = &axg_aoclk_32k_pre.hw, + [CLKID_AO_32K_DIV] = &axg_aoclk_32k_div.hw, + [CLKID_AO_32K_SEL] = &axg_aoclk_32k_sel.hw, + [CLKID_AO_32K] = &axg_aoclk_32k.hw, + [CLKID_AO_CTS_RTC_OSCIN] = &axg_aoclk_cts_rtc_oscin.hw, }, .num = NR_CLKS, }; +static const struct meson_aoclk_input axg_aoclk_inputs[] = { + { .name = "xtal", .required = true }, + { .name = "mpeg-clk", .required = true }, + { .name = "ext-32k-0", .required = false }, +}; + static const struct meson_aoclk_data axg_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(axg_aoclk_reset), @@ -143,6 +303,9 @@ static const struct meson_aoclk_data axg_aoclkc_data = { .num_clks = ARRAY_SIZE(axg_aoclk_regmap), .clks = axg_aoclk_regmap, .hw_data = &axg_aoclk_onecell_data, + .inputs = axg_aoclk_inputs, + .num_inputs = ARRAY_SIZE(axg_aoclk_inputs), + .input_prefix = IN_PREFIX, }; static const struct of_device_id axg_aoclkc_match_table[] = { diff --git a/drivers/clk/meson/axg-aoclk.h b/drivers/clk/meson/axg-aoclk.h index 91384d8dd844..3cc27e85170f 100644 --- a/drivers/clk/meson/axg-aoclk.h +++ b/drivers/clk/meson/axg-aoclk.h @@ -10,18 +10,7 @@ #ifndef __AXG_AOCLKC_H #define __AXG_AOCLKC_H -#define NR_CLKS 11 -/* AO Configuration Clock registers offsets - * Register offsets from the data sheet must be multiplied by 4. - */ -#define AO_RTI_PWR_CNTL_REG1 0x0C -#define AO_RTI_PWR_CNTL_REG0 0x10 -#define AO_RTI_GEN_CNTL_REG0 0x40 -#define AO_OSCIN_CNTL 0x58 -#define AO_CRT_CLK_CNTL1 0x68 -#define AO_SAR_CLK 0x90 -#define AO_RTC_ALT_CLK_CNTL0 0x94 -#define AO_RTC_ALT_CLK_CNTL1 0x98 +#define NR_CLKS 17 #include <dt-bindings/clock/axg-aoclkc.h> #include <dt-bindings/reset/axg-aoclkc.h> diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c index 8ac3a2295473..7ab200b6c3bf 100644 --- a/drivers/clk/meson/axg-audio.c +++ b/drivers/clk/meson/axg-audio.c @@ -14,8 +14,11 @@ #include <linux/reset.h> #include <linux/slab.h> -#include "clkc-audio.h" #include "axg-audio.h" +#include "clk-input.h" +#include "clk-regmap.h" +#include "clk-phase.h" +#include "sclk-div.h" #define AXG_MST_IN_COUNT 8 #define AXG_SLV_SCLK_COUNT 10 diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 792735d7e46e..7a8ef80e5f2c 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -9,16 +9,17 @@ * Author: Qiufang Dai <qiufang.dai@amlogic.com> */ -#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/init.h> #include <linux/of_device.h> -#include <linux/mfd/syscon.h> #include <linux/platform_device.h> -#include <linux/regmap.h> -#include "clkc.h" +#include "clk-input.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-mpll.h" #include "axg.h" +#include "meson-eeclk.h" static DEFINE_SPINLOCK(meson_clk_lock); @@ -58,7 +59,7 @@ static struct clk_regmap axg_fixed_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "fixed_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -113,7 +114,7 @@ static struct clk_regmap axg_sys_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "sys_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -214,7 +215,7 @@ static struct clk_regmap axg_gp0_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "gp0_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -283,7 +284,7 @@ static struct clk_regmap axg_hifi_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "hifi_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -701,7 +702,7 @@ static struct clk_regmap axg_pcie_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "pcie_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -803,7 +804,7 @@ static struct clk_regmap axg_pcie_cml_en1 = { static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 }; static const char * const clk81_parent_names[] = { - "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", + IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", "fclk_div3", "fclk_div5" }; @@ -852,7 +853,7 @@ static struct clk_regmap axg_clk81 = { }; static const char * const axg_sd_emmc_clk0_parent_names[] = { - "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", /* * Following these parent clocks, we should also have had mpll2, mpll3 @@ -957,7 +958,7 @@ static struct clk_regmap axg_sd_emmc_c_clk0 = { static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, }; static const char * const gen_clk_parent_names[] = { - "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3", + IN_PREFIX "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3", "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll", }; @@ -1255,46 +1256,20 @@ static struct clk_regmap *const axg_clk_regmaps[] = { &axg_pcie_pll_od, }; +static const struct meson_eeclkc_data axg_clkc_data = { + .regmap_clks = axg_clk_regmaps, + .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps), + .hw_onecell_data = &axg_hw_onecell_data, +}; + + static const struct of_device_id clkc_match_table[] = { - { .compatible = "amlogic,axg-clkc" }, + { .compatible = "amlogic,axg-clkc", .data = &axg_clkc_data }, {} }; -static int axg_clkc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct regmap *map; - int ret, i; - - /* Get the hhi system controller node if available */ - map = syscon_node_to_regmap(of_get_parent(dev->of_node)); - if (IS_ERR(map)) { - dev_err(dev, "failed to get HHI regmap\n"); - return PTR_ERR(map); - } - - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(axg_clk_regmaps); i++) - axg_clk_regmaps[i]->map = map; - - for (i = 0; i < axg_hw_onecell_data.num; i++) { - /* array might be sparse */ - if (!axg_hw_onecell_data.hws[i]) - continue; - - ret = devm_clk_hw_register(dev, axg_hw_onecell_data.hws[i]); - if (ret) { - dev_err(dev, "Clock registration failed\n"); - return ret; - } - } - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, - &axg_hw_onecell_data); -} - static struct platform_driver axg_driver = { - .probe = axg_clkc_probe, + .probe = meson_eeclkc_probe, .driver = { .name = "axg-clkc", .of_match_table = clkc_match_table, diff --git a/drivers/clk/meson/clk-dualdiv.c b/drivers/clk/meson/clk-dualdiv.c new file mode 100644 index 000000000000..c5ca23a5e3e8 --- /dev/null +++ b/drivers/clk/meson/clk-dualdiv.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +/* + * The AO Domain embeds a dual/divider to generate a more precise + * 32,768KHz clock for low-power suspend mode and CEC. + * ______ ______ + * | | | | + * | Div1 |-| Cnt1 | + * /|______| |______|\ + * -| ______ ______ X--> Out + * \| | | |/ + * | Div2 |-| Cnt2 | + * |______| |______| + * + * The dividing can be switched to single or dual, with a counter + * for each divider to set when the switching is done. + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> + +#include "clk-regmap.h" +#include "clk-dualdiv.h" + +static inline struct meson_clk_dualdiv_data * +meson_clk_dualdiv_data(struct clk_regmap *clk) +{ + return (struct meson_clk_dualdiv_data *)clk->data; +} + +static unsigned long +__dualdiv_param_to_rate(unsigned long parent_rate, + const struct meson_clk_dualdiv_param *p) +{ + if (!p->dual) + return DIV_ROUND_CLOSEST(parent_rate, p->n1); + + return DIV_ROUND_CLOSEST(parent_rate * (p->m1 + p->m2), + p->n1 * p->m1 + p->n2 * p->m2); +} + +static unsigned long meson_clk_dualdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk); + struct meson_clk_dualdiv_param setting; + + setting.dual = meson_parm_read(clk->map, &dualdiv->dual); + setting.n1 = meson_parm_read(clk->map, &dualdiv->n1) + 1; + setting.m1 = meson_parm_read(clk->map, &dualdiv->m1) + 1; + setting.n2 = meson_parm_read(clk->map, &dualdiv->n2) + 1; + setting.m2 = meson_parm_read(clk->map, &dualdiv->m2) + 1; + + return __dualdiv_param_to_rate(parent_rate, &setting); +} + +static const struct meson_clk_dualdiv_param * +__dualdiv_get_setting(unsigned long rate, unsigned long parent_rate, + struct meson_clk_dualdiv_data *dualdiv) +{ + const struct meson_clk_dualdiv_param *table = dualdiv->table; + unsigned long best = 0, now = 0; + unsigned int i, best_i = 0; + + if (!table) + return NULL; + + for (i = 0; table[i].n1; i++) { + now = __dualdiv_param_to_rate(parent_rate, &table[i]); + + /* If we get an exact match, don't bother any further */ + if (now == rate) { + return &table[i]; + } else if (abs(now - rate) < abs(best - rate)) { + best = now; + best_i = i; + } + } + + return (struct meson_clk_dualdiv_param *)&table[best_i]; +} + +static long meson_clk_dualdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk); + const struct meson_clk_dualdiv_param *setting = + __dualdiv_get_setting(rate, *parent_rate, dualdiv); + + if (!setting) + return meson_clk_dualdiv_recalc_rate(hw, *parent_rate); + + return __dualdiv_param_to_rate(*parent_rate, setting); +} + +static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk); + const struct meson_clk_dualdiv_param *setting = + __dualdiv_get_setting(rate, parent_rate, dualdiv); + + if (!setting) + return -EINVAL; + + meson_parm_write(clk->map, &dualdiv->dual, setting->dual); + meson_parm_write(clk->map, &dualdiv->n1, setting->n1 - 1); + meson_parm_write(clk->map, &dualdiv->m1, setting->m1 - 1); + meson_parm_write(clk->map, &dualdiv->n2, setting->n2 - 1); + meson_parm_write(clk->map, &dualdiv->m2, setting->m2 - 1); + + return 0; +} + +const struct clk_ops meson_clk_dualdiv_ops = { + .recalc_rate = meson_clk_dualdiv_recalc_rate, + .round_rate = meson_clk_dualdiv_round_rate, + .set_rate = meson_clk_dualdiv_set_rate, +}; +EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ops); + +const struct clk_ops meson_clk_dualdiv_ro_ops = { + .recalc_rate = meson_clk_dualdiv_recalc_rate, +}; +EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops); + +MODULE_DESCRIPTION("Amlogic dual divider driver"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-dualdiv.h b/drivers/clk/meson/clk-dualdiv.h new file mode 100644 index 000000000000..4aa939018012 --- /dev/null +++ b/drivers/clk/meson/clk-dualdiv.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_CLK_DUALDIV_H +#define __MESON_CLK_DUALDIV_H + +#include <linux/clk-provider.h> +#include "parm.h" + +struct meson_clk_dualdiv_param { + unsigned int n1; + unsigned int n2; + unsigned int m1; + unsigned int m2; + unsigned int dual; +}; + +struct meson_clk_dualdiv_data { + struct parm n1; + struct parm n2; + struct parm m1; + struct parm m2; + struct parm dual; + const struct meson_clk_dualdiv_param *table; +}; + +extern const struct clk_ops meson_clk_dualdiv_ops; +extern const struct clk_ops meson_clk_dualdiv_ro_ops; + +#endif /* __MESON_CLK_DUALDIV_H */ diff --git a/drivers/clk/meson/clk-input.c b/drivers/clk/meson/clk-input.c index 06b3e3bb6a66..086226e9dba6 100644 --- a/drivers/clk/meson/clk-input.c +++ b/drivers/clk/meson/clk-input.c @@ -7,7 +7,8 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/device.h> -#include "clkc.h" +#include <linux/module.h> +#include "clk-input.h" static const struct clk_ops meson_clk_no_ops = {}; @@ -42,3 +43,7 @@ struct clk_hw *meson_clk_hw_register_input(struct device *dev, return ret ? ERR_PTR(ret) : hw; } EXPORT_SYMBOL_GPL(meson_clk_hw_register_input); + +MODULE_DESCRIPTION("Amlogic clock input helper"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-input.h b/drivers/clk/meson/clk-input.h new file mode 100644 index 000000000000..4a541b9685a6 --- /dev/null +++ b/drivers/clk/meson/clk-input.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_CLK_INPUT_H +#define __MESON_CLK_INPUT_H + +#include <linux/clk-provider.h> + +struct device; + +struct clk_hw *meson_clk_hw_register_input(struct device *dev, + const char *of_name, + const char *clk_name, + unsigned long flags); + +#endif /* __MESON_CLK_INPUT_H */ diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c index 650f75cc15a9..f76850d99e59 100644 --- a/drivers/clk/meson/clk-mpll.c +++ b/drivers/clk/meson/clk-mpll.c @@ -12,7 +12,11 @@ */ #include <linux/clk-provider.h> -#include "clkc.h" +#include <linux/module.h> +#include <linux/spinlock.h> + +#include "clk-regmap.h" +#include "clk-mpll.h" #define SDM_DEN 16384 #define N2_MIN 4 @@ -138,9 +142,15 @@ const struct clk_ops meson_clk_mpll_ro_ops = { .recalc_rate = mpll_recalc_rate, .round_rate = mpll_round_rate, }; +EXPORT_SYMBOL_GPL(meson_clk_mpll_ro_ops); const struct clk_ops meson_clk_mpll_ops = { .recalc_rate = mpll_recalc_rate, .round_rate = mpll_round_rate, .set_rate = mpll_set_rate, }; +EXPORT_SYMBOL_GPL(meson_clk_mpll_ops); + +MODULE_DESCRIPTION("Amlogic MPLL driver"); +MODULE_AUTHOR("Michael Turquette <mturquette@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-mpll.h b/drivers/clk/meson/clk-mpll.h new file mode 100644 index 000000000000..cf79340006dd --- /dev/null +++ b/drivers/clk/meson/clk-mpll.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_CLK_MPLL_H +#define __MESON_CLK_MPLL_H + +#include <linux/clk-provider.h> +#include <linux/spinlock.h> + +#include "parm.h" + +struct meson_clk_mpll_data { + struct parm sdm; + struct parm sdm_en; + struct parm n2; + struct parm ssen; + struct parm misc; + spinlock_t *lock; + u8 flags; +}; + +#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0) + +extern const struct clk_ops meson_clk_mpll_ro_ops; +extern const struct clk_ops meson_clk_mpll_ops; + +#endif /* __MESON_CLK_MPLL_H */ diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c index cba43748ce3d..80c3ada193a4 100644 --- a/drivers/clk/meson/clk-phase.c +++ b/drivers/clk/meson/clk-phase.c @@ -5,7 +5,10 @@ */ #include <linux/clk-provider.h> -#include "clkc.h" +#include <linux/module.h> + +#include "clk-regmap.h" +#include "clk-phase.h" #define phase_step(_width) (360 / (1 << (_width))) @@ -15,13 +18,12 @@ meson_clk_phase_data(struct clk_regmap *clk) return (struct meson_clk_phase_data *)clk->data; } -int meson_clk_degrees_from_val(unsigned int val, unsigned int width) +static int meson_clk_degrees_from_val(unsigned int val, unsigned int width) { return phase_step(width) * val; } -EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val); -unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) +static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) { unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width)); @@ -31,7 +33,6 @@ unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) */ return val % (1 << width); } -EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val); static int meson_clk_phase_get_phase(struct clk_hw *hw) { @@ -61,3 +62,67 @@ const struct clk_ops meson_clk_phase_ops = { .set_phase = meson_clk_phase_set_phase, }; EXPORT_SYMBOL_GPL(meson_clk_phase_ops); + +/* + * This is a special clock for the audio controller. + * The phase of mst_sclk clock output can be controlled independently + * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2). + * Controlling these 3 phases as just one makes things simpler and + * give the same clock view to all the element on the i2s bus. + * If necessary, we can still control the phase in the tdm block + * which makes these independent control redundant. + */ +static inline struct meson_clk_triphase_data * +meson_clk_triphase_data(struct clk_regmap *clk) +{ + return (struct meson_clk_triphase_data *)clk->data; +} + +static void meson_clk_triphase_sync(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + /* Get phase 0 and sync it to phase 1 and 2 */ + val = meson_parm_read(clk->map, &tph->ph0); + meson_parm_write(clk->map, &tph->ph1, val); + meson_parm_write(clk->map, &tph->ph2, val); +} + +static int meson_clk_triphase_get_phase(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + /* Phase are in sync, reading phase 0 is enough */ + val = meson_parm_read(clk->map, &tph->ph0); + + return meson_clk_degrees_from_val(val, tph->ph0.width); +} + +static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + val = meson_clk_degrees_to_val(degrees, tph->ph0.width); + meson_parm_write(clk->map, &tph->ph0, val); + meson_parm_write(clk->map, &tph->ph1, val); + meson_parm_write(clk->map, &tph->ph2, val); + + return 0; +} + +const struct clk_ops meson_clk_triphase_ops = { + .init = meson_clk_triphase_sync, + .get_phase = meson_clk_triphase_get_phase, + .set_phase = meson_clk_triphase_set_phase, +}; +EXPORT_SYMBOL_GPL(meson_clk_triphase_ops); + +MODULE_DESCRIPTION("Amlogic phase driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-phase.h b/drivers/clk/meson/clk-phase.h new file mode 100644 index 000000000000..5579f9ced142 --- /dev/null +++ b/drivers/clk/meson/clk-phase.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_CLK_PHASE_H +#define __MESON_CLK_PHASE_H + +#include <linux/clk-provider.h> +#include "parm.h" + +struct meson_clk_phase_data { + struct parm ph; +}; + +struct meson_clk_triphase_data { + struct parm ph0; + struct parm ph1; + struct parm ph2; +}; + +extern const struct clk_ops meson_clk_phase_ops; +extern const struct clk_ops meson_clk_triphase_ops; + +#endif /* __MESON_CLK_PHASE_H */ diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index afffc1547e20..41e16dd7272a 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -32,11 +32,10 @@ #include <linux/io.h> #include <linux/math64.h> #include <linux/module.h> -#include <linux/of_address.h> -#include <linux/slab.h> -#include <linux/string.h> +#include <linux/rational.h> -#include "clkc.h" +#include "clk-regmap.h" +#include "clk-pll.h" static inline struct meson_clk_pll_data * meson_clk_pll_data(struct clk_regmap *clk) @@ -44,12 +43,21 @@ meson_clk_pll_data(struct clk_regmap *clk) return (struct meson_clk_pll_data *)clk->data; } +static int __pll_round_closest_mult(struct meson_clk_pll_data *pll) +{ + if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) && + !MESON_PARM_APPLICABLE(&pll->frac)) + return 1; + + return 0; +} + static unsigned long __pll_params_to_rate(unsigned long parent_rate, - const struct pll_params_table *pllt, - u16 frac, + unsigned int m, unsigned int n, + unsigned int frac, struct meson_clk_pll_data *pll) { - u64 rate = (u64)parent_rate * pllt->m; + u64 rate = (u64)parent_rate * m; if (frac && MESON_PARM_APPLICABLE(&pll->frac)) { u64 frac_rate = (u64)parent_rate * frac; @@ -58,7 +66,7 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate, (1 << pll->frac.width)); } - return DIV_ROUND_UP_ULL(rate, pllt->n); + return DIV_ROUND_UP_ULL(rate, n); } static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, @@ -66,35 +74,39 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); - struct pll_params_table pllt; - u16 frac; + unsigned int m, n, frac; - pllt.n = meson_parm_read(clk->map, &pll->n); - pllt.m = meson_parm_read(clk->map, &pll->m); + n = meson_parm_read(clk->map, &pll->n); + m = meson_parm_read(clk->map, &pll->m); frac = MESON_PARM_APPLICABLE(&pll->frac) ? meson_parm_read(clk->map, &pll->frac) : 0; - return __pll_params_to_rate(parent_rate, &pllt, frac, pll); + return __pll_params_to_rate(parent_rate, m, n, frac, pll); } -static u16 __pll_params_with_frac(unsigned long rate, - unsigned long parent_rate, - const struct pll_params_table *pllt, - struct meson_clk_pll_data *pll) +static unsigned int __pll_params_with_frac(unsigned long rate, + unsigned long parent_rate, + unsigned int m, + unsigned int n, + struct meson_clk_pll_data *pll) { - u16 frac_max = (1 << pll->frac.width); - u64 val = (u64)rate * pllt->n; + unsigned int frac_max = (1 << pll->frac.width); + u64 val = (u64)rate * n; + + /* Bail out if we are already over the requested rate */ + if (rate < parent_rate * m / n) + return 0; if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate); else val = div_u64(val * frac_max, parent_rate); - val -= pllt->m * frac_max; + val -= m * frac_max; - return min((u16)val, (u16)(frac_max - 1)); + return min((unsigned int)val, (frac_max - 1)); } static bool meson_clk_pll_is_better(unsigned long rate, @@ -102,45 +114,123 @@ static bool meson_clk_pll_is_better(unsigned long rate, unsigned long now, struct meson_clk_pll_data *pll) { - if (!(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) || - MESON_PARM_APPLICABLE(&pll->frac)) { - /* Round down */ - if (now < rate && best < now) - return true; - } else { + if (__pll_round_closest_mult(pll)) { /* Round Closest */ if (abs(now - rate) < abs(best - rate)) return true; + } else { + /* Round down */ + if (now < rate && best < now) + return true; } return false; } -static const struct pll_params_table * -meson_clk_get_pll_settings(unsigned long rate, - unsigned long parent_rate, - struct meson_clk_pll_data *pll) +static int meson_clk_get_pll_table_index(unsigned int index, + unsigned int *m, + unsigned int *n, + struct meson_clk_pll_data *pll) { - const struct pll_params_table *table = pll->table; - unsigned long best = 0, now = 0; - unsigned int i, best_i = 0; + if (!pll->table[index].n) + return -EINVAL; + + *m = pll->table[index].m; + *n = pll->table[index].n; + + return 0; +} + +static unsigned int meson_clk_get_pll_range_m(unsigned long rate, + unsigned long parent_rate, + unsigned int n, + struct meson_clk_pll_data *pll) +{ + u64 val = (u64)rate * n; - if (!table) - return NULL; + if (__pll_round_closest_mult(pll)) + return DIV_ROUND_CLOSEST_ULL(val, parent_rate); - for (i = 0; table[i].n; i++) { - now = __pll_params_to_rate(parent_rate, &table[i], 0, pll); + return div_u64(val, parent_rate); +} - /* If we get an exact match, don't bother any further */ - if (now == rate) { - return &table[i]; - } else if (meson_clk_pll_is_better(rate, best, now, pll)) { +static int meson_clk_get_pll_range_index(unsigned long rate, + unsigned long parent_rate, + unsigned int index, + unsigned int *m, + unsigned int *n, + struct meson_clk_pll_data *pll) +{ + *n = index + 1; + + /* Check the predivider range */ + if (*n >= (1 << pll->n.width)) + return -EINVAL; + + if (*n == 1) { + /* Get the boundaries out the way */ + if (rate <= pll->range->min * parent_rate) { + *m = pll->range->min; + return -ENODATA; + } else if (rate >= pll->range->max * parent_rate) { + *m = pll->range->max; + return -ENODATA; + } + } + + *m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll); + + /* the pre-divider gives a multiplier too big - stop */ + if (*m >= (1 << pll->m.width)) + return -EINVAL; + + return 0; +} + +static int meson_clk_get_pll_get_index(unsigned long rate, + unsigned long parent_rate, + unsigned int index, + unsigned int *m, + unsigned int *n, + struct meson_clk_pll_data *pll) +{ + if (pll->range) + return meson_clk_get_pll_range_index(rate, parent_rate, + index, m, n, pll); + else if (pll->table) + return meson_clk_get_pll_table_index(index, m, n, pll); + + return -EINVAL; +} + +static int meson_clk_get_pll_settings(unsigned long rate, + unsigned long parent_rate, + unsigned int *best_m, + unsigned int *best_n, + struct meson_clk_pll_data *pll) +{ + unsigned long best = 0, now = 0; + unsigned int i, m, n; + int ret; + + for (i = 0, ret = 0; !ret; i++) { + ret = meson_clk_get_pll_get_index(rate, parent_rate, + i, &m, &n, pll); + if (ret == -EINVAL) + break; + + now = __pll_params_to_rate(parent_rate, m, n, 0, pll); + if (meson_clk_pll_is_better(rate, best, now, pll)) { best = now; - best_i = i; + *best_m = m; + *best_n = n; + + if (now == rate) + break; } } - return (struct pll_params_table *)&table[best_i]; + return best ? 0 : -EINVAL; } static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -148,15 +238,15 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); - const struct pll_params_table *pllt = - meson_clk_get_pll_settings(rate, *parent_rate, pll); + unsigned int m, n, frac; unsigned long round; - u16 frac; + int ret; - if (!pllt) + ret = meson_clk_get_pll_settings(rate, *parent_rate, &m, &n, pll); + if (ret) return meson_clk_pll_recalc_rate(hw, *parent_rate); - round = __pll_params_to_rate(*parent_rate, pllt, 0, pll); + round = __pll_params_to_rate(*parent_rate, m, n, 0, pll); if (!MESON_PARM_APPLICABLE(&pll->frac) || rate == round) return round; @@ -165,9 +255,9 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, * The rate provided by the setting is not an exact match, let's * try to improve the result using the fractional parameter */ - frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll); + frac = __pll_params_with_frac(rate, *parent_rate, m, n, pll); - return __pll_params_to_rate(*parent_rate, pllt, frac, pll); + return __pll_params_to_rate(*parent_rate, m, n, frac, pll); } static int meson_clk_pll_wait_lock(struct clk_hw *hw) @@ -254,30 +344,27 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); - const struct pll_params_table *pllt; - unsigned int enabled; + unsigned int enabled, m, n, frac = 0, ret; unsigned long old_rate; - u16 frac = 0; if (parent_rate == 0 || rate == 0) return -EINVAL; old_rate = rate; - pllt = meson_clk_get_pll_settings(rate, parent_rate, pll); - if (!pllt) - return -EINVAL; + ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll); + if (ret) + return ret; enabled = meson_parm_read(clk->map, &pll->en); if (enabled) meson_clk_pll_disable(hw); - meson_parm_write(clk->map, &pll->n, pllt->n); - meson_parm_write(clk->map, &pll->m, pllt->m); - + meson_parm_write(clk->map, &pll->n, n); + meson_parm_write(clk->map, &pll->m, m); if (MESON_PARM_APPLICABLE(&pll->frac)) { - frac = __pll_params_with_frac(rate, parent_rate, pllt, pll); + frac = __pll_params_with_frac(rate, parent_rate, m, n, pll); meson_parm_write(clk->map, &pll->frac, frac); } @@ -309,8 +396,15 @@ const struct clk_ops meson_clk_pll_ops = { .enable = meson_clk_pll_enable, .disable = meson_clk_pll_disable }; +EXPORT_SYMBOL_GPL(meson_clk_pll_ops); const struct clk_ops meson_clk_pll_ro_ops = { .recalc_rate = meson_clk_pll_recalc_rate, .is_enabled = meson_clk_pll_is_enabled, }; +EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops); + +MODULE_DESCRIPTION("Amlogic PLL driver"); +MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h new file mode 100644 index 000000000000..55af2e285b1b --- /dev/null +++ b/drivers/clk/meson/clk-pll.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_CLK_PLL_H +#define __MESON_CLK_PLL_H + +#include <linux/clk-provider.h> +#include <linux/regmap.h> +#include "parm.h" + +struct pll_params_table { + unsigned int m; + unsigned int n; +}; + +struct pll_mult_range { + unsigned int min; + unsigned int max; +}; + +#define PLL_PARAMS(_m, _n) \ + { \ + .m = (_m), \ + .n = (_n), \ + } + +#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0) + +struct meson_clk_pll_data { + struct parm en; + struct parm m; + struct parm n; + struct parm frac; + struct parm l; + struct parm rst; + const struct reg_sequence *init_regs; + unsigned int init_count; + const struct pll_params_table *table; + const struct pll_mult_range *range; + u8 flags; +}; + +extern const struct clk_ops meson_clk_pll_ro_ops; +extern const struct clk_ops meson_clk_pll_ops; + +#endif /* __MESON_CLK_PLL_H */ diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c index c515f67322a3..dcd1757cc5df 100644 --- a/drivers/clk/meson/clk-regmap.c +++ b/drivers/clk/meson/clk-regmap.c @@ -4,6 +4,7 @@ * Author: Jerome Brunet <jbrunet@baylibre.com> */ +#include <linux/module.h> #include "clk-regmap.h" static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) @@ -180,3 +181,7 @@ const struct clk_ops clk_regmap_mux_ro_ops = { .get_parent = clk_regmap_mux_get_parent, }; EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); + +MODULE_DESCRIPTION("Amlogic regmap backed clock driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h index e9c5728d40eb..1dd0abe3ba91 100644 --- a/drivers/clk/meson/clk-regmap.h +++ b/drivers/clk/meson/clk-regmap.h @@ -111,4 +111,24 @@ clk_get_regmap_mux_data(struct clk_regmap *clk) extern const struct clk_ops clk_regmap_mux_ops; extern const struct clk_ops clk_regmap_mux_ro_ops; +#define __MESON_GATE(_name, _reg, _bit, _ops) \ +struct clk_regmap _name = { \ + .data = &(struct clk_regmap_gate_data){ \ + .offset = (_reg), \ + .bit_idx = (_bit), \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = #_name, \ + .ops = _ops, \ + .parent_names = (const char *[]){ "clk81" }, \ + .num_parents = 1, \ + .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ + }, \ +} + +#define MESON_GATE(_name, _reg, _bit) \ + __MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ops) + +#define MESON_GATE_RO(_name, _reg, _bit) \ + __MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ro_ops) #endif /* __CLK_REGMAP_H */ diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c deleted file mode 100644 index 4a59936251e5..000000000000 --- a/drivers/clk/meson/clk-triphase.c +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* - * Copyright (c) 2018 BayLibre, SAS. - * Author: Jerome Brunet <jbrunet@baylibre.com> - */ - -#include <linux/clk-provider.h> -#include "clkc-audio.h" - -/* - * This is a special clock for the audio controller. - * The phase of mst_sclk clock output can be controlled independently - * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2). - * Controlling these 3 phases as just one makes things simpler and - * give the same clock view to all the element on the i2s bus. - * If necessary, we can still control the phase in the tdm block - * which makes these independent control redundant. - */ -static inline struct meson_clk_triphase_data * -meson_clk_triphase_data(struct clk_regmap *clk) -{ - return (struct meson_clk_triphase_data *)clk->data; -} - -static void meson_clk_triphase_sync(struct clk_hw *hw) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); - unsigned int val; - - /* Get phase 0 and sync it to phase 1 and 2 */ - val = meson_parm_read(clk->map, &tph->ph0); - meson_parm_write(clk->map, &tph->ph1, val); - meson_parm_write(clk->map, &tph->ph2, val); -} - -static int meson_clk_triphase_get_phase(struct clk_hw *hw) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); - unsigned int val; - - /* Phase are in sync, reading phase 0 is enough */ - val = meson_parm_read(clk->map, &tph->ph0); - - return meson_clk_degrees_from_val(val, tph->ph0.width); -} - -static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); - unsigned int val; - - val = meson_clk_degrees_to_val(degrees, tph->ph0.width); - meson_parm_write(clk->map, &tph->ph0, val); - meson_parm_write(clk->map, &tph->ph1, val); - meson_parm_write(clk->map, &tph->ph2, val); - - return 0; -} - -const struct clk_ops meson_clk_triphase_ops = { - .init = meson_clk_triphase_sync, - .get_phase = meson_clk_triphase_get_phase, - .set_phase = meson_clk_triphase_set_phase, -}; -EXPORT_SYMBOL_GPL(meson_clk_triphase_ops); diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h deleted file mode 100644 index 6183b22c4bf2..000000000000 --- a/drivers/clk/meson/clkc.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2015 Endless Mobile, Inc. - * Author: Carlo Caione <carlo@endlessm.com> - */ - -#ifndef __CLKC_H -#define __CLKC_H - -#include <linux/clk-provider.h> -#include "clk-regmap.h" - -#define PMASK(width) GENMASK(width - 1, 0) -#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift) -#define CLRPMASK(width, shift) (~SETPMASK(width, shift)) - -#define PARM_GET(width, shift, reg) \ - (((reg) & SETPMASK(width, shift)) >> (shift)) -#define PARM_SET(width, shift, reg, val) \ - (((reg) & CLRPMASK(width, shift)) | ((val) << (shift))) - -#define MESON_PARM_APPLICABLE(p) (!!((p)->width)) - -struct parm { - u16 reg_off; - u8 shift; - u8 width; -}; - -static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p) -{ - unsigned int val; - - regmap_read(map, p->reg_off, &val); - return PARM_GET(p->width, p->shift, val); -} - -static inline void meson_parm_write(struct regmap *map, struct parm *p, - unsigned int val) -{ - regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift), - val << p->shift); -} - - -struct pll_params_table { - u16 m; - u16 n; -}; - -#define PLL_PARAMS(_m, _n) \ - { \ - .m = (_m), \ - .n = (_n), \ - } - -#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0) - -struct meson_clk_pll_data { - struct parm en; - struct parm m; - struct parm n; - struct parm frac; - struct parm l; - struct parm rst; - const struct reg_sequence *init_regs; - unsigned int init_count; - const struct pll_params_table *table; - u8 flags; -}; - -#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw) - -struct meson_clk_mpll_data { - struct parm sdm; - struct parm sdm_en; - struct parm n2; - struct parm ssen; - struct parm misc; - spinlock_t *lock; - u8 flags; -}; - -#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0) - -struct meson_clk_phase_data { - struct parm ph; -}; - -int meson_clk_degrees_from_val(unsigned int val, unsigned int width); -unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width); - -struct meson_vid_pll_div_data { - struct parm val; - struct parm sel; -}; - -#define MESON_GATE(_name, _reg, _bit) \ -struct clk_regmap _name = { \ - .data = &(struct clk_regmap_gate_data){ \ - .offset = (_reg), \ - .bit_idx = (_bit), \ - }, \ - .hw.init = &(struct clk_init_data) { \ - .name = #_name, \ - .ops = &clk_regmap_gate_ops, \ - .parent_names = (const char *[]){ "clk81" }, \ - .num_parents = 1, \ - .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ - }, \ -}; - -/* clk_ops */ -extern const struct clk_ops meson_clk_pll_ro_ops; -extern const struct clk_ops meson_clk_pll_ops; -extern const struct clk_ops meson_clk_cpu_ops; -extern const struct clk_ops meson_clk_mpll_ro_ops; -extern const struct clk_ops meson_clk_mpll_ops; -extern const struct clk_ops meson_clk_phase_ops; -extern const struct clk_ops meson_vid_pll_div_ro_ops; - -struct clk_hw *meson_clk_hw_register_input(struct device *dev, - const char *of_name, - const char *clk_name, - unsigned long flags); - -#endif /* __CLKC_H */ diff --git a/drivers/clk/meson/g12a-aoclk.c b/drivers/clk/meson/g12a-aoclk.c new file mode 100644 index 000000000000..1994e735396b --- /dev/null +++ b/drivers/clk/meson/g12a-aoclk.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson-AXG Clock Controller Driver + * + * Copyright (c) 2016 Baylibre SAS. + * Author: Michael Turquette <mturquette@baylibre.com> + * + * Copyright (c) 2019 Baylibre SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ +#include <linux/clk-provider.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/mfd/syscon.h> +#include "meson-aoclk.h" +#include "g12a-aoclk.h" + +#include "clk-regmap.h" +#include "clk-dualdiv.h" + +#define IN_PREFIX "ao-in-" + +/* + * AO Configuration Clock registers offsets + * Register offsets from the data sheet must be multiplied by 4. + */ +#define AO_RTI_STATUS_REG3 0x0C +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_CLK_GATE0 0x4c +#define AO_CLK_GATE0_SP 0x50 +#define AO_OSCIN_CNTL 0x58 +#define AO_CEC_CLK_CNTL_REG0 0x74 +#define AO_CEC_CLK_CNTL_REG1 0x78 +#define AO_SAR_CLK 0x90 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + +/* + * Like every other peripheral clock gate in Amlogic Clock drivers, + * we are using CLK_IGNORE_UNUSED here, so we keep the state of the + * bootloader. The goal is to remove this flag at some point. + * Actually removing it will require some extensive test to be done safely. + */ +#define AXG_AO_GATE(_name, _reg, _bit) \ +static struct clk_regmap g12a_aoclk_##_name = { \ + .data = &(struct clk_regmap_gate_data) { \ + .offset = (_reg), \ + .bit_idx = (_bit), \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = "g12a_ao_" #_name, \ + .ops = &clk_regmap_gate_ops, \ + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \ + .num_parents = 1, \ + .flags = CLK_IGNORE_UNUSED, \ + }, \ +} + +AXG_AO_GATE(ahb, AO_CLK_GATE0, 0); +AXG_AO_GATE(ir_in, AO_CLK_GATE0, 1); +AXG_AO_GATE(i2c_m0, AO_CLK_GATE0, 2); +AXG_AO_GATE(i2c_s0, AO_CLK_GATE0, 3); +AXG_AO_GATE(uart, AO_CLK_GATE0, 4); +AXG_AO_GATE(prod_i2c, AO_CLK_GATE0, 5); +AXG_AO_GATE(uart2, AO_CLK_GATE0, 6); +AXG_AO_GATE(ir_out, AO_CLK_GATE0, 7); +AXG_AO_GATE(saradc, AO_CLK_GATE0, 8); +AXG_AO_GATE(mailbox, AO_CLK_GATE0_SP, 0); +AXG_AO_GATE(m3, AO_CLK_GATE0_SP, 1); +AXG_AO_GATE(ahb_sram, AO_CLK_GATE0_SP, 2); +AXG_AO_GATE(rti, AO_CLK_GATE0_SP, 3); +AXG_AO_GATE(m4_fclk, AO_CLK_GATE0_SP, 4); +AXG_AO_GATE(m4_hclk, AO_CLK_GATE0_SP, 5); + +static struct clk_regmap g12a_aoclk_cts_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTI_PWR_CNTL_REG0, + .bit_idx = 14, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_oscin", + .ops = &clk_regmap_gate_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static const struct meson_clk_dualdiv_param g12a_32k_div_table[] = { + { + .dual = 1, + .n1 = 733, + .m1 = 8, + .n2 = 732, + .m2 = 11, + }, {} +}; + +/* 32k_by_oscin clock */ + +static struct clk_regmap g12a_aoclk_32k_by_oscin_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_oscin" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_32k_by_oscin_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 28, + .width = 1, + }, + .table = g12a_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_32k_by_oscin_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTC_ALT_CLK_CNTL1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_div", + "g12a_ao_32k_by_oscin_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_32k_by_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* cec clock */ + +static struct clk_regmap g12a_aoclk_cec_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_CEC_CLK_CNTL_REG0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_oscin" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_cec_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_CEC_CLK_CNTL_REG0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_CEC_CLK_CNTL_REG0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_CEC_CLK_CNTL_REG1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_CEC_CLK_CNTL_REG1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_CEC_CLK_CNTL_REG0, + .shift = 28, + .width = 1, + }, + .table = g12a_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "g12a_ao_cec_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_cec_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_CEC_CLK_CNTL_REG1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "g12a_ao_cec_div", + "g12a_ao_cec_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_cec = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_CEC_CLK_CNTL_REG0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "g12a_ao_cec_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_cts_rtc_oscin = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 10, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cts_rtc_oscin", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin", + IN_PREFIX "ext_32k-0" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_clk81 = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 8, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_clk81", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk", + "g12a_ao_cts_rtc_oscin"}, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_saradc_mux = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_SAR_CLK, + .mask = 0x3, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_saradc_mux", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal", + "g12a_ao_clk81" }, + .num_parents = 2, + }, +}; + +static struct clk_regmap g12a_aoclk_saradc_div = { + .data = &(struct clk_regmap_div_data) { + .offset = AO_SAR_CLK, + .shift = 0, + .width = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_saradc_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "g12a_ao_saradc_mux" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_saradc_gate = { + .data = &(struct clk_regmap_gate_data) { + .offset = AO_SAR_CLK, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_saradc_gate", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "g12a_ao_saradc_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const unsigned int g12a_aoclk_reset[] = { + [RESET_AO_IR_IN] = 16, + [RESET_AO_UART] = 17, + [RESET_AO_I2C_M] = 18, + [RESET_AO_I2C_S] = 19, + [RESET_AO_SAR_ADC] = 20, + [RESET_AO_UART2] = 22, + [RESET_AO_IR_OUT] = 23, +}; + +static struct clk_regmap *g12a_aoclk_regmap[] = { + &g12a_aoclk_ahb, + &g12a_aoclk_ir_in, + &g12a_aoclk_i2c_m0, + &g12a_aoclk_i2c_s0, + &g12a_aoclk_uart, + &g12a_aoclk_prod_i2c, + &g12a_aoclk_uart2, + &g12a_aoclk_ir_out, + &g12a_aoclk_saradc, + &g12a_aoclk_mailbox, + &g12a_aoclk_m3, + &g12a_aoclk_ahb_sram, + &g12a_aoclk_rti, + &g12a_aoclk_m4_fclk, + &g12a_aoclk_m4_hclk, + &g12a_aoclk_cts_oscin, + &g12a_aoclk_32k_by_oscin_pre, + &g12a_aoclk_32k_by_oscin_div, + &g12a_aoclk_32k_by_oscin_sel, + &g12a_aoclk_32k_by_oscin, + &g12a_aoclk_cec_pre, + &g12a_aoclk_cec_div, + &g12a_aoclk_cec_sel, + &g12a_aoclk_cec, + &g12a_aoclk_cts_rtc_oscin, + &g12a_aoclk_clk81, + &g12a_aoclk_saradc_mux, + &g12a_aoclk_saradc_div, + &g12a_aoclk_saradc_gate, +}; + +static const struct clk_hw_onecell_data g12a_aoclk_onecell_data = { + .hws = { + [CLKID_AO_AHB] = &g12a_aoclk_ahb.hw, + [CLKID_AO_IR_IN] = &g12a_aoclk_ir_in.hw, + [CLKID_AO_I2C_M0] = &g12a_aoclk_i2c_m0.hw, + [CLKID_AO_I2C_S0] = &g12a_aoclk_i2c_s0.hw, + [CLKID_AO_UART] = &g12a_aoclk_uart.hw, + [CLKID_AO_PROD_I2C] = &g12a_aoclk_prod_i2c.hw, + [CLKID_AO_UART2] = &g12a_aoclk_uart2.hw, + [CLKID_AO_IR_OUT] = &g12a_aoclk_ir_out.hw, + [CLKID_AO_SAR_ADC] = &g12a_aoclk_saradc.hw, + [CLKID_AO_MAILBOX] = &g12a_aoclk_mailbox.hw, + [CLKID_AO_M3] = &g12a_aoclk_m3.hw, + [CLKID_AO_AHB_SRAM] = &g12a_aoclk_ahb_sram.hw, + [CLKID_AO_RTI] = &g12a_aoclk_rti.hw, + [CLKID_AO_M4_FCLK] = &g12a_aoclk_m4_fclk.hw, + [CLKID_AO_M4_HCLK] = &g12a_aoclk_m4_hclk.hw, + [CLKID_AO_CLK81] = &g12a_aoclk_clk81.hw, + [CLKID_AO_SAR_ADC_SEL] = &g12a_aoclk_saradc_mux.hw, + [CLKID_AO_SAR_ADC_DIV] = &g12a_aoclk_saradc_div.hw, + [CLKID_AO_SAR_ADC_CLK] = &g12a_aoclk_saradc_gate.hw, + [CLKID_AO_CTS_OSCIN] = &g12a_aoclk_cts_oscin.hw, + [CLKID_AO_32K_PRE] = &g12a_aoclk_32k_by_oscin_pre.hw, + [CLKID_AO_32K_DIV] = &g12a_aoclk_32k_by_oscin_div.hw, + [CLKID_AO_32K_SEL] = &g12a_aoclk_32k_by_oscin_sel.hw, + [CLKID_AO_32K] = &g12a_aoclk_32k_by_oscin.hw, + [CLKID_AO_CEC_PRE] = &g12a_aoclk_cec_pre.hw, + [CLKID_AO_CEC_DIV] = &g12a_aoclk_cec_div.hw, + [CLKID_AO_CEC_SEL] = &g12a_aoclk_cec_sel.hw, + [CLKID_AO_CEC] = &g12a_aoclk_cec.hw, + [CLKID_AO_CTS_RTC_OSCIN] = &g12a_aoclk_cts_rtc_oscin.hw, + }, + .num = NR_CLKS, +}; + +static const struct meson_aoclk_input g12a_aoclk_inputs[] = { + { .name = "xtal", .required = true }, + { .name = "mpeg-clk", .required = true }, + { .name = "ext-32k-0", .required = false }, +}; + +static const struct meson_aoclk_data g12a_aoclkc_data = { + .reset_reg = AO_RTI_GEN_CNTL_REG0, + .num_reset = ARRAY_SIZE(g12a_aoclk_reset), + .reset = g12a_aoclk_reset, + .num_clks = ARRAY_SIZE(g12a_aoclk_regmap), + .clks = g12a_aoclk_regmap, + .hw_data = &g12a_aoclk_onecell_data, + .inputs = g12a_aoclk_inputs, + .num_inputs = ARRAY_SIZE(g12a_aoclk_inputs), + .input_prefix = IN_PREFIX, +}; + +static const struct of_device_id g12a_aoclkc_match_table[] = { + { + .compatible = "amlogic,meson-g12a-aoclkc", + .data = &g12a_aoclkc_data, + }, + { } +}; + +static struct platform_driver g12a_aoclkc_driver = { + .probe = meson_aoclkc_probe, + .driver = { + .name = "g12a-aoclkc", + .of_match_table = g12a_aoclkc_match_table, + }, +}; + +builtin_platform_driver(g12a_aoclkc_driver); diff --git a/drivers/clk/meson/g12a-aoclk.h b/drivers/clk/meson/g12a-aoclk.h new file mode 100644 index 000000000000..04b0d5506641 --- /dev/null +++ b/drivers/clk/meson/g12a-aoclk.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __G12A_AOCLKC_H +#define __G12A_AOCLKC_H + +/* + * CLKID index values + * + * These indices are entirely contrived and do not map onto the hardware. + * It has now been decided to expose everything by default in the DT header: + * include/dt-bindings/clock/g12a-aoclkc.h. Only the clocks ids we don't want + * to expose, such as the internal muxes and dividers of composite clocks, + * will remain defined here. + */ +#define CLKID_AO_SAR_ADC_SEL 16 +#define CLKID_AO_SAR_ADC_DIV 17 +#define CLKID_AO_CTS_OSCIN 19 +#define CLKID_AO_32K_PRE 20 +#define CLKID_AO_32K_DIV 21 +#define CLKID_AO_32K_SEL 22 +#define CLKID_AO_CEC_PRE 24 +#define CLKID_AO_CEC_DIV 25 +#define CLKID_AO_CEC_SEL 26 + +#define NR_CLKS 29 + +#include <dt-bindings/clock/g12a-aoclkc.h> +#include <dt-bindings/reset/g12a-aoclkc.h> + +#endif /* __G12A_AOCLKC_H */ diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c new file mode 100644 index 000000000000..0e1ce8c03259 --- /dev/null +++ b/drivers/clk/meson/g12a.c @@ -0,0 +1,2359 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson-G12A Clock Controller Driver + * + * Copyright (c) 2016 Baylibre SAS. + * Author: Michael Turquette <mturquette@baylibre.com> + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Qiufang Dai <qiufang.dai@amlogic.com> + * Author: Jian Hu <jian.hu@amlogic.com> + */ + +#include <linux/clk-provider.h> +#include <linux/init.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include "clk-input.h" +#include "clk-mpll.h" +#include "clk-pll.h" +#include "clk-regmap.h" +#include "vid-pll-div.h" +#include "meson-eeclk.h" +#include "g12a.h" + +static DEFINE_SPINLOCK(meson_clk_lock); + +static struct clk_regmap g12a_fixed_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_FIX_PLL_CNTL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + }, + .hw.init = &(struct clk_init_data){ + .name = "fixed_pll_dco", + .ops = &meson_clk_pll_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fixed_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_FIX_PLL_CNTL0, + .shift = 16, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "fixed_pll", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + /* + * This clock won't ever change at runtime so + * CLK_SET_RATE_PARENT is not required + */ + }, +}; + +/* + * Internal sys pll emulation configuration parameters + */ +static const struct reg_sequence g12a_sys_init_regs[] = { + { .reg = HHI_SYS_PLL_CNTL1, .def = 0x00000000 }, + { .reg = HHI_SYS_PLL_CNTL2, .def = 0x00000000 }, + { .reg = HHI_SYS_PLL_CNTL3, .def = 0x48681c00 }, + { .reg = HHI_SYS_PLL_CNTL4, .def = 0x88770290 }, + { .reg = HHI_SYS_PLL_CNTL5, .def = 0x39272000 }, + { .reg = HHI_SYS_PLL_CNTL6, .def = 0x56540000 }, +}; + +static struct clk_regmap g12a_sys_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .l = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + .init_regs = g12a_sys_init_regs, + .init_count = ARRAY_SIZE(g12a_sys_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "sys_pll_dco", + .ops = &meson_clk_pll_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_sys_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_SYS_PLL_CNTL0, + .shift = 16, + .width = 3, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "sys_pll", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "sys_pll_dco" }, + .num_parents = 1, + }, +}; + +static const struct pll_mult_range g12a_gp0_pll_mult_range = { + .min = 55, + .max = 255, +}; + +/* + * Internal gp0 pll emulation configuration parameters + */ +static const struct reg_sequence g12a_gp0_init_regs[] = { + { .reg = HHI_GP0_PLL_CNTL1, .def = 0x00000000 }, + { .reg = HHI_GP0_PLL_CNTL2, .def = 0x00000000 }, + { .reg = HHI_GP0_PLL_CNTL3, .def = 0x48681c00 }, + { .reg = HHI_GP0_PLL_CNTL4, .def = 0x33771290 }, + { .reg = HHI_GP0_PLL_CNTL5, .def = 0x39272000 }, + { .reg = HHI_GP0_PLL_CNTL6, .def = 0x56540000 }, +}; + +static struct clk_regmap g12a_gp0_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_GP0_PLL_CNTL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + .range = &g12a_gp0_pll_mult_range, + .init_regs = g12a_gp0_init_regs, + .init_count = ARRAY_SIZE(g12a_gp0_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "gp0_pll_dco", + .ops = &meson_clk_pll_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_gp0_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_GP0_PLL_CNTL0, + .shift = 16, + .width = 3, + .flags = (CLK_DIVIDER_POWER_OF_TWO | + CLK_DIVIDER_ROUND_CLOSEST), + }, + .hw.init = &(struct clk_init_data){ + .name = "gp0_pll", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "gp0_pll_dco" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* + * Internal hifi pll emulation configuration parameters + */ +static const struct reg_sequence g12a_hifi_init_regs[] = { + { .reg = HHI_HIFI_PLL_CNTL1, .def = 0x00000000 }, + { .reg = HHI_HIFI_PLL_CNTL2, .def = 0x00000000 }, + { .reg = HHI_HIFI_PLL_CNTL3, .def = 0x6a285c00 }, + { .reg = HHI_HIFI_PLL_CNTL4, .def = 0x65771290 }, + { .reg = HHI_HIFI_PLL_CNTL5, .def = 0x39272000 }, + { .reg = HHI_HIFI_PLL_CNTL6, .def = 0x56540000 }, +}; + +static struct clk_regmap g12a_hifi_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_HIFI_PLL_CNTL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + .range = &g12a_gp0_pll_mult_range, + .init_regs = g12a_hifi_init_regs, + .init_count = ARRAY_SIZE(g12a_hifi_init_regs), + .flags = CLK_MESON_PLL_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "hifi_pll_dco", + .ops = &meson_clk_pll_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_hifi_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HIFI_PLL_CNTL0, + .shift = 16, + .width = 2, + .flags = (CLK_DIVIDER_POWER_OF_TWO | + CLK_DIVIDER_ROUND_CLOSEST), + }, + .hw.init = &(struct clk_init_data){ + .name = "hifi_pll", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "hifi_pll_dco" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_hdmi_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_HDMI_PLL_CNTL1, + .shift = 0, + .width = 16, + }, + .l = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 30, + .width = 1, + }, + .rst = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_dco", + .ops = &meson_clk_pll_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + /* + * Display directly handle hdmi pll registers ATM, we need + * NOCACHE to keep our view of the clock as accurate as possible + */ + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_hdmi_pll_od = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_PLL_CNTL0, + .shift = 16, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_od", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll_dco" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_hdmi_pll_od2 = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_PLL_CNTL0, + .shift = 18, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_od2", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll_od" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_hdmi_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_PLL_CNTL0, + .shift = 20, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll_od2" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div2_div = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div2_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div3_div = { + .mult = 1, + .div = 3, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div3_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div3 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 20, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div3", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div3_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div4_div = { + .mult = 1, + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div4_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div4 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 21, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div4", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div4_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div5_div = { + .mult = 1, + .div = 5, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div5_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div5 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 22, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div5", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div5_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div7_div = { + .mult = 1, + .div = 7, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div7_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div7 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 23, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div7", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div7_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div2p5_div = { + .mult = 1, + .div = 5, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2p5_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div2p5 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2p5", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div2p5_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_mpll_50m_div = { + .mult = 1, + .div = 80, + .hw.init = &(struct clk_init_data){ + .name = "mpll_50m_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll_50m = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_FIX_PLL_CNTL3, + .mask = 0x1, + .shift = 5, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll_50m", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal", + "mpll_50m_div" }, + .num_parents = 2, + }, +}; + +static struct clk_fixed_factor g12a_mpll_prediv = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "mpll_prediv", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll0_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll0_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL1, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mpll1_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll1_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL3, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mpll2_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll2_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL5, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll2", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll2_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mpll3_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll3_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll3 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL7, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll3", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll3_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 }; +static const char * const clk81_parent_names[] = { + IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", + "fclk_div3", "fclk_div5" +}; + +static struct clk_regmap g12a_mpeg_clk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MPEG_CLK_CNTL, + .mask = 0x7, + .shift = 12, + .table = mux_table_clk81, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpeg_clk_sel", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = clk81_parent_names, + .num_parents = ARRAY_SIZE(clk81_parent_names), + }, +}; + +static struct clk_regmap g12a_mpeg_clk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MPEG_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpeg_clk_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mpeg_clk_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_clk81 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPEG_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "clk81", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpeg_clk_div" }, + .num_parents = 1, + .flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL), + }, +}; + +static const char * const g12a_sd_emmc_clk0_parent_names[] = { + IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + + /* + * Following these parent clocks, we should also have had mpll2, mpll3 + * and gp0_pll but these clocks are too precious to be used here. All + * the necessary rates for MMC and NAND operation can be acheived using + * g12a_ee_core or fclk_div clocks + */ +}; + +/* SDIO clock */ +static struct clk_regmap g12a_sd_emmc_a_clk0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 9, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_a_clk0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_a_clk0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_a_clk0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* SDcard clock */ +static struct clk_regmap g12a_sd_emmc_b_clk0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 25, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_b_clk0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_b_clk0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 23, + }, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_b_clk0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* EMMC/NAND clock */ +static struct clk_regmap g12a_sd_emmc_c_clk0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_NAND_CLK_CNTL, + .mask = 0x7, + .shift = 9, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_c_clk0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_NAND_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_c_clk0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_NAND_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_c_clk0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* VPU Clock */ + +static const char * const g12a_vpu_parent_names[] = { + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", + "mpll1", "vid_pll", "hifi_pll", "gp0_pll", +}; + +static struct clk_regmap g12a_vpu_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VPU_CLK_CNTL, + .mask = 0x3, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vpu_parent_names, + .num_parents = ARRAY_SIZE(g12a_vpu_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vpu_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VPU_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vpu_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vpu_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VPU_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "vpu_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vpu_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vpu_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VPU_CLK_CNTL, + .mask = 0x3, + .shift = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vpu_parent_names, + .num_parents = ARRAY_SIZE(g12a_vpu_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vpu_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VPU_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vpu_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vpu_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VPU_CLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data) { + .name = "vpu_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vpu_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vpu = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VPU_CLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu", + .ops = &clk_regmap_mux_ops, + /* + * bit 31 selects from 2 possible parents: + * vpu_0 or vpu_1 + */ + .parent_names = (const char *[]){ "vpu_0", "vpu_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +/* VAPB Clock */ + +static const char * const g12a_vapb_parent_names[] = { + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", + "mpll1", "vid_pll", "mpll2", "fclk_div2p5", +}; + +static struct clk_regmap g12a_vapb_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VAPBCLK_CNTL, + .mask = 0x3, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vapb_parent_names, + .num_parents = ARRAY_SIZE(g12a_vapb_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vapb_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VAPBCLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vapb_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vapb_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VAPBCLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "vapb_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vapb_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vapb_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VAPBCLK_CNTL, + .mask = 0x3, + .shift = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vapb_parent_names, + .num_parents = ARRAY_SIZE(g12a_vapb_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vapb_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VAPBCLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vapb_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vapb_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VAPBCLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data) { + .name = "vapb_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vapb_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vapb_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VAPBCLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_sel", + .ops = &clk_regmap_mux_ops, + /* + * bit 31 selects from 2 possible parents: + * vapb_0 or vapb_1 + */ + .parent_names = (const char *[]){ "vapb_0", "vapb_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vapb = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VAPBCLK_CNTL, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data) { + .name = "vapb", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vapb_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* Video Clocks */ + +static struct clk_regmap g12a_vid_pll_div = { + .data = &(struct meson_vid_pll_div_data){ + .val = { + .reg_off = HHI_VID_PLL_CLK_DIV, + .shift = 0, + .width = 15, + }, + .sel = { + .reg_off = HHI_VID_PLL_CLK_DIV, + .shift = 16, + .width = 2, + }, + }, + .hw.init = &(struct clk_init_data) { + .name = "vid_pll_div", + .ops = &meson_vid_pll_div_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static const char * const g12a_vid_pll_parent_names[] = { "vid_pll_div", + "hdmi_pll" }; + +static struct clk_regmap g12a_vid_pll_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_PLL_CLK_DIV, + .mask = 0x1, + .shift = 18, + }, + .hw.init = &(struct clk_init_data){ + .name = "vid_pll_sel", + .ops = &clk_regmap_mux_ops, + /* + * bit 18 selects from 2 possible parents: + * vid_pll_div or hdmi_pll + */ + .parent_names = g12a_vid_pll_parent_names, + .num_parents = ARRAY_SIZE(g12a_vid_pll_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vid_pll = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_PLL_CLK_DIV, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data) { + .name = "vid_pll", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vid_pll_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static const char * const g12a_vclk_parent_names[] = { + "vid_pll", "gp0_pll", "hifi_pll", "mpll1", "fclk_div3", "fclk_div4", + "fclk_div5", "fclk_div7" +}; + +static struct clk_regmap g12a_vclk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_CLK_CNTL, + .mask = 0x7, + .shift = 16, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vclk_parent_names, + .num_parents = ARRAY_SIZE(g12a_vclk_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk2_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VIID_CLK_CNTL, + .mask = 0x7, + .shift = 16, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vclk_parent_names, + .num_parents = ARRAY_SIZE(g12a_vclk_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk_input = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_DIV, + .bit_idx = 16, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_input", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_input = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_DIV, + .bit_idx = 16, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_input", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VID_CLK_DIV, + .shift = 0, + .width = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vclk_input" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk2_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VIID_CLK_DIV, + .shift = 0, + .width = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vclk2_input" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 0, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div2_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 1, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div2_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div4_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 2, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div4_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div6_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 3, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div6_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div12_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 4, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div12_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 0, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div2_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 1, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div2_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div4_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 2, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div4_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div6_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 3, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div6_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div12_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 4, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div12_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div2 = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div2", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div2_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div4 = { + .mult = 1, + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div4", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div4_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div6 = { + .mult = 1, + .div = 6, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div6", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div6_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div12 = { + .mult = 1, + .div = 12, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div12", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div12_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div2 = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div2", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div2_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div4 = { + .mult = 1, + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div4", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div4_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div6 = { + .mult = 1, + .div = 6, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div6", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div6_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div12 = { + .mult = 1, + .div = 12, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div12", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div12_en" }, + .num_parents = 1, + }, +}; + +static u32 mux_table_cts_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 }; +static const char * const g12a_cts_parent_names[] = { + "vclk_div1", "vclk_div2", "vclk_div4", "vclk_div6", + "vclk_div12", "vclk2_div1", "vclk2_div2", "vclk2_div4", + "vclk2_div6", "vclk2_div12" +}; + +static struct clk_regmap g12a_cts_enci_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_CLK_DIV, + .mask = 0xf, + .shift = 28, + .table = mux_table_cts_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_enci_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_cts_encp_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_CLK_DIV, + .mask = 0xf, + .shift = 20, + .table = mux_table_cts_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_encp_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_cts_vdac_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VIID_CLK_DIV, + .mask = 0xf, + .shift = 28, + .table = mux_table_cts_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_vdac_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +/* TOFIX: add support for cts_tcon */ +static u32 mux_table_hdmi_tx_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 }; +static const char * const g12a_cts_hdmi_tx_parent_names[] = { + "vclk_div1", "vclk_div2", "vclk_div4", "vclk_div6", + "vclk_div12", "vclk2_div1", "vclk2_div2", "vclk2_div4", + "vclk2_div6", "vclk2_div12" +}; + +static struct clk_regmap g12a_hdmi_tx_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_HDMI_CLK_CNTL, + .mask = 0xf, + .shift = 16, + .table = mux_table_hdmi_tx_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_tx_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_hdmi_tx_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_hdmi_tx_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_cts_enci = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 0, + }, + .hw.init = &(struct clk_init_data) { + .name = "cts_enci", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_enci_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_cts_encp = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 2, + }, + .hw.init = &(struct clk_init_data) { + .name = "cts_encp", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_encp_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_cts_vdac = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 4, + }, + .hw.init = &(struct clk_init_data) { + .name = "cts_vdac", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_vdac_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_hdmi_tx = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 5, + }, + .hw.init = &(struct clk_init_data) { + .name = "hdmi_tx", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "hdmi_tx_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* HDMI Clocks */ + +static const char * const g12a_hdmi_parent_names[] = { + IN_PREFIX "xtal", "fclk_div4", "fclk_div3", "fclk_div5" +}; + +static struct clk_regmap g12a_hdmi_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_HDMI_CLK_CNTL, + .mask = 0x3, + .shift = 9, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_hdmi_parent_names, + .num_parents = ARRAY_SIZE(g12a_hdmi_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_hdmi_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "hdmi_sel" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_hdmi = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_HDMI_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "hdmi", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "hdmi_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* + * The MALI IP is clocked by two identical clocks (mali_0 and mali_1) + * muxed by a glitch-free switch. + */ + +static const char * const g12a_mali_0_1_parent_names[] = { + IN_PREFIX "xtal", "gp0_pll", "hihi_pll", "fclk_div2p5", + "fclk_div3", "fclk_div4", "fclk_div5", "fclk_div7" +}; + +static struct clk_regmap g12a_mali_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_mali_0_1_parent_names, + .num_parents = 8, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mali_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_mali_0_1_parent_names, + .num_parents = 8, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const char * const g12a_mali_parent_names[] = { + "mali_0", "mali_1" +}; + +static struct clk_regmap g12a_mali = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_mali_parent_names, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +/* Everything Else (EE) domain gates */ +static MESON_GATE(g12a_ddr, HHI_GCLK_MPEG0, 0); +static MESON_GATE(g12a_dos, HHI_GCLK_MPEG0, 1); +static MESON_GATE(g12a_audio_locker, HHI_GCLK_MPEG0, 2); +static MESON_GATE(g12a_mipi_dsi_host, HHI_GCLK_MPEG0, 3); +static MESON_GATE(g12a_eth_phy, HHI_GCLK_MPEG0, 4); +static MESON_GATE(g12a_isa, HHI_GCLK_MPEG0, 5); +static MESON_GATE(g12a_pl301, HHI_GCLK_MPEG0, 6); +static MESON_GATE(g12a_periphs, HHI_GCLK_MPEG0, 7); +static MESON_GATE(g12a_spicc_0, HHI_GCLK_MPEG0, 8); +static MESON_GATE(g12a_i2c, HHI_GCLK_MPEG0, 9); +static MESON_GATE(g12a_sana, HHI_GCLK_MPEG0, 10); +static MESON_GATE(g12a_sd, HHI_GCLK_MPEG0, 11); +static MESON_GATE(g12a_rng0, HHI_GCLK_MPEG0, 12); +static MESON_GATE(g12a_uart0, HHI_GCLK_MPEG0, 13); +static MESON_GATE(g12a_spicc_1, HHI_GCLK_MPEG0, 14); +static MESON_GATE(g12a_hiu_reg, HHI_GCLK_MPEG0, 19); +static MESON_GATE(g12a_mipi_dsi_phy, HHI_GCLK_MPEG0, 20); +static MESON_GATE(g12a_assist_misc, HHI_GCLK_MPEG0, 23); +static MESON_GATE(g12a_emmc_a, HHI_GCLK_MPEG0, 4); +static MESON_GATE(g12a_emmc_b, HHI_GCLK_MPEG0, 25); +static MESON_GATE(g12a_emmc_c, HHI_GCLK_MPEG0, 26); +static MESON_GATE(g12a_audio_codec, HHI_GCLK_MPEG0, 28); + +static MESON_GATE(g12a_audio, HHI_GCLK_MPEG1, 0); +static MESON_GATE(g12a_eth_core, HHI_GCLK_MPEG1, 3); +static MESON_GATE(g12a_demux, HHI_GCLK_MPEG1, 4); +static MESON_GATE(g12a_audio_ififo, HHI_GCLK_MPEG1, 11); +static MESON_GATE(g12a_adc, HHI_GCLK_MPEG1, 13); +static MESON_GATE(g12a_uart1, HHI_GCLK_MPEG1, 16); +static MESON_GATE(g12a_g2d, HHI_GCLK_MPEG1, 20); +static MESON_GATE(g12a_reset, HHI_GCLK_MPEG1, 23); +static MESON_GATE(g12a_pcie_comb, HHI_GCLK_MPEG1, 24); +static MESON_GATE(g12a_parser, HHI_GCLK_MPEG1, 25); +static MESON_GATE(g12a_usb_general, HHI_GCLK_MPEG1, 26); +static MESON_GATE(g12a_pcie_phy, HHI_GCLK_MPEG1, 27); +static MESON_GATE(g12a_ahb_arb0, HHI_GCLK_MPEG1, 29); + +static MESON_GATE(g12a_ahb_data_bus, HHI_GCLK_MPEG2, 1); +static MESON_GATE(g12a_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2); +static MESON_GATE(g12a_htx_hdcp22, HHI_GCLK_MPEG2, 3); +static MESON_GATE(g12a_htx_pclk, HHI_GCLK_MPEG2, 4); +static MESON_GATE(g12a_bt656, HHI_GCLK_MPEG2, 6); +static MESON_GATE(g12a_usb1_to_ddr, HHI_GCLK_MPEG2, 8); +static MESON_GATE(g12a_mmc_pclk, HHI_GCLK_MPEG2, 11); +static MESON_GATE(g12a_uart2, HHI_GCLK_MPEG2, 15); +static MESON_GATE(g12a_vpu_intr, HHI_GCLK_MPEG2, 25); +static MESON_GATE(g12a_gic, HHI_GCLK_MPEG2, 30); + +static MESON_GATE(g12a_vclk2_venci0, HHI_GCLK_OTHER, 1); +static MESON_GATE(g12a_vclk2_venci1, HHI_GCLK_OTHER, 2); +static MESON_GATE(g12a_vclk2_vencp0, HHI_GCLK_OTHER, 3); +static MESON_GATE(g12a_vclk2_vencp1, HHI_GCLK_OTHER, 4); +static MESON_GATE(g12a_vclk2_venct0, HHI_GCLK_OTHER, 5); +static MESON_GATE(g12a_vclk2_venct1, HHI_GCLK_OTHER, 6); +static MESON_GATE(g12a_vclk2_other, HHI_GCLK_OTHER, 7); +static MESON_GATE(g12a_vclk2_enci, HHI_GCLK_OTHER, 8); +static MESON_GATE(g12a_vclk2_encp, HHI_GCLK_OTHER, 9); +static MESON_GATE(g12a_dac_clk, HHI_GCLK_OTHER, 10); +static MESON_GATE(g12a_aoclk_gate, HHI_GCLK_OTHER, 14); +static MESON_GATE(g12a_iec958_gate, HHI_GCLK_OTHER, 16); +static MESON_GATE(g12a_enc480p, HHI_GCLK_OTHER, 20); +static MESON_GATE(g12a_rng1, HHI_GCLK_OTHER, 21); +static MESON_GATE(g12a_vclk2_enct, HHI_GCLK_OTHER, 22); +static MESON_GATE(g12a_vclk2_encl, HHI_GCLK_OTHER, 23); +static MESON_GATE(g12a_vclk2_venclmmc, HHI_GCLK_OTHER, 24); +static MESON_GATE(g12a_vclk2_vencl, HHI_GCLK_OTHER, 25); +static MESON_GATE(g12a_vclk2_other1, HHI_GCLK_OTHER, 26); + +static MESON_GATE_RO(g12a_dma, HHI_GCLK_OTHER2, 0); +static MESON_GATE_RO(g12a_efuse, HHI_GCLK_OTHER2, 1); +static MESON_GATE_RO(g12a_rom_boot, HHI_GCLK_OTHER2, 2); +static MESON_GATE_RO(g12a_reset_sec, HHI_GCLK_OTHER2, 3); +static MESON_GATE_RO(g12a_sec_ahb_apb3, HHI_GCLK_OTHER2, 4); + +/* Array of all clocks provided by this provider */ +static struct clk_hw_onecell_data g12a_hw_onecell_data = { + .hws = { + [CLKID_SYS_PLL] = &g12a_sys_pll.hw, + [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw, + [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw, + [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw, + [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw, + [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw, + [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw, + [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw, + [CLKID_GP0_PLL] = &g12a_gp0_pll.hw, + [CLKID_MPEG_SEL] = &g12a_mpeg_clk_sel.hw, + [CLKID_MPEG_DIV] = &g12a_mpeg_clk_div.hw, + [CLKID_CLK81] = &g12a_clk81.hw, + [CLKID_MPLL0] = &g12a_mpll0.hw, + [CLKID_MPLL1] = &g12a_mpll1.hw, + [CLKID_MPLL2] = &g12a_mpll2.hw, + [CLKID_MPLL3] = &g12a_mpll3.hw, + [CLKID_DDR] = &g12a_ddr.hw, + [CLKID_DOS] = &g12a_dos.hw, + [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw, + [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw, + [CLKID_ETH_PHY] = &g12a_eth_phy.hw, + [CLKID_ISA] = &g12a_isa.hw, + [CLKID_PL301] = &g12a_pl301.hw, + [CLKID_PERIPHS] = &g12a_periphs.hw, + [CLKID_SPICC0] = &g12a_spicc_0.hw, + [CLKID_I2C] = &g12a_i2c.hw, + [CLKID_SANA] = &g12a_sana.hw, + [CLKID_SD] = &g12a_sd.hw, + [CLKID_RNG0] = &g12a_rng0.hw, + [CLKID_UART0] = &g12a_uart0.hw, + [CLKID_SPICC1] = &g12a_spicc_1.hw, + [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw, + [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw, + [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw, + [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw, + [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw, + [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw, + [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw, + [CLKID_AUDIO] = &g12a_audio.hw, + [CLKID_ETH] = &g12a_eth_core.hw, + [CLKID_DEMUX] = &g12a_demux.hw, + [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw, + [CLKID_ADC] = &g12a_adc.hw, + [CLKID_UART1] = &g12a_uart1.hw, + [CLKID_G2D] = &g12a_g2d.hw, + [CLKID_RESET] = &g12a_reset.hw, + [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw, + [CLKID_PARSER] = &g12a_parser.hw, + [CLKID_USB] = &g12a_usb_general.hw, + [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw, + [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw, + [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw, + [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw, + [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw, + [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw, + [CLKID_BT656] = &g12a_bt656.hw, + [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw, + [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw, + [CLKID_UART2] = &g12a_uart2.hw, + [CLKID_VPU_INTR] = &g12a_vpu_intr.hw, + [CLKID_GIC] = &g12a_gic.hw, + [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw, + [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw, + [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw, + [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw, + [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw, + [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw, + [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw, + [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw, + [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw, + [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw, + [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw, + [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw, + [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw, + [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw, + [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw, + [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw, + [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw, + [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw, + [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw, + [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw, + [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw, + [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw, + [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw, + [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw, + [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw, + [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw, + [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw, + [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw, + [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw, + [CLKID_DAC_CLK] = &g12a_dac_clk.hw, + [CLKID_AOCLK] = &g12a_aoclk_gate.hw, + [CLKID_IEC958] = &g12a_iec958_gate.hw, + [CLKID_ENC480P] = &g12a_enc480p.hw, + [CLKID_RNG1] = &g12a_rng1.hw, + [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw, + [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw, + [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw, + [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw, + [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw, + [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw, + [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw, + [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw, + [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw, + [CLKID_DMA] = &g12a_dma.hw, + [CLKID_EFUSE] = &g12a_efuse.hw, + [CLKID_ROM_BOOT] = &g12a_rom_boot.hw, + [CLKID_RESET_SEC] = &g12a_reset_sec.hw, + [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw, + [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw, + [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw, + [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw, + [CLKID_VPU_0] = &g12a_vpu_0.hw, + [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw, + [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw, + [CLKID_VPU_1] = &g12a_vpu_1.hw, + [CLKID_VPU] = &g12a_vpu.hw, + [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw, + [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw, + [CLKID_VAPB_0] = &g12a_vapb_0.hw, + [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw, + [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw, + [CLKID_VAPB_1] = &g12a_vapb_1.hw, + [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw, + [CLKID_VAPB] = &g12a_vapb.hw, + [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw, + [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw, + [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw, + [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw, + [CLKID_VID_PLL] = &g12a_vid_pll_div.hw, + [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw, + [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw, + [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw, + [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw, + [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw, + [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw, + [CLKID_VCLK_DIV] = &g12a_vclk_div.hw, + [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw, + [CLKID_VCLK] = &g12a_vclk.hw, + [CLKID_VCLK2] = &g12a_vclk2.hw, + [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw, + [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw, + [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw, + [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw, + [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw, + [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw, + [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw, + [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw, + [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw, + [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw, + [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw, + [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw, + [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw, + [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw, + [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw, + [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw, + [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw, + [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw, + [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw, + [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw, + [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw, + [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw, + [CLKID_CTS_ENCI] = &g12a_cts_enci.hw, + [CLKID_CTS_ENCP] = &g12a_cts_encp.hw, + [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw, + [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw, + [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw, + [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw, + [CLKID_HDMI] = &g12a_hdmi.hw, + [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw, + [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw, + [CLKID_MALI_0] = &g12a_mali_0.hw, + [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw, + [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw, + [CLKID_MALI_1] = &g12a_mali_1.hw, + [CLKID_MALI] = &g12a_mali.hw, + [CLKID_MPLL_5OM_DIV] = &g12a_mpll_50m_div.hw, + [CLKID_MPLL_5OM] = &g12a_mpll_50m.hw, + [NR_CLKS] = NULL, + }, + .num = NR_CLKS, +}; + +/* Convenience table to populate regmap in .probe */ +static struct clk_regmap *const g12a_clk_regmaps[] = { + &g12a_clk81, + &g12a_dos, + &g12a_ddr, + &g12a_audio_locker, + &g12a_mipi_dsi_host, + &g12a_eth_phy, + &g12a_isa, + &g12a_pl301, + &g12a_periphs, + &g12a_spicc_0, + &g12a_i2c, + &g12a_sana, + &g12a_sd, + &g12a_rng0, + &g12a_uart0, + &g12a_spicc_1, + &g12a_hiu_reg, + &g12a_mipi_dsi_phy, + &g12a_assist_misc, + &g12a_emmc_a, + &g12a_emmc_b, + &g12a_emmc_c, + &g12a_audio_codec, + &g12a_audio, + &g12a_eth_core, + &g12a_demux, + &g12a_audio_ififo, + &g12a_adc, + &g12a_uart1, + &g12a_g2d, + &g12a_reset, + &g12a_pcie_comb, + &g12a_parser, + &g12a_usb_general, + &g12a_pcie_phy, + &g12a_ahb_arb0, + &g12a_ahb_data_bus, + &g12a_ahb_ctrl_bus, + &g12a_htx_hdcp22, + &g12a_htx_pclk, + &g12a_bt656, + &g12a_usb1_to_ddr, + &g12a_mmc_pclk, + &g12a_vpu_intr, + &g12a_gic, + &g12a_sd_emmc_a_clk0, + &g12a_sd_emmc_b_clk0, + &g12a_sd_emmc_c_clk0, + &g12a_mpeg_clk_div, + &g12a_sd_emmc_a_clk0_div, + &g12a_sd_emmc_b_clk0_div, + &g12a_sd_emmc_c_clk0_div, + &g12a_mpeg_clk_sel, + &g12a_sd_emmc_a_clk0_sel, + &g12a_sd_emmc_b_clk0_sel, + &g12a_sd_emmc_c_clk0_sel, + &g12a_mpll0, + &g12a_mpll1, + &g12a_mpll2, + &g12a_mpll3, + &g12a_mpll0_div, + &g12a_mpll1_div, + &g12a_mpll2_div, + &g12a_mpll3_div, + &g12a_fixed_pll, + &g12a_sys_pll, + &g12a_gp0_pll, + &g12a_hifi_pll, + &g12a_vclk2_venci0, + &g12a_vclk2_venci1, + &g12a_vclk2_vencp0, + &g12a_vclk2_vencp1, + &g12a_vclk2_venct0, + &g12a_vclk2_venct1, + &g12a_vclk2_other, + &g12a_vclk2_enci, + &g12a_vclk2_encp, + &g12a_dac_clk, + &g12a_aoclk_gate, + &g12a_iec958_gate, + &g12a_enc480p, + &g12a_rng1, + &g12a_vclk2_enct, + &g12a_vclk2_encl, + &g12a_vclk2_venclmmc, + &g12a_vclk2_vencl, + &g12a_vclk2_other1, + &g12a_fixed_pll_dco, + &g12a_sys_pll_dco, + &g12a_gp0_pll_dco, + &g12a_hifi_pll_dco, + &g12a_fclk_div2, + &g12a_fclk_div3, + &g12a_fclk_div4, + &g12a_fclk_div5, + &g12a_fclk_div7, + &g12a_fclk_div2p5, + &g12a_dma, + &g12a_efuse, + &g12a_rom_boot, + &g12a_reset_sec, + &g12a_sec_ahb_apb3, + &g12a_vpu_0_sel, + &g12a_vpu_0_div, + &g12a_vpu_0, + &g12a_vpu_1_sel, + &g12a_vpu_1_div, + &g12a_vpu_1, + &g12a_vpu, + &g12a_vapb_0_sel, + &g12a_vapb_0_div, + &g12a_vapb_0, + &g12a_vapb_1_sel, + &g12a_vapb_1_div, + &g12a_vapb_1, + &g12a_vapb_sel, + &g12a_vapb, + &g12a_hdmi_pll_dco, + &g12a_hdmi_pll_od, + &g12a_hdmi_pll_od2, + &g12a_hdmi_pll, + &g12a_vid_pll_div, + &g12a_vid_pll_sel, + &g12a_vid_pll, + &g12a_vclk_sel, + &g12a_vclk2_sel, + &g12a_vclk_input, + &g12a_vclk2_input, + &g12a_vclk_div, + &g12a_vclk2_div, + &g12a_vclk, + &g12a_vclk2, + &g12a_vclk_div1, + &g12a_vclk_div2_en, + &g12a_vclk_div4_en, + &g12a_vclk_div6_en, + &g12a_vclk_div12_en, + &g12a_vclk2_div1, + &g12a_vclk2_div2_en, + &g12a_vclk2_div4_en, + &g12a_vclk2_div6_en, + &g12a_vclk2_div12_en, + &g12a_cts_enci_sel, + &g12a_cts_encp_sel, + &g12a_cts_vdac_sel, + &g12a_hdmi_tx_sel, + &g12a_cts_enci, + &g12a_cts_encp, + &g12a_cts_vdac, + &g12a_hdmi_tx, + &g12a_hdmi_sel, + &g12a_hdmi_div, + &g12a_hdmi, + &g12a_mali_0_sel, + &g12a_mali_0_div, + &g12a_mali_0, + &g12a_mali_1_sel, + &g12a_mali_1_div, + &g12a_mali_1, + &g12a_mali, + &g12a_mpll_50m, +}; + +static const struct meson_eeclkc_data g12a_clkc_data = { + .regmap_clks = g12a_clk_regmaps, + .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps), + .hw_onecell_data = &g12a_hw_onecell_data +}; + +static const struct of_device_id clkc_match_table[] = { + { .compatible = "amlogic,g12a-clkc", .data = &g12a_clkc_data }, + {} +}; + +static struct platform_driver g12a_driver = { + .probe = meson_eeclkc_probe, + .driver = { + .name = "g12a-clkc", + .of_match_table = clkc_match_table, + }, +}; + +builtin_platform_driver(g12a_driver); diff --git a/drivers/clk/meson/g12a.h b/drivers/clk/meson/g12a.h new file mode 100644 index 000000000000..f399dfe1401c --- /dev/null +++ b/drivers/clk/meson/g12a.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2016 Amlogic, Inc. + * Author: Michael Turquette <mturquette@baylibre.com> + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Qiufang Dai <qiufang.dai@amlogic.com> + * Author: Jian Hu <jian.hu@amlogic.com> + * + */ +#ifndef __G12A_H +#define __G12A_H + +/* + * Clock controller register offsets + * + * Register offsets from the data sheet must be multiplied by 4 before + * adding them to the base address to get the right value. + */ +#define HHI_MIPI_CNTL0 0x000 +#define HHI_MIPI_CNTL1 0x004 +#define HHI_MIPI_CNTL2 0x008 +#define HHI_MIPI_STS 0x00C +#define HHI_GP0_PLL_CNTL0 0x040 +#define HHI_GP0_PLL_CNTL1 0x044 +#define HHI_GP0_PLL_CNTL2 0x048 +#define HHI_GP0_PLL_CNTL3 0x04C +#define HHI_GP0_PLL_CNTL4 0x050 +#define HHI_GP0_PLL_CNTL5 0x054 +#define HHI_GP0_PLL_CNTL6 0x058 +#define HHI_GP0_PLL_STS 0x05C +#define HHI_PCIE_PLL_CNTL0 0x098 +#define HHI_PCIE_PLL_CNTL1 0x09C +#define HHI_PCIE_PLL_CNTL2 0x0A0 +#define HHI_PCIE_PLL_CNTL3 0x0A4 +#define HHI_PCIE_PLL_CNTL4 0x0A8 +#define HHI_PCIE_PLL_CNTL5 0x0AC +#define HHI_PCIE_PLL_STS 0x0B8 +#define HHI_HIFI_PLL_CNTL0 0x0D8 +#define HHI_HIFI_PLL_CNTL1 0x0DC +#define HHI_HIFI_PLL_CNTL2 0x0E0 +#define HHI_HIFI_PLL_CNTL3 0x0E4 +#define HHI_HIFI_PLL_CNTL4 0x0E8 +#define HHI_HIFI_PLL_CNTL5 0x0EC +#define HHI_HIFI_PLL_CNTL6 0x0F0 +#define HHI_VIID_CLK_DIV 0x128 +#define HHI_VIID_CLK_CNTL 0x12C +#define HHI_GCLK_MPEG0 0x140 +#define HHI_GCLK_MPEG1 0x144 +#define HHI_GCLK_MPEG2 0x148 +#define HHI_GCLK_OTHER 0x150 +#define HHI_GCLK_OTHER2 0x154 +#define HHI_VID_CLK_DIV 0x164 +#define HHI_MPEG_CLK_CNTL 0x174 +#define HHI_AUD_CLK_CNTL 0x178 +#define HHI_VID_CLK_CNTL 0x17c +#define HHI_TS_CLK_CNTL 0x190 +#define HHI_VID_CLK_CNTL2 0x194 +#define HHI_SYS_CPU_CLK_CNTL0 0x19c +#define HHI_VID_PLL_CLK_DIV 0x1A0 +#define HHI_MALI_CLK_CNTL 0x1b0 +#define HHI_VPU_CLKC_CNTL 0x1b4 +#define HHI_VPU_CLK_CNTL 0x1bC +#define HHI_HDMI_CLK_CNTL 0x1CC +#define HHI_VDEC_CLK_CNTL 0x1E0 +#define HHI_VDEC2_CLK_CNTL 0x1E4 +#define HHI_VDEC3_CLK_CNTL 0x1E8 +#define HHI_VDEC4_CLK_CNTL 0x1EC +#define HHI_HDCP22_CLK_CNTL 0x1F0 +#define HHI_VAPBCLK_CNTL 0x1F4 +#define HHI_VPU_CLKB_CNTL 0x20C +#define HHI_GEN_CLK_CNTL 0x228 +#define HHI_VDIN_MEAS_CLK_CNTL 0x250 +#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254 +#define HHI_NAND_CLK_CNTL 0x25C +#define HHI_SD_EMMC_CLK_CNTL 0x264 +#define HHI_MPLL_CNTL0 0x278 +#define HHI_MPLL_CNTL1 0x27C +#define HHI_MPLL_CNTL2 0x280 +#define HHI_MPLL_CNTL3 0x284 +#define HHI_MPLL_CNTL4 0x288 +#define HHI_MPLL_CNTL5 0x28c +#define HHI_MPLL_CNTL6 0x290 +#define HHI_MPLL_CNTL7 0x294 +#define HHI_MPLL_CNTL8 0x298 +#define HHI_FIX_PLL_CNTL0 0x2A0 +#define HHI_FIX_PLL_CNTL1 0x2A4 +#define HHI_FIX_PLL_CNTL3 0x2AC +#define HHI_SYS_PLL_CNTL0 0x2f4 +#define HHI_SYS_PLL_CNTL1 0x2f8 +#define HHI_SYS_PLL_CNTL2 0x2fc +#define HHI_SYS_PLL_CNTL3 0x300 +#define HHI_SYS_PLL_CNTL4 0x304 +#define HHI_SYS_PLL_CNTL5 0x308 +#define HHI_SYS_PLL_CNTL6 0x30c +#define HHI_HDMI_PLL_CNTL0 0x320 +#define HHI_HDMI_PLL_CNTL1 0x324 +#define HHI_HDMI_PLL_CNTL2 0x328 +#define HHI_HDMI_PLL_CNTL3 0x32c +#define HHI_HDMI_PLL_CNTL4 0x330 +#define HHI_HDMI_PLL_CNTL5 0x334 +#define HHI_HDMI_PLL_CNTL6 0x338 +#define HHI_SPICC_CLK_CNTL 0x3dc + +/* + * CLKID index values + * + * These indices are entirely contrived and do not map onto the hardware. + * It has now been decided to expose everything by default in the DT header: + * include/dt-bindings/clock/g12a-clkc.h. Only the clocks ids we don't want + * to expose, such as the internal muxes and dividers of composite clocks, + * will remain defined here. + */ +#define CLKID_MPEG_SEL 8 +#define CLKID_MPEG_DIV 9 +#define CLKID_SD_EMMC_A_CLK0_SEL 63 +#define CLKID_SD_EMMC_A_CLK0_DIV 64 +#define CLKID_SD_EMMC_B_CLK0_SEL 65 +#define CLKID_SD_EMMC_B_CLK0_DIV 66 +#define CLKID_SD_EMMC_C_CLK0_SEL 67 +#define CLKID_SD_EMMC_C_CLK0_DIV 68 +#define CLKID_MPLL0_DIV 69 +#define CLKID_MPLL1_DIV 70 +#define CLKID_MPLL2_DIV 71 +#define CLKID_MPLL3_DIV 72 +#define CLKID_MPLL_PREDIV 73 +#define CLKID_FCLK_DIV2_DIV 75 +#define CLKID_FCLK_DIV3_DIV 76 +#define CLKID_FCLK_DIV4_DIV 77 +#define CLKID_FCLK_DIV5_DIV 78 +#define CLKID_FCLK_DIV7_DIV 79 +#define CLKID_FCLK_DIV2P5_DIV 100 +#define CLKID_FIXED_PLL_DCO 101 +#define CLKID_SYS_PLL_DCO 102 +#define CLKID_GP0_PLL_DCO 103 +#define CLKID_HIFI_PLL_DCO 104 +#define CLKID_VPU_0_DIV 111 +#define CLKID_VPU_1_DIV 114 +#define CLKID_VAPB_0_DIV 118 +#define CLKID_VAPB_1_DIV 121 +#define CLKID_HDMI_PLL_DCO 125 +#define CLKID_HDMI_PLL_OD 126 +#define CLKID_HDMI_PLL_OD2 127 +#define CLKID_VID_PLL_SEL 130 +#define CLKID_VID_PLL_DIV 131 +#define CLKID_VCLK_SEL 132 +#define CLKID_VCLK2_SEL 133 +#define CLKID_VCLK_INPUT 134 +#define CLKID_VCLK2_INPUT 135 +#define CLKID_VCLK_DIV 136 +#define CLKID_VCLK2_DIV 137 +#define CLKID_VCLK_DIV2_EN 140 +#define CLKID_VCLK_DIV4_EN 141 +#define CLKID_VCLK_DIV6_EN 142 +#define CLKID_VCLK_DIV12_EN 143 +#define CLKID_VCLK2_DIV2_EN 144 +#define CLKID_VCLK2_DIV4_EN 145 +#define CLKID_VCLK2_DIV6_EN 146 +#define CLKID_VCLK2_DIV12_EN 147 +#define CLKID_CTS_ENCI_SEL 158 +#define CLKID_CTS_ENCP_SEL 159 +#define CLKID_CTS_VDAC_SEL 160 +#define CLKID_HDMI_TX_SEL 161 +#define CLKID_HDMI_SEL 166 +#define CLKID_HDMI_DIV 167 +#define CLKID_MALI_0_DIV 170 +#define CLKID_MALI_1_DIV 173 +#define CLKID_MPLL_5OM_DIV 176 + +#define NR_CLKS 178 + +/* include the CLKIDs that have been made part of the DT binding */ +#include <dt-bindings/clock/g12a-clkc.h> + +#endif /* __G12A_H */ diff --git a/drivers/clk/meson/gxbb-aoclk-32k.c b/drivers/clk/meson/gxbb-aoclk-32k.c deleted file mode 100644 index 680467141a1d..000000000000 --- a/drivers/clk/meson/gxbb-aoclk-32k.c +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2017 BayLibre, SAS. - * Author: Neil Armstrong <narmstrong@baylibre.com> - */ - -#include <linux/clk-provider.h> -#include <linux/bitfield.h> -#include <linux/regmap.h> -#include "gxbb-aoclk.h" - -/* - * The AO Domain embeds a dual/divider to generate a more precise - * 32,768KHz clock for low-power suspend mode and CEC. - * ______ ______ - * | | | | - * ______ | Div1 |-| Cnt1 | ______ - * | | /|______| |______|\ | | - * Xtal-->| Gate |---| ______ ______ X-X--| Gate |--> - * |______| | \| | | |/ | |______| - * | | Div2 |-| Cnt2 | | - * | |______| |______| | - * |_______________________| - * - * The dividing can be switched to single or dual, with a counter - * for each divider to set when the switching is done. - * The entire dividing mechanism can be also bypassed. - */ - -#define CLK_CNTL0_N1_MASK GENMASK(11, 0) -#define CLK_CNTL0_N2_MASK GENMASK(23, 12) -#define CLK_CNTL0_DUALDIV_EN BIT(28) -#define CLK_CNTL0_OUT_GATE_EN BIT(30) -#define CLK_CNTL0_IN_GATE_EN BIT(31) - -#define CLK_CNTL1_M1_MASK GENMASK(11, 0) -#define CLK_CNTL1_M2_MASK GENMASK(23, 12) -#define CLK_CNTL1_BYPASS_EN BIT(24) -#define CLK_CNTL1_SELECT_OSC BIT(27) - -#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10) - -struct cec_32k_freq_table { - unsigned long parent_rate; - unsigned long target_rate; - bool dualdiv; - unsigned int n1; - unsigned int n2; - unsigned int m1; - unsigned int m2; -}; - -static const struct cec_32k_freq_table aoclk_cec_32k_table[] = { - [0] = { - .parent_rate = 24000000, - .target_rate = 32768, - .dualdiv = true, - .n1 = 733, - .n2 = 732, - .m1 = 8, - .m2 = 11, - }, -}; - -/* - * If CLK_CNTL0_DUALDIV_EN == 0 - * - will use N1 divider only - * If CLK_CNTL0_DUALDIV_EN == 1 - * - hold M1 cycles of N1 divider then changes to N2 - * - hold M2 cycles of N2 divider then changes to N1 - * Then we can get more accurate division. - */ -static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); - unsigned long n1; - u32 reg0, reg1; - - regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, ®0); - regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, ®1); - - if (reg1 & CLK_CNTL1_BYPASS_EN) - return parent_rate; - - if (reg0 & CLK_CNTL0_DUALDIV_EN) { - unsigned long n2, m1, m2, f1, f2, p1, p2; - - n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; - n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1; - - m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1; - m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1; - - f1 = DIV_ROUND_CLOSEST(parent_rate, n1); - f2 = DIV_ROUND_CLOSEST(parent_rate, n2); - - p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2)); - p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2)); - - return DIV_ROUND_UP(100000000, p1 + p2); - } - - n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; - - return DIV_ROUND_CLOSEST(parent_rate, n1); -} - -static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate, - unsigned long prate) -{ - int i; - - for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i) - if (aoclk_cec_32k_table[i].parent_rate == prate && - aoclk_cec_32k_table[i].target_rate == rate) - return &aoclk_cec_32k_table[i]; - - return NULL; -} - -static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) -{ - const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, - *prate); - - /* If invalid return first one */ - if (!freq) - return aoclk_cec_32k_table[0].target_rate; - - return freq->target_rate; -} - -/* - * From the Amlogic init procedure, the IN and OUT gates needs to be handled - * in the init procedure to avoid any glitches. - */ - -static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, - parent_rate); - struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); - u32 reg = 0; - - if (!freq) - return -EINVAL; - - /* Disable clock */ - regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, - CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0); - - reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1); - if (freq->dualdiv) - reg |= CLK_CNTL0_DUALDIV_EN | - FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1); - - regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg); - - reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1); - if (freq->dualdiv) - reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1); - - regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg); - - /* Enable clock */ - regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, - CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN); - - udelay(200); - - regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, - CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN); - - regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1, - CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC); - - /* Select 32k from XTAL */ - regmap_update_bits(cec_32k->regmap, - AO_RTI_PWR_CNTL_REG0, - PWR_CNTL_ALT_32K_SEL, - FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4)); - - return 0; -} - -const struct clk_ops meson_aoclk_cec_32k_ops = { - .recalc_rate = aoclk_cec_32k_recalc_rate, - .round_rate = aoclk_cec_32k_round_rate, - .set_rate = aoclk_cec_32k_set_rate, -}; diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index 42ed61d3c3fb..449f6ac189d8 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -5,10 +5,23 @@ */ #include <linux/platform_device.h> #include <linux/mfd/syscon.h> -#include "clk-regmap.h" #include "meson-aoclk.h" #include "gxbb-aoclk.h" +#include "clk-regmap.h" +#include "clk-dualdiv.h" + +#define IN_PREFIX "ao-in-" + +/* AO Configuration Clock registers offsets */ +#define AO_RTI_PWR_CNTL_REG1 0x0c +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_OSCIN_CNTL 0x58 +#define AO_CRT_CLK_CNTL1 0x68 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + #define GXBB_AO_GATE(_name, _bit) \ static struct clk_regmap _name##_ao = { \ .data = &(struct clk_regmap_gate_data) { \ @@ -18,7 +31,7 @@ static struct clk_regmap _name##_ao = { \ .hw.init = &(struct clk_init_data) { \ .name = #_name "_ao", \ .ops = &clk_regmap_gate_ops, \ - .parent_names = (const char *[]){ "clk81" }, \ + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \ .num_parents = 1, \ .flags = CLK_IGNORE_UNUSED, \ }, \ @@ -31,13 +44,174 @@ GXBB_AO_GATE(uart1, 3); GXBB_AO_GATE(uart2, 5); GXBB_AO_GATE(ir_blaster, 6); -static struct aoclk_cec_32k cec_32k_ao = { - .hw.init = &(struct clk_init_data) { - .name = "cec_32k_ao", - .ops = &meson_aoclk_cec_32k_ops, - .parent_names = (const char *[]){ "xtal" }, +static struct clk_regmap ao_cts_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTI_PWR_CNTL_REG0, + .bit_idx = 6, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_cts_oscin", + .ops = &clk_regmap_gate_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap ao_32k_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "ao_cts_oscin" }, + .num_parents = 1, + }, +}; + +static const struct meson_clk_dualdiv_param gxbb_32k_div_table[] = { + { + .dual = 1, + .n1 = 733, + .m1 = 8, + .n2 = 732, + .m2 = 11, + }, {} +}; + +static struct clk_regmap ao_32k_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 28, + .width = 1, + }, + .table = gxbb_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "ao_32k_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap ao_32k_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTC_ALT_CLK_CNTL1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "ao_32k_div", + "ao_32k_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_32k = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "ao_32k_sel" }, .num_parents = 1, - .flags = CLK_IGNORE_UNUSED, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_cts_rtc_oscin = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x7, + .shift = 10, + .table = (u32[]){ 1, 2, 3, 4 }, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_cts_rtc_oscin", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ IN_PREFIX "ext-32k-0", + IN_PREFIX "ext-32k-1", + IN_PREFIX "ext-32k-2", + "ao_32k" }, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_clk81 = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 0, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_clk81", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk", + "ao_cts_rtc_oscin" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_cts_cec = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_CRT_CLK_CNTL1, + .mask = 0x1, + .shift = 27, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_cts_cec", + .ops = &clk_regmap_mux_ops, + /* + * FIXME: The 'fixme' parent obviously does not exist. + * + * ATM, CCF won't call get_parent() if num_parents is 1. It + * does not allow NULL as a parent name either. + * + * On this particular mux, we only know the input #1 parent + * but, on boot, unknown input #0 is set, so it is critical + * to call .get_parent() on it + * + * Until CCF gets fixed, adding this fake parent that won't + * ever be registered should work around the problem + */ + .parent_names = (const char *[]){ "fixme", + "ao_cts_rtc_oscin" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -50,13 +224,21 @@ static const unsigned int gxbb_aoclk_reset[] = { [RESET_AO_IR_BLASTER] = 23, }; -static struct clk_regmap *gxbb_aoclk_gate[] = { - [CLKID_AO_REMOTE] = &remote_ao, - [CLKID_AO_I2C_MASTER] = &i2c_master_ao, - [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao, - [CLKID_AO_UART1] = &uart1_ao, - [CLKID_AO_UART2] = &uart2_ao, - [CLKID_AO_IR_BLASTER] = &ir_blaster_ao, +static struct clk_regmap *gxbb_aoclk[] = { + &remote_ao, + &i2c_master_ao, + &i2c_slave_ao, + &uart1_ao, + &uart2_ao, + &ir_blaster_ao, + &ao_cts_oscin, + &ao_32k_pre, + &ao_32k_div, + &ao_32k_sel, + &ao_32k, + &ao_cts_rtc_oscin, + &ao_clk81, + &ao_cts_cec, }; static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { @@ -67,52 +249,38 @@ static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { [CLKID_AO_UART1] = &uart1_ao.hw, [CLKID_AO_UART2] = &uart2_ao.hw, [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw, - [CLKID_AO_CEC_32K] = &cec_32k_ao.hw, + [CLKID_AO_CEC_32K] = &ao_cts_cec.hw, + [CLKID_AO_CTS_OSCIN] = &ao_cts_oscin.hw, + [CLKID_AO_32K_PRE] = &ao_32k_pre.hw, + [CLKID_AO_32K_DIV] = &ao_32k_div.hw, + [CLKID_AO_32K_SEL] = &ao_32k_sel.hw, + [CLKID_AO_32K] = &ao_32k.hw, + [CLKID_AO_CTS_RTC_OSCIN] = &ao_cts_rtc_oscin.hw, + [CLKID_AO_CLK81] = &ao_clk81.hw, }, .num = NR_CLKS, }; -static int gxbb_register_cec_ao_32k(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct regmap *regmap; - int ret; - - regmap = syscon_node_to_regmap(of_get_parent(dev->of_node)); - if (IS_ERR(regmap)) { - dev_err(dev, "failed to get regmap\n"); - return PTR_ERR(regmap); - } - - /* Specific clocks */ - cec_32k_ao.regmap = regmap; - ret = devm_clk_hw_register(dev, &cec_32k_ao.hw); - if (ret) { - dev_err(&pdev->dev, "clk cec_32k_ao register failed.\n"); - return ret; - } - - return 0; -} +static const struct meson_aoclk_input gxbb_aoclk_inputs[] = { + { .name = "xtal", .required = true, }, + { .name = "mpeg-clk", .required = true, }, + {. name = "ext-32k-0", .required = false, }, + {. name = "ext-32k-1", .required = false, }, + {. name = "ext-32k-2", .required = false, }, +}; static const struct meson_aoclk_data gxbb_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(gxbb_aoclk_reset), .reset = gxbb_aoclk_reset, - .num_clks = ARRAY_SIZE(gxbb_aoclk_gate), - .clks = gxbb_aoclk_gate, + .num_clks = ARRAY_SIZE(gxbb_aoclk), + .clks = gxbb_aoclk, .hw_data = &gxbb_aoclk_onecell_data, + .inputs = gxbb_aoclk_inputs, + .num_inputs = ARRAY_SIZE(gxbb_aoclk_inputs), + .input_prefix = IN_PREFIX, }; -static int gxbb_aoclkc_probe(struct platform_device *pdev) -{ - int ret = gxbb_register_cec_ao_32k(pdev); - if (ret) - return ret; - - return meson_aoclkc_probe(pdev); -} - static const struct of_device_id gxbb_aoclkc_match_table[] = { { .compatible = "amlogic,meson-gx-aoclkc", @@ -122,7 +290,7 @@ static const struct of_device_id gxbb_aoclkc_match_table[] = { }; static struct platform_driver gxbb_aoclkc_driver = { - .probe = gxbb_aoclkc_probe, + .probe = meson_aoclkc_probe, .driver = { .name = "gxbb-aoclkc", .of_match_table = gxbb_aoclkc_match_table, diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h index c514493d989a..1db16f9b37d4 100644 --- a/drivers/clk/meson/gxbb-aoclk.h +++ b/drivers/clk/meson/gxbb-aoclk.h @@ -7,25 +7,7 @@ #ifndef __GXBB_AOCLKC_H #define __GXBB_AOCLKC_H -#define NR_CLKS 7 - -/* AO Configuration Clock registers offsets */ -#define AO_RTI_PWR_CNTL_REG1 0x0c -#define AO_RTI_PWR_CNTL_REG0 0x10 -#define AO_RTI_GEN_CNTL_REG0 0x40 -#define AO_OSCIN_CNTL 0x58 -#define AO_CRT_CLK_CNTL1 0x68 -#define AO_RTC_ALT_CLK_CNTL0 0x94 -#define AO_RTC_ALT_CLK_CNTL1 0x98 - -struct aoclk_cec_32k { - struct clk_hw hw; - struct regmap *regmap; -}; - -#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw) - -extern const struct clk_ops meson_aoclk_cec_32k_ops; +#define NR_CLKS 14 #include <dt-bindings/clock/gxbb-aoclkc.h> #include <dt-bindings/reset/gxbb-aoclkc.h> diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 65f2599e5243..04df2e208ed6 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -4,17 +4,20 @@ * Michael Turquette <mturquette@baylibre.com> */ -#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/init.h> #include <linux/of_device.h> -#include <linux/mfd/syscon.h> #include <linux/platform_device.h> -#include <linux/regmap.h> -#include "clkc.h" #include "gxbb.h" +#include "clk-input.h" #include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-mpll.h" +#include "meson-eeclk.h" +#include "vid-pll-div.h" + +#define IN_PREFIX "ee-in-" static DEFINE_SPINLOCK(meson_clk_lock); @@ -118,7 +121,7 @@ static struct clk_regmap gxbb_fixed_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "fixed_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -148,7 +151,7 @@ static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = { .hw.init = &(struct clk_init_data){ .name = "hdmi_pll_pre_mult", .ops = &clk_fixed_factor_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -241,7 +244,7 @@ static struct clk_regmap gxl_hdmi_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "hdmi_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, /* * Display directly handle hdmi pll registers ATM, we need @@ -378,7 +381,7 @@ static struct clk_regmap gxbb_sys_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "sys_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -439,7 +442,7 @@ static struct clk_regmap gxbb_gp0_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "gp0_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -491,7 +494,7 @@ static struct clk_regmap gxl_gp0_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "gp0_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -789,7 +792,7 @@ static struct clk_regmap gxbb_mpll2 = { static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 }; static const char * const clk81_parent_names[] = { - "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", + IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", "fclk_div3", "fclk_div5" }; @@ -852,7 +855,7 @@ static struct clk_regmap gxbb_sar_adc_clk_sel = { .name = "sar_adc_clk_sel", .ops = &clk_regmap_mux_ops, /* NOTE: The datasheet doesn't list the parents for bit 10 */ - .parent_names = (const char *[]){ "xtal", "clk81", }, + .parent_names = (const char *[]){ IN_PREFIX "xtal", "clk81", }, .num_parents = 2, }, }; @@ -891,7 +894,7 @@ static struct clk_regmap gxbb_sar_adc_clk = { */ static const char * const gxbb_mali_0_1_parent_names[] = { - "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7", + IN_PREFIX "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7", "fclk_div4", "fclk_div3", "fclk_div5" }; @@ -1153,7 +1156,7 @@ static struct clk_regmap gxbb_32k_clk = { }; static const char * const gxbb_32k_clk_parent_names[] = { - "xtal", "cts_slow_oscin", "fclk_div3", "fclk_div5" + IN_PREFIX "xtal", "cts_slow_oscin", "fclk_div3", "fclk_div5" }; static struct clk_regmap gxbb_32k_clk_sel = { @@ -1172,7 +1175,7 @@ static struct clk_regmap gxbb_32k_clk_sel = { }; static const char * const gxbb_sd_emmc_clk0_parent_names[] = { - "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", /* * Following these parent clocks, we should also have had mpll2, mpll3 @@ -2138,7 +2141,7 @@ static struct clk_regmap gxbb_hdmi_tx = { /* HDMI Clocks */ static const char * const gxbb_hdmi_parent_names[] = { - "xtal", "fclk_div4", "fclk_div3", "fclk_div5" + IN_PREFIX "xtal", "fclk_div4", "fclk_div3", "fclk_div5" }; static struct clk_regmap gxbb_hdmi_sel = { @@ -2285,7 +2288,7 @@ static struct clk_regmap gxbb_vdec_hevc = { static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, }; static const char * const gen_clk_parent_names[] = { - "xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2", + IN_PREFIX "xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2", "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll", }; @@ -2854,6 +2857,192 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = { }; static struct clk_regmap *const gxbb_clk_regmaps[] = { + &gxbb_clk81, + &gxbb_ddr, + &gxbb_dos, + &gxbb_isa, + &gxbb_pl301, + &gxbb_periphs, + &gxbb_spicc, + &gxbb_i2c, + &gxbb_sar_adc, + &gxbb_smart_card, + &gxbb_rng0, + &gxbb_uart0, + &gxbb_sdhc, + &gxbb_stream, + &gxbb_async_fifo, + &gxbb_sdio, + &gxbb_abuf, + &gxbb_hiu_iface, + &gxbb_assist_misc, + &gxbb_spi, + &gxbb_i2s_spdif, + &gxbb_eth, + &gxbb_demux, + &gxbb_aiu_glue, + &gxbb_iec958, + &gxbb_i2s_out, + &gxbb_amclk, + &gxbb_aififo2, + &gxbb_mixer, + &gxbb_mixer_iface, + &gxbb_adc, + &gxbb_blkmv, + &gxbb_aiu, + &gxbb_uart1, + &gxbb_g2d, + &gxbb_usb0, + &gxbb_usb1, + &gxbb_reset, + &gxbb_nand, + &gxbb_dos_parser, + &gxbb_usb, + &gxbb_vdin1, + &gxbb_ahb_arb0, + &gxbb_efuse, + &gxbb_boot_rom, + &gxbb_ahb_data_bus, + &gxbb_ahb_ctrl_bus, + &gxbb_hdmi_intr_sync, + &gxbb_hdmi_pclk, + &gxbb_usb1_ddr_bridge, + &gxbb_usb0_ddr_bridge, + &gxbb_mmc_pclk, + &gxbb_dvin, + &gxbb_uart2, + &gxbb_sana, + &gxbb_vpu_intr, + &gxbb_sec_ahb_ahb3_bridge, + &gxbb_clk81_a53, + &gxbb_vclk2_venci0, + &gxbb_vclk2_venci1, + &gxbb_vclk2_vencp0, + &gxbb_vclk2_vencp1, + &gxbb_gclk_venci_int0, + &gxbb_gclk_vencp_int, + &gxbb_dac_clk, + &gxbb_aoclk_gate, + &gxbb_iec958_gate, + &gxbb_enc480p, + &gxbb_rng1, + &gxbb_gclk_venci_int1, + &gxbb_vclk2_venclmcc, + &gxbb_vclk2_vencl, + &gxbb_vclk_other, + &gxbb_edp, + &gxbb_ao_media_cpu, + &gxbb_ao_ahb_sram, + &gxbb_ao_ahb_bus, + &gxbb_ao_iface, + &gxbb_ao_i2c, + &gxbb_emmc_a, + &gxbb_emmc_b, + &gxbb_emmc_c, + &gxbb_sar_adc_clk, + &gxbb_mali_0, + &gxbb_mali_1, + &gxbb_cts_amclk, + &gxbb_cts_mclk_i958, + &gxbb_32k_clk, + &gxbb_sd_emmc_a_clk0, + &gxbb_sd_emmc_b_clk0, + &gxbb_sd_emmc_c_clk0, + &gxbb_vpu_0, + &gxbb_vpu_1, + &gxbb_vapb_0, + &gxbb_vapb_1, + &gxbb_vapb, + &gxbb_mpeg_clk_div, + &gxbb_sar_adc_clk_div, + &gxbb_mali_0_div, + &gxbb_mali_1_div, + &gxbb_cts_mclk_i958_div, + &gxbb_32k_clk_div, + &gxbb_sd_emmc_a_clk0_div, + &gxbb_sd_emmc_b_clk0_div, + &gxbb_sd_emmc_c_clk0_div, + &gxbb_vpu_0_div, + &gxbb_vpu_1_div, + &gxbb_vapb_0_div, + &gxbb_vapb_1_div, + &gxbb_mpeg_clk_sel, + &gxbb_sar_adc_clk_sel, + &gxbb_mali_0_sel, + &gxbb_mali_1_sel, + &gxbb_mali, + &gxbb_cts_amclk_sel, + &gxbb_cts_mclk_i958_sel, + &gxbb_cts_i958, + &gxbb_32k_clk_sel, + &gxbb_sd_emmc_a_clk0_sel, + &gxbb_sd_emmc_b_clk0_sel, + &gxbb_sd_emmc_c_clk0_sel, + &gxbb_vpu_0_sel, + &gxbb_vpu_1_sel, + &gxbb_vpu, + &gxbb_vapb_0_sel, + &gxbb_vapb_1_sel, + &gxbb_vapb_sel, + &gxbb_mpll0, + &gxbb_mpll1, + &gxbb_mpll2, + &gxbb_mpll0_div, + &gxbb_mpll1_div, + &gxbb_mpll2_div, + &gxbb_cts_amclk_div, + &gxbb_fixed_pll, + &gxbb_sys_pll, + &gxbb_mpll_prediv, + &gxbb_fclk_div2, + &gxbb_fclk_div3, + &gxbb_fclk_div4, + &gxbb_fclk_div5, + &gxbb_fclk_div7, + &gxbb_vdec_1_sel, + &gxbb_vdec_1_div, + &gxbb_vdec_1, + &gxbb_vdec_hevc_sel, + &gxbb_vdec_hevc_div, + &gxbb_vdec_hevc, + &gxbb_gen_clk_sel, + &gxbb_gen_clk_div, + &gxbb_gen_clk, + &gxbb_fixed_pll_dco, + &gxbb_sys_pll_dco, + &gxbb_gp0_pll, + &gxbb_vid_pll, + &gxbb_vid_pll_sel, + &gxbb_vid_pll_div, + &gxbb_vclk, + &gxbb_vclk_sel, + &gxbb_vclk_div, + &gxbb_vclk_input, + &gxbb_vclk_div1, + &gxbb_vclk_div2_en, + &gxbb_vclk_div4_en, + &gxbb_vclk_div6_en, + &gxbb_vclk_div12_en, + &gxbb_vclk2, + &gxbb_vclk2_sel, + &gxbb_vclk2_div, + &gxbb_vclk2_input, + &gxbb_vclk2_div1, + &gxbb_vclk2_div2_en, + &gxbb_vclk2_div4_en, + &gxbb_vclk2_div6_en, + &gxbb_vclk2_div12_en, + &gxbb_cts_enci, + &gxbb_cts_enci_sel, + &gxbb_cts_encp, + &gxbb_cts_encp_sel, + &gxbb_cts_vdac, + &gxbb_cts_vdac_sel, + &gxbb_hdmi_tx, + &gxbb_hdmi_tx_sel, + &gxbb_hdmi_sel, + &gxbb_hdmi_div, + &gxbb_hdmi, &gxbb_gp0_pll_dco, &gxbb_hdmi_pll, &gxbb_hdmi_pll_od, @@ -2862,14 +3051,6 @@ static struct clk_regmap *const gxbb_clk_regmaps[] = { }; static struct clk_regmap *const gxl_clk_regmaps[] = { - &gxl_gp0_pll_dco, - &gxl_hdmi_pll, - &gxl_hdmi_pll_od, - &gxl_hdmi_pll_od2, - &gxl_hdmi_pll_dco, -}; - -static struct clk_regmap *const gx_clk_regmaps[] = { &gxbb_clk81, &gxbb_ddr, &gxbb_dos, @@ -3056,23 +3237,22 @@ static struct clk_regmap *const gx_clk_regmaps[] = { &gxbb_hdmi_sel, &gxbb_hdmi_div, &gxbb_hdmi, + &gxl_gp0_pll_dco, + &gxl_hdmi_pll, + &gxl_hdmi_pll_od, + &gxl_hdmi_pll_od2, + &gxl_hdmi_pll_dco, }; -struct clkc_data { - struct clk_regmap *const *regmap_clks; - unsigned int regmap_clks_count; - struct clk_hw_onecell_data *hw_onecell_data; -}; - -static const struct clkc_data gxbb_clkc_data = { +static const struct meson_eeclkc_data gxbb_clkc_data = { .regmap_clks = gxbb_clk_regmaps, - .regmap_clks_count = ARRAY_SIZE(gxbb_clk_regmaps), + .regmap_clk_num = ARRAY_SIZE(gxbb_clk_regmaps), .hw_onecell_data = &gxbb_hw_onecell_data, }; -static const struct clkc_data gxl_clkc_data = { +static const struct meson_eeclkc_data gxl_clkc_data = { .regmap_clks = gxl_clk_regmaps, - .regmap_clks_count = ARRAY_SIZE(gxl_clk_regmaps), + .regmap_clk_num = ARRAY_SIZE(gxl_clk_regmaps), .hw_onecell_data = &gxl_hw_onecell_data, }; @@ -3082,52 +3262,8 @@ static const struct of_device_id clkc_match_table[] = { {}, }; -static int gxbb_clkc_probe(struct platform_device *pdev) -{ - const struct clkc_data *clkc_data; - struct regmap *map; - int ret, i; - struct device *dev = &pdev->dev; - - clkc_data = of_device_get_match_data(dev); - if (!clkc_data) - return -EINVAL; - - /* Get the hhi system controller node if available */ - map = syscon_node_to_regmap(of_get_parent(dev->of_node)); - if (IS_ERR(map)) { - dev_err(dev, "failed to get HHI regmap\n"); - return PTR_ERR(map); - } - - /* Populate regmap for the common regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(gx_clk_regmaps); i++) - gx_clk_regmaps[i]->map = map; - - /* Populate regmap for soc specific clocks */ - for (i = 0; i < clkc_data->regmap_clks_count; i++) - clkc_data->regmap_clks[i]->map = map; - - /* Register all clks */ - for (i = 0; i < clkc_data->hw_onecell_data->num; i++) { - /* array might be sparse */ - if (!clkc_data->hw_onecell_data->hws[i]) - continue; - - ret = devm_clk_hw_register(dev, - clkc_data->hw_onecell_data->hws[i]); - if (ret) { - dev_err(dev, "Clock registration failed\n"); - return ret; - } - } - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, - clkc_data->hw_onecell_data); -} - static struct platform_driver gxbb_driver = { - .probe = gxbb_clkc_probe, + .probe = meson_eeclkc_probe, .driver = { .name = "gxbb-clkc", .of_match_table = clkc_match_table, diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c index f965845917e3..b67951909e04 100644 --- a/drivers/clk/meson/meson-aoclk.c +++ b/drivers/clk/meson/meson-aoclk.c @@ -14,9 +14,11 @@ #include <linux/reset-controller.h> #include <linux/mfd/syscon.h> #include <linux/of_device.h> -#include "clk-regmap.h" +#include <linux/slab.h> #include "meson-aoclk.h" +#include "clk-input.h" + static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev, unsigned long id) { @@ -31,6 +33,37 @@ static const struct reset_control_ops meson_aoclk_reset_ops = { .reset = meson_aoclk_do_reset, }; +static int meson_aoclkc_register_inputs(struct device *dev, + struct meson_aoclk_data *data) +{ + struct clk_hw *hw; + char *str; + int i; + + for (i = 0; i < data->num_inputs; i++) { + const struct meson_aoclk_input *in = &data->inputs[i]; + + str = kasprintf(GFP_KERNEL, "%s%s", data->input_prefix, + in->name); + if (!str) + return -ENOMEM; + + hw = meson_clk_hw_register_input(dev, in->name, str, 0); + kfree(str); + + if (IS_ERR(hw)) { + if (!in->required && PTR_ERR(hw) == -ENOENT) + continue; + else if (PTR_ERR(hw) != -EPROBE_DEFER) + dev_err(dev, "failed to register input %s\n", + in->name); + return PTR_ERR(hw); + } + } + + return 0; +} + int meson_aoclkc_probe(struct platform_device *pdev) { struct meson_aoclk_reset_controller *rstc; @@ -53,6 +86,10 @@ int meson_aoclkc_probe(struct platform_device *pdev) return PTR_ERR(regmap); } + ret = meson_aoclkc_register_inputs(dev, data); + if (ret) + return ret; + /* Reset Controller */ rstc->data = data; rstc->regmap = regmap; @@ -65,15 +102,20 @@ int meson_aoclkc_probe(struct platform_device *pdev) return ret; } - /* - * Populate regmap and register all clks - */ - for (clkid = 0; clkid < data->num_clks; clkid++) { + /* Populate regmap */ + for (clkid = 0; clkid < data->num_clks; clkid++) data->clks[clkid]->map = regmap; + /* Register all clks */ + for (clkid = 0; clkid < data->hw_data->num; clkid++) { + if (!data->hw_data->hws[clkid]) + continue; + ret = devm_clk_hw_register(dev, data->hw_data->hws[clkid]); - if (ret) + if (ret) { + dev_err(dev, "Clock registration failed\n"); return ret; + } } return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, diff --git a/drivers/clk/meson/meson-aoclk.h b/drivers/clk/meson/meson-aoclk.h index ab2819e88922..999cde3868f7 100644 --- a/drivers/clk/meson/meson-aoclk.h +++ b/drivers/clk/meson/meson-aoclk.h @@ -11,16 +11,27 @@ #ifndef __MESON_AOCLK_H__ #define __MESON_AOCLK_H__ +#include <linux/clk-provider.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/reset-controller.h> + #include "clk-regmap.h" +struct meson_aoclk_input { + const char *name; + bool required; +}; + struct meson_aoclk_data { const unsigned int reset_reg; const int num_reset; const unsigned int *reset; - int num_clks; + const int num_clks; struct clk_regmap **clks; + const int num_inputs; + const struct meson_aoclk_input *inputs; + const char *input_prefix; const struct clk_hw_onecell_data *hw_data; }; diff --git a/drivers/clk/meson/meson-eeclk.c b/drivers/clk/meson/meson-eeclk.c new file mode 100644 index 000000000000..37a34c9c3885 --- /dev/null +++ b/drivers/clk/meson/meson-eeclk.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#include <linux/clk-provider.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include "clk-input.h" +#include "clk-regmap.h" +#include "meson-eeclk.h" + +int meson_eeclkc_probe(struct platform_device *pdev) +{ + const struct meson_eeclkc_data *data; + struct device *dev = &pdev->dev; + struct clk_hw *input; + struct regmap *map; + int ret, i; + + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + /* Get the hhi system controller node */ + map = syscon_node_to_regmap(of_get_parent(dev->of_node)); + if (IS_ERR(map)) { + dev_err(dev, + "failed to get HHI regmap\n"); + return PTR_ERR(map); + } + + input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); + if (IS_ERR(input)) { + ret = PTR_ERR(input); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get input clock"); + return ret; + } + + /* Populate regmap for the regmap backed clocks */ + for (i = 0; i < data->regmap_clk_num; i++) + data->regmap_clks[i]->map = map; + + for (i = 0; i < data->hw_onecell_data->num; i++) { + /* array might be sparse */ + if (!data->hw_onecell_data->hws[i]) + continue; + + ret = devm_clk_hw_register(dev, data->hw_onecell_data->hws[i]); + if (ret) { + dev_err(dev, "Clock registration failed\n"); + return ret; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + data->hw_onecell_data); +} diff --git a/drivers/clk/meson/meson-eeclk.h b/drivers/clk/meson/meson-eeclk.h new file mode 100644 index 000000000000..1b809b1419fe --- /dev/null +++ b/drivers/clk/meson/meson-eeclk.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_CLKC_H +#define __MESON_CLKC_H + +#include <linux/clk-provider.h> +#include "clk-regmap.h" + +#define IN_PREFIX "ee-in-" + +struct platform_device; + +struct meson_eeclkc_data { + struct clk_regmap *const *regmap_clks; + unsigned int regmap_clk_num; + struct clk_hw_onecell_data *hw_onecell_data; +}; + +int meson_eeclkc_probe(struct platform_device *pdev); + +#endif /* __MESON_CLKC_H */ diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 950d0e548c75..576ad42252d0 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -16,9 +16,10 @@ #include <linux/slab.h> #include <linux/regmap.h> -#include "clkc.h" #include "meson8b.h" #include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-mpll.h" static DEFINE_SPINLOCK(meson_clk_lock); @@ -803,16 +804,16 @@ static struct clk_fixed_factor meson8b_cpu_clk_div8 = { }, }; -static u32 mux_table_abp[] = { 1, 2, 3, 4, 5, 6, 7 }; -static struct clk_regmap meson8b_abp_clk_sel = { +static u32 mux_table_apb[] = { 1, 2, 3, 4, 5, 6, 7 }; +static struct clk_regmap meson8b_apb_clk_sel = { .data = &(struct clk_regmap_mux_data){ .offset = HHI_SYS_CPU_CLK_CNTL1, .mask = 0x7, .shift = 3, - .table = mux_table_abp, + .table = mux_table_apb, }, .hw.init = &(struct clk_init_data){ - .name = "abp_clk_sel", + .name = "apb_clk_sel", .ops = &clk_regmap_mux_ops, .parent_names = (const char *[]){ "cpu_clk_div2", "cpu_clk_div3", @@ -825,16 +826,16 @@ static struct clk_regmap meson8b_abp_clk_sel = { }, }; -static struct clk_regmap meson8b_abp_clk_gate = { +static struct clk_regmap meson8b_apb_clk_gate = { .data = &(struct clk_regmap_gate_data){ .offset = HHI_SYS_CPU_CLK_CNTL1, .bit_idx = 16, .flags = CLK_GATE_SET_TO_DISABLE, }, .hw.init = &(struct clk_init_data){ - .name = "abp_clk_dis", + .name = "apb_clk_dis", .ops = &clk_regmap_gate_ro_ops, - .parent_names = (const char *[]){ "abp_clk_sel" }, + .parent_names = (const char *[]){ "apb_clk_sel" }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, }, @@ -1573,6 +1574,135 @@ static struct clk_regmap meson8b_hdmi_sys = { }, }; +/* + * The MALI IP is clocked by two identical clocks (mali_0 and mali_1) + * muxed by a glitch-free switch on Meson8b and Meson8m2. Meson8 only + * has mali_0 and no glitch-free mux. + */ +static const char * const meson8b_mali_0_1_parent_names[] = { + "xtal", "mpll2", "mpll1", "fclk_div7", "fclk_div4", "fclk_div3", + "fclk_div5" +}; + +static u32 meson8b_mali_0_1_mux_table[] = { 0, 2, 3, 4, 5, 6, 7 }; + +static struct clk_regmap meson8b_mali_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 9, + .table = meson8b_mali_0_1_mux_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = meson8b_mali_0_1_parent_names, + .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_names), + /* + * Don't propagate rate changes up because the only changeable + * parents are mpll1 and mpll2 but we need those for audio and + * RGMII (Ethernet). We don't want to change the audio or + * Ethernet clocks when setting the GPU frequency. + */ + .flags = 0, + }, +}; + +static struct clk_regmap meson8b_mali_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 25, + .table = meson8b_mali_0_1_mux_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = meson8b_mali_0_1_parent_names, + .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_names), + /* + * Don't propagate rate changes up because the only changeable + * parents are mpll1 and mpll2 but we need those for audio and + * RGMII (Ethernet). We don't want to change the audio or + * Ethernet clocks when setting the GPU frequency. + */ + .flags = 0, + }, +}; + +static struct clk_regmap meson8b_mali_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "mali_0", "mali_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(meson8b_ddr, HHI_GCLK_MPEG0, 0); @@ -1659,6 +1789,188 @@ static MESON_GATE(meson8b_ao_ahb_sram, HHI_GCLK_AO, 1); static MESON_GATE(meson8b_ao_ahb_bus, HHI_GCLK_AO, 2); static MESON_GATE(meson8b_ao_iface, HHI_GCLK_AO, 3); +static struct clk_hw_onecell_data meson8_hw_onecell_data = { + .hws = { + [CLKID_XTAL] = &meson8b_xtal.hw, + [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw, + [CLKID_PLL_VID] = &meson8b_vid_pll.hw, + [CLKID_PLL_SYS] = &meson8b_sys_pll.hw, + [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw, + [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw, + [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw, + [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw, + [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw, + [CLKID_CPUCLK] = &meson8b_cpu_clk.hw, + [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw, + [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw, + [CLKID_CLK81] = &meson8b_clk81.hw, + [CLKID_DDR] = &meson8b_ddr.hw, + [CLKID_DOS] = &meson8b_dos.hw, + [CLKID_ISA] = &meson8b_isa.hw, + [CLKID_PL301] = &meson8b_pl301.hw, + [CLKID_PERIPHS] = &meson8b_periphs.hw, + [CLKID_SPICC] = &meson8b_spicc.hw, + [CLKID_I2C] = &meson8b_i2c.hw, + [CLKID_SAR_ADC] = &meson8b_sar_adc.hw, + [CLKID_SMART_CARD] = &meson8b_smart_card.hw, + [CLKID_RNG0] = &meson8b_rng0.hw, + [CLKID_UART0] = &meson8b_uart0.hw, + [CLKID_SDHC] = &meson8b_sdhc.hw, + [CLKID_STREAM] = &meson8b_stream.hw, + [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw, + [CLKID_SDIO] = &meson8b_sdio.hw, + [CLKID_ABUF] = &meson8b_abuf.hw, + [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw, + [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw, + [CLKID_SPI] = &meson8b_spi.hw, + [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw, + [CLKID_ETH] = &meson8b_eth.hw, + [CLKID_DEMUX] = &meson8b_demux.hw, + [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw, + [CLKID_IEC958] = &meson8b_iec958.hw, + [CLKID_I2S_OUT] = &meson8b_i2s_out.hw, + [CLKID_AMCLK] = &meson8b_amclk.hw, + [CLKID_AIFIFO2] = &meson8b_aififo2.hw, + [CLKID_MIXER] = &meson8b_mixer.hw, + [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw, + [CLKID_ADC] = &meson8b_adc.hw, + [CLKID_BLKMV] = &meson8b_blkmv.hw, + [CLKID_AIU] = &meson8b_aiu.hw, + [CLKID_UART1] = &meson8b_uart1.hw, + [CLKID_G2D] = &meson8b_g2d.hw, + [CLKID_USB0] = &meson8b_usb0.hw, + [CLKID_USB1] = &meson8b_usb1.hw, + [CLKID_RESET] = &meson8b_reset.hw, + [CLKID_NAND] = &meson8b_nand.hw, + [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw, + [CLKID_USB] = &meson8b_usb.hw, + [CLKID_VDIN1] = &meson8b_vdin1.hw, + [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw, + [CLKID_EFUSE] = &meson8b_efuse.hw, + [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw, + [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw, + [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw, + [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw, + [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw, + [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw, + [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw, + [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw, + [CLKID_DVIN] = &meson8b_dvin.hw, + [CLKID_UART2] = &meson8b_uart2.hw, + [CLKID_SANA] = &meson8b_sana.hw, + [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw, + [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw, + [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw, + [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw, + [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw, + [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw, + [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw, + [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw, + [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw, + [CLKID_DAC_CLK] = &meson8b_dac_clk.hw, + [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw, + [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw, + [CLKID_ENC480P] = &meson8b_enc480p.hw, + [CLKID_RNG1] = &meson8b_rng1.hw, + [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw, + [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw, + [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw, + [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw, + [CLKID_EDP] = &meson8b_edp.hw, + [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw, + [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw, + [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw, + [CLKID_AO_IFACE] = &meson8b_ao_iface.hw, + [CLKID_MPLL0] = &meson8b_mpll0.hw, + [CLKID_MPLL1] = &meson8b_mpll1.hw, + [CLKID_MPLL2] = &meson8b_mpll2.hw, + [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw, + [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw, + [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw, + [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw, + [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw, + [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw, + [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw, + [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw, + [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw, + [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw, + [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw, + [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw, + [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw, + [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw, + [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw, + [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw, + [CLKID_NAND_CLK] = &meson8b_nand_clk_gate.hw, + [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw, + [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw, + [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw, + [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw, + [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw, + [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw, + [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw, + [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw, + [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw, + [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw, + [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw, + [CLKID_APB] = &meson8b_apb_clk_gate.hw, + [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw, + [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw, + [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw, + [CLKID_AXI] = &meson8b_axi_clk_gate.hw, + [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_clk_sel.hw, + [CLKID_L2_DRAM] = &meson8b_l2_dram_clk_gate.hw, + [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw, + [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw, + [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw, + [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw, + [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw, + [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw, + [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw, + [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw, + [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw, + [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw, + [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw, + [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw, + [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw, + [CLKID_VCLK_DIV4] = &meson8b_vclk_div4_div_gate.hw, + [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw, + [CLKID_VCLK_DIV6] = &meson8b_vclk_div6_div_gate.hw, + [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw, + [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw, + [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw, + [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw, + [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw, + [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw, + [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw, + [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw, + [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4_div_gate.hw, + [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw, + [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6_div_gate.hw, + [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw, + [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12_div_gate.hw, + [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw, + [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw, + [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw, + [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw, + [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw, + [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw, + [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw, + [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw, + [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw, + [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw, + [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw, + [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw, + [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw, + [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw, + [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw, + [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw, + [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw, + [CLKID_MALI] = &meson8b_mali_0.hw, + [CLK_NR_CLKS] = NULL, + }, + .num = CLK_NR_CLKS, +}; + static struct clk_hw_onecell_data meson8b_hw_onecell_data = { .hws = { [CLKID_XTAL] = &meson8b_xtal.hw, @@ -1781,8 +2093,8 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw, [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw, [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw, - [CLKID_ABP_SEL] = &meson8b_abp_clk_sel.hw, - [CLKID_ABP] = &meson8b_abp_clk_gate.hw, + [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw, + [CLKID_APB] = &meson8b_apb_clk_gate.hw, [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw, [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw, [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw, @@ -1833,6 +2145,13 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw, [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw, [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw, + [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw, + [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw, + [CLKID_MALI_0] = &meson8b_mali_0.hw, + [CLKID_MALI_1_SEL] = &meson8b_mali_1_sel.hw, + [CLKID_MALI_1_DIV] = &meson8b_mali_1_div.hw, + [CLKID_MALI_1] = &meson8b_mali_1.hw, + [CLKID_MALI] = &meson8b_mali.hw, [CLK_NR_CLKS] = NULL, }, .num = CLK_NR_CLKS, @@ -1943,8 +2262,8 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = { &meson8b_fixed_pll_dco, &meson8b_hdmi_pll_dco, &meson8b_sys_pll_dco, - &meson8b_abp_clk_sel, - &meson8b_abp_clk_gate, + &meson8b_apb_clk_sel, + &meson8b_apb_clk_gate, &meson8b_periph_clk_sel, &meson8b_periph_clk_gate, &meson8b_axi_clk_sel, @@ -1988,6 +2307,13 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = { &meson8b_hdmi_sys_sel, &meson8b_hdmi_sys_div, &meson8b_hdmi_sys, + &meson8b_mali_0_sel, + &meson8b_mali_0_div, + &meson8b_mali_0, + &meson8b_mali_1_sel, + &meson8b_mali_1_div, + &meson8b_mali_1, + &meson8b_mali, }; static const struct meson8b_clk_reset_line { @@ -2132,7 +2458,6 @@ static int meson8b_cpu_clk_notifier_cb(struct notifier_block *nb, static struct meson8b_nb_data meson8b_cpu_nb_data = { .nb.notifier_call = meson8b_cpu_clk_notifier_cb, - .onecell_data = &meson8b_hw_onecell_data, }; static const struct regmap_config clkc_regmap_config = { @@ -2141,7 +2466,8 @@ static const struct regmap_config clkc_regmap_config = { .reg_stride = 4, }; -static void __init meson8b_clkc_init(struct device_node *np) +static void __init meson8b_clkc_init_common(struct device_node *np, + struct clk_hw_onecell_data *clk_hw_onecell_data) { struct meson8b_clk_reset *rstc; const char *notifier_clk_name; @@ -2192,14 +2518,16 @@ static void __init meson8b_clkc_init(struct device_node *np) */ for (i = CLKID_XTAL; i < CLK_NR_CLKS; i++) { /* array might be sparse */ - if (!meson8b_hw_onecell_data.hws[i]) + if (!clk_hw_onecell_data->hws[i]) continue; - ret = clk_hw_register(NULL, meson8b_hw_onecell_data.hws[i]); + ret = clk_hw_register(NULL, clk_hw_onecell_data->hws[i]); if (ret) return; } + meson8b_cpu_nb_data.onecell_data = clk_hw_onecell_data; + /* * FIXME we shouldn't program the muxes in notifier handlers. The * tricky programming sequence will be handled by the forthcoming @@ -2215,13 +2543,23 @@ static void __init meson8b_clkc_init(struct device_node *np) } ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, - &meson8b_hw_onecell_data); + clk_hw_onecell_data); if (ret) pr_err("%s: failed to register clock provider\n", __func__); } +static void __init meson8_clkc_init(struct device_node *np) +{ + return meson8b_clkc_init_common(np, &meson8_hw_onecell_data); +} + +static void __init meson8b_clkc_init(struct device_node *np) +{ + return meson8b_clkc_init_common(np, &meson8b_hw_onecell_data); +} + CLK_OF_DECLARE_DRIVER(meson8_clkc, "amlogic,meson8-clkc", - meson8b_clkc_init); + meson8_clkc_init); CLK_OF_DECLARE_DRIVER(meson8b_clkc, "amlogic,meson8b-clkc", meson8b_clkc_init); CLK_OF_DECLARE_DRIVER(meson8m2_clkc, "amlogic,meson8m2-clkc", diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index 87fba739af81..b8c58faeae52 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -33,6 +33,7 @@ #define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ #define HHI_VID_DIVIDER_CNTL 0x198 /* 0x66 offset in data sheet */ #define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */ +#define HHI_MALI_CLK_CNTL 0x1b0 /* 0x6c offset in data sheet */ #define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */ #define HHI_NAND_CLK_CNTL 0x25c /* 0x97 offset in data sheet */ #define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ @@ -91,7 +92,7 @@ #define CLKID_CPU_CLK_DIV6 120 #define CLKID_CPU_CLK_DIV7 121 #define CLKID_CPU_CLK_DIV8 122 -#define CLKID_ABP_SEL 123 +#define CLKID_APB_SEL 123 #define CLKID_PERIPH_SEL 125 #define CLKID_AXI_SEL 127 #define CLKID_L2_DRAM_SEL 129 @@ -139,8 +140,14 @@ #define CLKID_HDMI_SYS_SEL 172 #define CLKID_HDMI_SYS_DIV 173 #define CLKID_HDMI_SYS 174 +#define CLKID_MALI_0_SEL 175 +#define CLKID_MALI_0_DIV 176 +#define CLKID_MALI_0 177 +#define CLKID_MALI_1_SEL 178 +#define CLKID_MALI_1_DIV 179 +#define CLKID_MALI_1 180 -#define CLK_NR_CLKS 175 +#define CLK_NR_CLKS 181 /* * include the CLKID and RESETID that have diff --git a/drivers/clk/meson/parm.h b/drivers/clk/meson/parm.h new file mode 100644 index 000000000000..3c9ef1b505ce --- /dev/null +++ b/drivers/clk/meson/parm.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione <carlo@endlessm.com> + */ + +#ifndef __MESON_PARM_H +#define __MESON_PARM_H + +#include <linux/bits.h> +#include <linux/regmap.h> + +#define PMASK(width) GENMASK(width - 1, 0) +#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift) +#define CLRPMASK(width, shift) (~SETPMASK(width, shift)) + +#define PARM_GET(width, shift, reg) \ + (((reg) & SETPMASK(width, shift)) >> (shift)) +#define PARM_SET(width, shift, reg, val) \ + (((reg) & CLRPMASK(width, shift)) | ((val) << (shift))) + +#define MESON_PARM_APPLICABLE(p) (!!((p)->width)) + +struct parm { + u16 reg_off; + u8 shift; + u8 width; +}; + +static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p) +{ + unsigned int val; + + regmap_read(map, p->reg_off, &val); + return PARM_GET(p->width, p->shift, val); +} + +static inline void meson_parm_write(struct regmap *map, struct parm *p, + unsigned int val) +{ + regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift), + val << p->shift); +} + +#endif /* __MESON_PARM_H */ + diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c index bc64019b8eeb..3acf03780221 100644 --- a/drivers/clk/meson/sclk-div.c +++ b/drivers/clk/meson/sclk-div.c @@ -16,7 +16,11 @@ * duty_cycle = (1 + hi) / (1 + val) */ -#include "clkc-audio.h" +#include <linux/clk-provider.h> +#include <linux/module.h> + +#include "clk-regmap.h" +#include "sclk-div.h" static inline struct meson_sclk_div_data * meson_sclk_div_data(struct clk_regmap *clk) @@ -241,3 +245,7 @@ const struct clk_ops meson_sclk_div_ops = { .init = sclk_div_init, }; EXPORT_SYMBOL_GPL(meson_sclk_div_ops); + +MODULE_DESCRIPTION("Amlogic Sample divider driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/sclk-div.h index 0a7c157ebf81..b64b2a32005f 100644 --- a/drivers/clk/meson/clkc-audio.h +++ b/drivers/clk/meson/sclk-div.h @@ -4,16 +4,11 @@ * Author: Jerome Brunet <jbrunet@baylibre.com> */ -#ifndef __MESON_CLKC_AUDIO_H -#define __MESON_CLKC_AUDIO_H +#ifndef __MESON_SCLK_DIV_H +#define __MESON_SCLK_DIV_H -#include "clkc.h" - -struct meson_clk_triphase_data { - struct parm ph0; - struct parm ph1; - struct parm ph2; -}; +#include <linux/clk-provider.h> +#include "parm.h" struct meson_sclk_div_data { struct parm div; @@ -22,7 +17,6 @@ struct meson_sclk_div_data { struct clk_duty cached_duty; }; -extern const struct clk_ops meson_clk_triphase_ops; extern const struct clk_ops meson_sclk_div_ops; -#endif /* __MESON_CLKC_AUDIO_H */ +#endif /* __MESON_SCLK_DIV_H */ diff --git a/drivers/clk/meson/vid-pll-div.c b/drivers/clk/meson/vid-pll-div.c index 88af0e282ea0..08bcc01c0923 100644 --- a/drivers/clk/meson/vid-pll-div.c +++ b/drivers/clk/meson/vid-pll-div.c @@ -5,7 +5,10 @@ */ #include <linux/clk-provider.h> -#include "clkc.h" +#include <linux/module.h> + +#include "clk-regmap.h" +#include "vid-pll-div.h" static inline struct meson_vid_pll_div_data * meson_vid_pll_div_data(struct clk_regmap *clk) @@ -89,3 +92,8 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw, const struct clk_ops meson_vid_pll_div_ro_ops = { .recalc_rate = meson_vid_pll_div_recalc_rate, }; +EXPORT_SYMBOL_GPL(meson_vid_pll_div_ro_ops); + +MODULE_DESCRIPTION("Amlogic video pll divider driver"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/vid-pll-div.h b/drivers/clk/meson/vid-pll-div.h new file mode 100644 index 000000000000..c0128e33ccf9 --- /dev/null +++ b/drivers/clk/meson/vid-pll-div.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_VID_PLL_DIV_H +#define __MESON_VID_PLL_DIV_H + +#include <linux/clk-provider.h> +#include "parm.h" + +struct meson_vid_pll_div_data { + struct parm val; + struct parm sel; +}; + +extern const struct clk_ops meson_vid_pll_div_ro_ops; + +#endif /* __MESON_VID_PLL_DIV_H */ diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index d083b860f083..a60a1be937ad 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -229,9 +229,10 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = { {MMP2_CLK_SDH1, "sdh1_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh_lock}, {MMP2_CLK_SDH2, "sdh2_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH2, 0x1b, 0x1b, 0x0, 0, &sdh_lock}, {MMP2_CLK_SDH3, "sdh3_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH3, 0x1b, 0x1b, 0x0, 0, &sdh_lock}, - {MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1b, 0x1b, 0x0, 0, &disp0_lock}, + {MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x09, 0x09, 0x0, 0, &disp0_lock}, + {MMP2_CLK_DISP0_LCDC, "disp0_lcdc_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x12, 0x12, 0x0, 0, &disp0_lock}, {MMP2_CLK_DISP0_SPHY, "disp0_sphy_clk", "disp0_sphy_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1024, 0x1024, 0x0, 0, &disp0_lock}, - {MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x1b, 0x1b, 0x0, 0, &disp1_lock}, + {MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x09, 0x09, 0x0, 0, &disp1_lock}, {MMP2_CLK_CCIC_ARBITER, "ccic_arbiter", "vctcxo", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1800, 0x1800, 0x0, 0, &ccic0_lock}, {MMP2_CLK_CCIC0, "ccic0_clk", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1b, 0x1b, 0x0, 0, &ccic0_lock}, {MMP2_CLK_CCIC0_PHY, "ccic0_phy_clk", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x24, 0x24, 0x0, 0, &ccic0_lock}, diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 7dedfaa6e152..5c6bbee396b3 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -175,8 +175,10 @@ static void __init a370_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &a370_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, a370_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(a370_clk, "marvell,armada-370-core-clock", a370_clk_init); diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index e8f03293ec83..fa1568279c23 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -226,7 +226,9 @@ static void __init axp_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &axp_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, axp_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init); diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c index e0dd99f36bf4..0bd09d33f9cf 100644 --- a/drivers/clk/mvebu/dove.c +++ b/drivers/clk/mvebu/dove.c @@ -188,10 +188,14 @@ static void __init dove_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &dove_coreclks); - if (ddnp) + if (ddnp) { dove_divider_clk_init(ddnp); + of_node_put(ddnp); + } - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, dove_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(dove_clk, "marvell,dove-core-clock", dove_clk_init); diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index 6f784167bda4..35af3aa18f1c 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -331,6 +331,8 @@ static void __init kirkwood_clk_init(struct device_node *np) if (cgnp) { mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc); + + of_node_put(cgnp); } } CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", diff --git a/drivers/clk/mvebu/mv98dx3236.c b/drivers/clk/mvebu/mv98dx3236.c index 0a74cf7a7725..1c8ab4f834ba 100644 --- a/drivers/clk/mvebu/mv98dx3236.c +++ b/drivers/clk/mvebu/mv98dx3236.c @@ -172,7 +172,9 @@ static void __init mv98dx3236_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &mv98dx3236_core_clocks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock", mv98dx3236_clk_init); diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index e5eca8a1abe4..c25b57c3cbc8 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -71,7 +71,6 @@ struct src_sel { * @freq_tbl: frequency table * @clkr: regmap clock handle * @lock: register lock - * */ struct clk_rcg { u32 ns_reg; @@ -107,7 +106,6 @@ extern const struct clk_ops clk_rcg_lcc_ops; * @freq_tbl: frequency table * @clkr: regmap clock handle * @lock: register lock - * */ struct clk_dyn_rcg { u32 ns_reg[2]; @@ -140,7 +138,7 @@ extern const struct clk_ops clk_dyn_rcg_ops; * @parent_map: map from software's parent index to hardware's src_sel field * @freq_tbl: frequency table * @clkr: regmap clock handle - * + * @cfg_off: defines the cfg register offset from the CMD_RCGR + CFG_REG */ struct clk_rcg2 { u32 cmd_rcgr; @@ -150,6 +148,7 @@ struct clk_rcg2 { const struct parent_map *parent_map; const struct freq_tbl *freq_tbl; struct clk_regmap clkr; + u8 cfg_off; }; #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 6e3bd195d012..8c02bffe50df 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -41,6 +41,11 @@ #define N_REG 0xc #define D_REG 0x10 +#define RCG_CFG_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + CFG_REG) +#define RCG_M_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + M_REG) +#define RCG_N_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + N_REG) +#define RCG_D_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + D_REG) + /* Dynamic Frequency Scaling */ #define MAX_PERF_LEVEL 8 #define SE_CMD_DFSR_OFFSET 0x14 @@ -74,7 +79,7 @@ static u8 clk_rcg2_get_parent(struct clk_hw *hw) u32 cfg; int i, ret; - ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); if (ret) goto err; @@ -123,7 +128,7 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) int ret; u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; - ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, + ret = regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), CFG_SRC_SEL_MASK, cfg); if (ret) return ret; @@ -162,13 +167,13 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) struct clk_rcg2 *rcg = to_clk_rcg2(hw); u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask; - regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); + regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); if (rcg->mnd_width) { mask = BIT(rcg->mnd_width) - 1; - regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m); + regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m); m &= mask; - regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n); + regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), &n); n = ~n; n &= mask; n += m; @@ -263,17 +268,17 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) if (rcg->mnd_width && f->n) { mask = BIT(rcg->mnd_width) - 1; ret = regmap_update_bits(rcg->clkr.regmap, - rcg->cmd_rcgr + M_REG, mask, f->m); + RCG_M_OFFSET(rcg), mask, f->m); if (ret) return ret; ret = regmap_update_bits(rcg->clkr.regmap, - rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m)); + RCG_N_OFFSET(rcg), mask, ~(f->n - f->m)); if (ret) return ret; ret = regmap_update_bits(rcg->clkr.regmap, - rcg->cmd_rcgr + D_REG, mask, ~f->n); + RCG_D_OFFSET(rcg), mask, ~f->n); if (ret) return ret; } @@ -284,8 +289,7 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; if (rcg->mnd_width && f->n && (f->m != f->n)) cfg |= CFG_MODE_DUAL_EDGE; - - return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, + return regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), mask, cfg); } diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 9f4fc7773fb2..c3fd632af119 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -18,6 +18,31 @@ #define CLK_RPMH_ARC_EN_OFFSET 0 #define CLK_RPMH_VRM_EN_OFFSET 4 +#define BCM_TCS_CMD_COMMIT_MASK 0x40000000 +#define BCM_TCS_CMD_VALID_SHIFT 29 +#define BCM_TCS_CMD_VOTE_MASK 0x3fff +#define BCM_TCS_CMD_VOTE_SHIFT 0 + +#define BCM_TCS_CMD(valid, vote) \ + (BCM_TCS_CMD_COMMIT_MASK | \ + ((valid) << BCM_TCS_CMD_VALID_SHIFT) | \ + ((vote & BCM_TCS_CMD_VOTE_MASK) \ + << BCM_TCS_CMD_VOTE_SHIFT)) + +/** + * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM) + * @unit: divisor used to convert Hz value to an RPMh msg + * @width: multiplier used to convert Hz value to an RPMh msg + * @vcd: virtual clock domain that this bcm belongs to + * @reserved: reserved to pad the struct + */ +struct bcm_db { + __le32 unit; + __le16 width; + u8 vcd; + u8 reserved; +}; + /** * struct clk_rpmh - individual rpmh clock data structure * @hw: handle between common and hardware-specific interfaces @@ -29,6 +54,7 @@ * @aggr_state: rpmh clock aggregated state * @last_sent_aggr_state: rpmh clock last aggr state sent to RPMh * @valid_state_mask: mask to determine the state of the rpmh clock + * @unit: divisor to convert rate to rpmh msg in magnitudes of Khz * @dev: device to which it is attached * @peer: pointer to the clock rpmh sibling */ @@ -42,6 +68,7 @@ struct clk_rpmh { u32 aggr_state; u32 last_sent_aggr_state; u32 valid_state_mask; + u32 unit; struct device *dev; struct clk_rpmh *peer; }; @@ -98,6 +125,17 @@ static DEFINE_MUTEX(rpmh_clk_lock); __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \ CLK_RPMH_VRM_EN_OFFSET, 1, _div) +#define DEFINE_CLK_RPMH_BCM(_platform, _name, _res_name) \ + static struct clk_rpmh _platform##_##_name = { \ + .res_name = _res_name, \ + .valid_state_mask = BIT(RPMH_ACTIVE_ONLY_STATE), \ + .div = 1, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpmh_bcm_ops, \ + .name = #_name, \ + }, \ + } + static inline struct clk_rpmh *to_clk_rpmh(struct clk_hw *_hw) { return container_of(_hw, struct clk_rpmh, hw); @@ -210,6 +248,96 @@ static const struct clk_ops clk_rpmh_ops = { .recalc_rate = clk_rpmh_recalc_rate, }; +static int clk_rpmh_bcm_send_cmd(struct clk_rpmh *c, bool enable) +{ + struct tcs_cmd cmd = { 0 }; + u32 cmd_state; + int ret; + + mutex_lock(&rpmh_clk_lock); + + cmd_state = 0; + if (enable) { + cmd_state = 1; + if (c->aggr_state) + cmd_state = c->aggr_state; + } + + if (c->last_sent_aggr_state == cmd_state) { + mutex_unlock(&rpmh_clk_lock); + return 0; + } + + cmd.addr = c->res_addr; + cmd.data = BCM_TCS_CMD(enable, cmd_state); + + ret = rpmh_write_async(c->dev, RPMH_ACTIVE_ONLY_STATE, &cmd, 1); + if (ret) { + dev_err(c->dev, "set active state of %s failed: (%d)\n", + c->res_name, ret); + mutex_unlock(&rpmh_clk_lock); + return ret; + } + + c->last_sent_aggr_state = cmd_state; + + mutex_unlock(&rpmh_clk_lock); + + return 0; +} + +static int clk_rpmh_bcm_prepare(struct clk_hw *hw) +{ + struct clk_rpmh *c = to_clk_rpmh(hw); + + return clk_rpmh_bcm_send_cmd(c, true); +}; + +static void clk_rpmh_bcm_unprepare(struct clk_hw *hw) +{ + struct clk_rpmh *c = to_clk_rpmh(hw); + + clk_rpmh_bcm_send_cmd(c, false); +}; + +static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rpmh *c = to_clk_rpmh(hw); + + c->aggr_state = rate / c->unit; + /* + * Since any non-zero value sent to hw would result in enabling the + * clock, only send the value if the clock has already been prepared. + */ + if (clk_hw_is_prepared(hw)) + clk_rpmh_bcm_send_cmd(c, true); + + return 0; +}; + +static long clk_rpmh_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct clk_rpmh *c = to_clk_rpmh(hw); + + return c->aggr_state * c->unit; +} + +static const struct clk_ops clk_rpmh_bcm_ops = { + .prepare = clk_rpmh_bcm_prepare, + .unprepare = clk_rpmh_bcm_unprepare, + .set_rate = clk_rpmh_bcm_set_rate, + .round_rate = clk_rpmh_round_rate, + .recalc_rate = clk_rpmh_bcm_recalc_rate, +}; + /* Resource name must match resource id present in cmd-db. */ DEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 2); DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", 2); @@ -217,6 +345,7 @@ DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", 2); DEFINE_CLK_RPMH_VRM(sdm845, rf_clk1, rf_clk1_ao, "rfclka1", 1); DEFINE_CLK_RPMH_VRM(sdm845, rf_clk2, rf_clk2_ao, "rfclka2", 1); DEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", 1); +DEFINE_CLK_RPMH_BCM(sdm845, ipa, "IP0"); static struct clk_hw *sdm845_rpmh_clocks[] = { [RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw, @@ -231,6 +360,7 @@ static struct clk_hw *sdm845_rpmh_clocks[] = { [RPMH_RF_CLK2_A] = &sdm845_rf_clk2_ao.hw, [RPMH_RF_CLK3] = &sdm845_rf_clk3.hw, [RPMH_RF_CLK3_A] = &sdm845_rf_clk3_ao.hw, + [RPMH_IPA_CLK] = &sdm845_ipa.hw, }; static const struct clk_rpmh_desc clk_rpmh_sdm845 = { @@ -267,6 +397,8 @@ static int clk_rpmh_probe(struct platform_device *pdev) for (i = 0; i < desc->num_clks; i++) { u32 res_addr; + size_t aux_data_len; + const struct bcm_db *data; rpmh_clk = to_clk_rpmh(hw_clks[i]); res_addr = cmd_db_read_addr(rpmh_clk->res_name); @@ -275,6 +407,20 @@ static int clk_rpmh_probe(struct platform_device *pdev) rpmh_clk->res_name); return -ENODEV; } + + data = cmd_db_read_aux_data(rpmh_clk->res_name, &aux_data_len); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + dev_err(&pdev->dev, + "error reading RPMh aux data for %s (%d)\n", + rpmh_clk->res_name, ret); + return ret; + } + + /* Convert unit from Khz to Hz */ + if (aux_data_len == sizeof(*data)) + rpmh_clk->unit = le32_to_cpu(data->unit) * 1000ULL; + rpmh_clk->res_addr += res_addr; rpmh_clk->dev = &pdev->dev; diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index d3aadaeb2903..22dd42ad9223 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -655,10 +655,73 @@ static const struct rpm_smd_clk_desc rpm_clk_qcs404 = { .num_clks = ARRAY_SIZE(qcs404_clks), }; +/* msm8998 */ +DEFINE_CLK_SMD_RPM(msm8998, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8998, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8998, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, div_clk1, div_clk1_a, 0xb); +DEFINE_CLK_SMD_RPM(msm8998, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, ln_bb_clk1, ln_bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, ln_bb_clk2, ln_bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, ln_bb_clk3_pin, ln_bb_clk3_a_pin, + 3); +DEFINE_CLK_SMD_RPM(msm8998, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk, + QCOM_SMD_RPM_MMAXI_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8998, aggre1_noc_clk, aggre1_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8998, aggre2_noc_clk, aggre2_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(msm8998, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk2_pin, rf_clk2_a_pin, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk3, rf_clk3_a, 6); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk3_pin, rf_clk3_a_pin, 6); +static struct clk_smd_rpm *msm8998_clks[] = { + [RPM_SMD_SNOC_CLK] = &msm8998_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8998_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8998_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8998_cnoc_a_clk, + [RPM_SMD_CE1_CLK] = &msm8998_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8998_ce1_a_clk, + [RPM_SMD_DIV_CLK1] = &msm8998_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8998_div_clk1_a, + [RPM_SMD_IPA_CLK] = &msm8998_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8998_ipa_a_clk, + [RPM_SMD_LN_BB_CLK1] = &msm8998_ln_bb_clk1, + [RPM_SMD_LN_BB_CLK1_A] = &msm8998_ln_bb_clk1_a, + [RPM_SMD_LN_BB_CLK2] = &msm8998_ln_bb_clk2, + [RPM_SMD_LN_BB_CLK2_A] = &msm8998_ln_bb_clk2_a, + [RPM_SMD_LN_BB_CLK3_PIN] = &msm8998_ln_bb_clk3_pin, + [RPM_SMD_LN_BB_CLK3_A_PIN] = &msm8998_ln_bb_clk3_a_pin, + [RPM_SMD_MMAXI_CLK] = &msm8998_mmssnoc_axi_rpm_clk, + [RPM_SMD_MMAXI_A_CLK] = &msm8998_mmssnoc_axi_rpm_a_clk, + [RPM_SMD_AGGR1_NOC_CLK] = &msm8998_aggre1_noc_clk, + [RPM_SMD_AGGR1_NOC_A_CLK] = &msm8998_aggre1_noc_a_clk, + [RPM_SMD_AGGR2_NOC_CLK] = &msm8998_aggre2_noc_clk, + [RPM_SMD_AGGR2_NOC_A_CLK] = &msm8998_aggre2_noc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8998_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8998_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &msm8998_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8998_rf_clk1_a, + [RPM_SMD_RF_CLK2_PIN] = &msm8998_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8998_rf_clk2_a_pin, + [RPM_SMD_RF_CLK3] = &msm8998_rf_clk3, + [RPM_SMD_RF_CLK3_A] = &msm8998_rf_clk3_a, + [RPM_SMD_RF_CLK3_PIN] = &msm8998_rf_clk3_pin, + [RPM_SMD_RF_CLK3_A_PIN] = &msm8998_rf_clk3_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8998 = { + .clks = msm8998_clks, + .num_clks = ARRAY_SIZE(msm8998_clks), +}; + static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, + { .compatible = "qcom,rpmcc-msm8998", .data = &rpm_clk_msm8998 }, { .compatible = "qcom,rpmcc-qcs404", .data = &rpm_clk_qcs404 }, { } }; diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 0a48ed56833b..a6b2f86112d8 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -231,6 +231,8 @@ int qcom_cc_really_probe(struct platform_device *pdev, struct gdsc_desc *scd; size_t num_clks = desc->num_clks; struct clk_regmap **rclks = desc->clks; + size_t num_clk_hws = desc->num_clk_hws; + struct clk_hw **clk_hws = desc->clk_hws; cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); if (!cc) @@ -269,6 +271,12 @@ int qcom_cc_really_probe(struct platform_device *pdev, qcom_cc_drop_protected(dev, cc); + for (i = 0; i < num_clk_hws; i++) { + ret = devm_clk_hw_register(dev, clk_hws[i]); + if (ret) + return ret; + } + for (i = 0; i < num_clks; i++) { if (!rclks[i]) continue; diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 4aa33ee70bae..1e2a8bdac55a 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -27,6 +27,8 @@ struct qcom_cc_desc { size_t num_resets; struct gdsc **gdscs; size_t num_gdscs; + struct clk_hw **clk_hws; + size_t num_clk_hws; }; /** diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index 505c6263141d..0e32892b438c 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -4715,18 +4715,12 @@ static const struct qcom_cc_desc gcc_ipq8074_desc = { .num_clks = ARRAY_SIZE(gcc_ipq8074_clks), .resets = gcc_ipq8074_resets, .num_resets = ARRAY_SIZE(gcc_ipq8074_resets), + .clk_hws = gcc_ipq8074_hws, + .num_clk_hws = ARRAY_SIZE(gcc_ipq8074_hws), }; static int gcc_ipq8074_probe(struct platform_device *pdev) { - int ret, i; - - for (i = 0; i < ARRAY_SIZE(gcc_ipq8074_hws); i++) { - ret = devm_clk_hw_register(&pdev->dev, gcc_ipq8074_hws[i]); - if (ret) - return ret; - } - return qcom_cc_probe(pdev, &gcc_ipq8074_desc); } diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c index 849046fbed6d..8c6d93144b9c 100644 --- a/drivers/clk/qcom/gcc-mdm9615.c +++ b/drivers/clk/qcom/gcc-mdm9615.c @@ -1702,6 +1702,8 @@ static const struct qcom_cc_desc gcc_mdm9615_desc = { .num_clks = ARRAY_SIZE(gcc_mdm9615_clks), .resets = gcc_mdm9615_resets, .num_resets = ARRAY_SIZE(gcc_mdm9615_resets), + .clk_hws = gcc_mdm9615_hws, + .num_clk_hws = ARRAY_SIZE(gcc_mdm9615_hws), }; static const struct of_device_id gcc_mdm9615_match_table[] = { @@ -1712,21 +1714,12 @@ MODULE_DEVICE_TABLE(of, gcc_mdm9615_match_table); static int gcc_mdm9615_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct regmap *regmap; - int ret; - int i; regmap = qcom_cc_map(pdev, &gcc_mdm9615_desc); if (IS_ERR(regmap)) return PTR_ERR(regmap); - for (i = 0; i < ARRAY_SIZE(gcc_mdm9615_hws); i++) { - ret = devm_clk_hw_register(dev, gcc_mdm9615_hws[i]); - if (ret) - return ret; - } - return qcom_cc_really_probe(pdev, &gcc_mdm9615_desc, regmap); } diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 9d136172c27c..4632b9272b7f 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -3656,6 +3656,8 @@ static const struct qcom_cc_desc gcc_msm8996_desc = { .num_resets = ARRAY_SIZE(gcc_msm8996_resets), .gdscs = gcc_msm8996_gdscs, .num_gdscs = ARRAY_SIZE(gcc_msm8996_gdscs), + .clk_hws = gcc_msm8996_hws, + .num_clk_hws = ARRAY_SIZE(gcc_msm8996_hws), }; static const struct of_device_id gcc_msm8996_match_table[] = { @@ -3666,8 +3668,6 @@ MODULE_DEVICE_TABLE(of, gcc_msm8996_match_table); static int gcc_msm8996_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - int i, ret; struct regmap *regmap; regmap = qcom_cc_map(pdev, &gcc_msm8996_desc); @@ -3680,12 +3680,6 @@ static int gcc_msm8996_probe(struct platform_device *pdev) */ regmap_update_bits(regmap, 0x52008, BIT(21), BIT(21)); - for (i = 0; i < ARRAY_SIZE(gcc_msm8996_hws); i++) { - ret = devm_clk_hw_register(dev, gcc_msm8996_hws[i]); - if (ret) - return ret; - } - return qcom_cc_really_probe(pdev, &gcc_msm8996_desc, regmap); } diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 1b779396e04f..c240fba794c7 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -1112,6 +1112,7 @@ static struct clk_rcg2 ufs_axi_clk_src = { static const struct freq_tbl ftbl_usb30_master_clk_src[] = { F(19200000, P_XO, 1, 0, 0), + F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0), F(120000000, P_GPLL0_OUT_MAIN, 5, 0, 0), F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0), { } @@ -1189,6 +1190,7 @@ static struct clk_branch gcc_aggre1_ufs_axi_clk = { "ufs_axi_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1206,6 +1208,7 @@ static struct clk_branch gcc_aggre1_usb3_axi_clk = { "usb30_master_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1288,6 +1291,7 @@ static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = { "blsp1_qup1_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1305,6 +1309,7 @@ static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = { "blsp1_qup1_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1322,6 +1327,7 @@ static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = { "blsp1_qup2_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1339,6 +1345,7 @@ static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = { "blsp1_qup2_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1356,6 +1363,7 @@ static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = { "blsp1_qup3_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1373,6 +1381,7 @@ static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = { "blsp1_qup3_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1390,6 +1399,7 @@ static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = { "blsp1_qup4_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1407,6 +1417,7 @@ static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = { "blsp1_qup4_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1424,6 +1435,7 @@ static struct clk_branch gcc_blsp1_qup5_i2c_apps_clk = { "blsp1_qup5_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1441,6 +1453,7 @@ static struct clk_branch gcc_blsp1_qup5_spi_apps_clk = { "blsp1_qup5_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1458,6 +1471,7 @@ static struct clk_branch gcc_blsp1_qup6_i2c_apps_clk = { "blsp1_qup6_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1475,6 +1489,7 @@ static struct clk_branch gcc_blsp1_qup6_spi_apps_clk = { "blsp1_qup6_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1505,6 +1520,7 @@ static struct clk_branch gcc_blsp1_uart1_apps_clk = { "blsp1_uart1_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1522,6 +1538,7 @@ static struct clk_branch gcc_blsp1_uart2_apps_clk = { "blsp1_uart2_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1539,6 +1556,7 @@ static struct clk_branch gcc_blsp1_uart3_apps_clk = { "blsp1_uart3_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1569,6 +1587,7 @@ static struct clk_branch gcc_blsp2_qup1_i2c_apps_clk = { "blsp2_qup1_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1586,6 +1605,7 @@ static struct clk_branch gcc_blsp2_qup1_spi_apps_clk = { "blsp2_qup1_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1603,6 +1623,7 @@ static struct clk_branch gcc_blsp2_qup2_i2c_apps_clk = { "blsp2_qup2_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1620,6 +1641,7 @@ static struct clk_branch gcc_blsp2_qup2_spi_apps_clk = { "blsp2_qup2_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1637,6 +1659,7 @@ static struct clk_branch gcc_blsp2_qup3_i2c_apps_clk = { "blsp2_qup3_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1654,6 +1677,7 @@ static struct clk_branch gcc_blsp2_qup3_spi_apps_clk = { "blsp2_qup3_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1671,6 +1695,7 @@ static struct clk_branch gcc_blsp2_qup4_i2c_apps_clk = { "blsp2_qup4_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1688,6 +1713,7 @@ static struct clk_branch gcc_blsp2_qup4_spi_apps_clk = { "blsp2_qup4_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1705,6 +1731,7 @@ static struct clk_branch gcc_blsp2_qup5_i2c_apps_clk = { "blsp2_qup5_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1722,6 +1749,7 @@ static struct clk_branch gcc_blsp2_qup5_spi_apps_clk = { "blsp2_qup5_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1739,6 +1767,7 @@ static struct clk_branch gcc_blsp2_qup6_i2c_apps_clk = { "blsp2_qup6_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1756,6 +1785,7 @@ static struct clk_branch gcc_blsp2_qup6_spi_apps_clk = { "blsp2_qup6_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1786,6 +1816,7 @@ static struct clk_branch gcc_blsp2_uart1_apps_clk = { "blsp2_uart1_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1803,6 +1834,7 @@ static struct clk_branch gcc_blsp2_uart2_apps_clk = { "blsp2_uart2_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1820,6 +1852,7 @@ static struct clk_branch gcc_blsp2_uart3_apps_clk = { "blsp2_uart3_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1837,6 +1870,7 @@ static struct clk_branch gcc_cfg_noc_usb3_axi_clk = { "usb30_master_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1854,6 +1888,7 @@ static struct clk_branch gcc_gp1_clk = { "gp1_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1871,6 +1906,7 @@ static struct clk_branch gcc_gp2_clk = { "gp2_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1888,6 +1924,7 @@ static struct clk_branch gcc_gp3_clk = { "gp3_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1957,6 +1994,7 @@ static struct clk_branch gcc_hmss_ahb_clk = { "hmss_ahb_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1987,6 +2025,7 @@ static struct clk_branch gcc_hmss_rbcpr_clk = { "hmss_rbcpr_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2088,6 +2127,7 @@ static struct clk_branch gcc_pcie_0_aux_clk = { "pcie_aux_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2157,6 +2197,7 @@ static struct clk_branch gcc_pcie_phy_aux_clk = { "pcie_aux_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2174,6 +2215,7 @@ static struct clk_branch gcc_pdm2_clk = { "pdm2_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2243,6 +2285,7 @@ static struct clk_branch gcc_sdcc2_apps_clk = { "sdcc2_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2273,6 +2316,7 @@ static struct clk_branch gcc_sdcc4_apps_clk = { "sdcc4_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2316,6 +2360,7 @@ static struct clk_branch gcc_tsif_ref_clk = { "tsif_ref_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2346,6 +2391,7 @@ static struct clk_branch gcc_ufs_axi_clk = { "ufs_axi_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2441,6 +2487,7 @@ static struct clk_branch gcc_usb30_master_clk = { "usb30_master_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2458,6 +2505,7 @@ static struct clk_branch gcc_usb30_mock_utmi_clk = { "usb30_mock_utmi_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2488,6 +2536,7 @@ static struct clk_branch gcc_usb3_phy_aux_clk = { "usb3_phy_aux_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2495,7 +2544,7 @@ static struct clk_branch gcc_usb3_phy_aux_clk = { static struct clk_branch gcc_usb3_phy_pipe_clk = { .halt_reg = 0x50004, - .halt_check = BRANCH_HALT, + .halt_check = BRANCH_HALT_SKIP, .clkr = { .enable_reg = 0x50004, .enable_mask = BIT(0), @@ -2910,6 +2959,10 @@ static const struct regmap_config gcc_msm8998_regmap_config = { .fast_io = true, }; +static struct clk_hw *gcc_msm8998_hws[] = { + &xo.hw, +}; + static const struct qcom_cc_desc gcc_msm8998_desc = { .config = &gcc_msm8998_regmap_config, .clks = gcc_msm8998_clocks, @@ -2918,6 +2971,8 @@ static const struct qcom_cc_desc gcc_msm8998_desc = { .num_resets = ARRAY_SIZE(gcc_msm8998_resets), .gdscs = gcc_msm8998_gdscs, .num_gdscs = ARRAY_SIZE(gcc_msm8998_gdscs), + .clk_hws = gcc_msm8998_hws, + .num_clk_hws = ARRAY_SIZE(gcc_msm8998_hws), }; static int gcc_msm8998_probe(struct platform_device *pdev) @@ -2937,10 +2992,6 @@ static int gcc_msm8998_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_clk_hw_register(&pdev->dev, &xo.hw); - if (ret) - return ret; - return qcom_cc_really_probe(pdev, &gcc_msm8998_desc, regmap); } diff --git a/drivers/clk/qcom/gcc-qcs404.c b/drivers/clk/qcom/gcc-qcs404.c index 64da032bb9ed..5a62f64ada93 100644 --- a/drivers/clk/qcom/gcc-qcs404.c +++ b/drivers/clk/qcom/gcc-qcs404.c @@ -678,6 +678,7 @@ static struct clk_rcg2 blsp1_uart3_apps_clk_src = { .cmd_rcgr = 0x4014, .mnd_width = 16, .hid_width = 5, + .cfg_off = 0x20, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_blsp1_uart0_apps_clk_src, .clkr.hw.init = &(struct clk_init_data){ @@ -2692,6 +2693,8 @@ static const struct qcom_cc_desc gcc_qcs404_desc = { .num_clks = ARRAY_SIZE(gcc_qcs404_clocks), .resets = gcc_qcs404_resets, .num_resets = ARRAY_SIZE(gcc_qcs404_resets), + .clk_hws = gcc_qcs404_hws, + .num_clk_hws = ARRAY_SIZE(gcc_qcs404_hws), }; static const struct of_device_id gcc_qcs404_match_table[] = { @@ -2703,7 +2706,6 @@ MODULE_DEVICE_TABLE(of, gcc_qcs404_match_table); static int gcc_qcs404_probe(struct platform_device *pdev) { struct regmap *regmap; - int ret, i; regmap = qcom_cc_map(pdev, &gcc_qcs404_desc); if (IS_ERR(regmap)) @@ -2711,12 +2713,6 @@ static int gcc_qcs404_probe(struct platform_device *pdev) clk_alpha_pll_configure(&gpll3_out_main, regmap, &gpll3_config); - for (i = 0; i < ARRAY_SIZE(gcc_qcs404_hws); i++) { - ret = devm_clk_hw_register(&pdev->dev, gcc_qcs404_hws[i]); - if (ret) - return ret; - } - return qcom_cc_really_probe(pdev, &gcc_qcs404_desc, regmap); } diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c index ba239ea4c842..8827db23066f 100644 --- a/drivers/clk/qcom/gcc-sdm660.c +++ b/drivers/clk/qcom/gcc-sdm660.c @@ -2420,6 +2420,8 @@ static const struct qcom_cc_desc gcc_sdm660_desc = { .num_resets = ARRAY_SIZE(gcc_sdm660_resets), .gdscs = gcc_sdm660_gdscs, .num_gdscs = ARRAY_SIZE(gcc_sdm660_gdscs), + .clk_hws = gcc_sdm660_hws, + .num_clk_hws = ARRAY_SIZE(gcc_sdm660_hws), }; static const struct of_device_id gcc_sdm660_match_table[] = { @@ -2431,7 +2433,7 @@ MODULE_DEVICE_TABLE(of, gcc_sdm660_match_table); static int gcc_sdm660_probe(struct platform_device *pdev) { - int i, ret; + int ret; struct regmap *regmap; regmap = qcom_cc_map(pdev, &gcc_sdm660_desc); @@ -2446,13 +2448,6 @@ static int gcc_sdm660_probe(struct platform_device *pdev) if (ret) return ret; - /* Register the hws */ - for (i = 0; i < ARRAY_SIZE(gcc_sdm660_hws); i++) { - ret = devm_clk_hw_register(&pdev->dev, gcc_sdm660_hws[i]); - if (ret) - return ret; - } - return qcom_cc_really_probe(pdev, &gcc_sdm660_desc, regmap); } diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index 58fa5c247af1..7131dcf9b060 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -1703,6 +1703,9 @@ static struct clk_branch gcc_pcie_0_pipe_clk = { .enable_mask = BIT(4), .hw.init = &(struct clk_init_data){ .name = "gcc_pcie_0_pipe_clk", + .parent_names = (const char *[]){ "pcie_0_pipe_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1802,6 +1805,8 @@ static struct clk_branch gcc_pcie_1_pipe_clk = { .enable_mask = BIT(30), .hw.init = &(struct clk_init_data){ .name = "gcc_pcie_1_pipe_clk", + .parent_names = (const char *[]){ "pcie_1_pipe_clk" }, + .num_parents = 1, .ops = &clk_branch2_ops, }, }, diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c index 7d4ee109435c..7235510eac94 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -3347,6 +3347,8 @@ static const struct qcom_cc_desc mmcc_msm8996_desc = { .num_resets = ARRAY_SIZE(mmcc_msm8996_resets), .gdscs = mmcc_msm8996_gdscs, .num_gdscs = ARRAY_SIZE(mmcc_msm8996_gdscs), + .clk_hws = mmcc_msm8996_hws, + .num_clk_hws = ARRAY_SIZE(mmcc_msm8996_hws), }; static const struct of_device_id mmcc_msm8996_match_table[] = { @@ -3357,8 +3359,6 @@ MODULE_DEVICE_TABLE(of, mmcc_msm8996_match_table); static int mmcc_msm8996_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - int i, ret; struct regmap *regmap; regmap = qcom_cc_map(pdev, &mmcc_msm8996_desc); @@ -3370,12 +3370,6 @@ static int mmcc_msm8996_probe(struct platform_device *pdev) /* Disable the NoC FSM for mmss_mmagic_cfg_ahb_clk */ regmap_update_bits(regmap, 0x5054, BIT(15), 0); - for (i = 0; i < ARRAY_SIZE(mmcc_msm8996_hws); i++) { - ret = devm_clk_hw_register(dev, mmcc_msm8996_hws[i]); - if (ret) - return ret; - } - return qcom_cc_really_probe(pdev, &mmcc_msm8996_desc, regmap); } diff --git a/drivers/clk/renesas/r8a774a1-cpg-mssr.c b/drivers/clk/renesas/r8a774a1-cpg-mssr.c index 10e852518870..4d92b27a6153 100644 --- a/drivers/clk/renesas/r8a774a1-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774a1-cpg-mssr.c @@ -21,7 +21,7 @@ enum clk_ids { /* Core Clock Outputs exported to DT */ - LAST_DT_CORE_CLK = R8A774A1_CLK_OSC, + LAST_DT_CORE_CLK = R8A774A1_CLK_CANFD, /* External Input Clocks */ CLK_EXTAL, @@ -102,6 +102,7 @@ static const struct cpg_core_clk r8a774a1_core_clks[] __initconst = { DEF_FIXED("cp", R8A774A1_CLK_CP, CLK_EXTAL, 2, 1), DEF_FIXED("cpex", R8A774A1_CLK_CPEX, CLK_EXTAL, 2, 1), + DEF_DIV6P1("canfd", R8A774A1_CLK_CANFD, CLK_PLL1_DIV4, 0x244), DEF_DIV6P1("csi0", R8A774A1_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), DEF_DIV6P1("mso", R8A774A1_CLK_MSO, CLK_PLL1_DIV4, 0x014), DEF_DIV6P1("hdmi", R8A774A1_CLK_HDMI, CLK_PLL1_DIV4, 0x250), @@ -191,6 +192,7 @@ static const struct mssr_mod_clk r8a774a1_mod_clks[] __initconst = { DEF_MOD("gpio2", 910, R8A774A1_CLK_S3D4), DEF_MOD("gpio1", 911, R8A774A1_CLK_S3D4), DEF_MOD("gpio0", 912, R8A774A1_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A774A1_CLK_S3D2), DEF_MOD("can-if1", 915, R8A774A1_CLK_S3D4), DEF_MOD("can-if0", 916, R8A774A1_CLK_S3D4), DEF_MOD("i2c6", 918, R8A774A1_CLK_S0D6), diff --git a/drivers/clk/renesas/r8a774c0-cpg-mssr.c b/drivers/clk/renesas/r8a774c0-cpg-mssr.c index 10b96895d452..34e274f2a273 100644 --- a/drivers/clk/renesas/r8a774c0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774c0-cpg-mssr.c @@ -22,7 +22,7 @@ enum clk_ids { /* Core Clock Outputs exported to DT */ - LAST_DT_CORE_CLK = R8A774C0_CLK_CPEX, + LAST_DT_CORE_CLK = R8A774C0_CLK_CANFD, /* External Input Clocks */ CLK_EXTAL, @@ -33,6 +33,7 @@ enum clk_ids { CLK_PLL1, CLK_PLL3, CLK_PLL0D4, + CLK_PLL0D6, CLK_PLL0D8, CLK_PLL0D20, CLK_PLL0D24, @@ -61,6 +62,7 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = { DEF_FIXED(".pll0", CLK_PLL0, CLK_MAIN, 1, 100), DEF_FIXED(".pll0d4", CLK_PLL0D4, CLK_PLL0, 4, 1), + DEF_FIXED(".pll0d6", CLK_PLL0D6, CLK_PLL0, 6, 1), DEF_FIXED(".pll0d8", CLK_PLL0D8, CLK_PLL0, 8, 1), DEF_FIXED(".pll0d20", CLK_PLL0D20, CLK_PLL0, 20, 1), DEF_FIXED(".pll0d24", CLK_PLL0D24, CLK_PLL0, 24, 1), @@ -112,6 +114,7 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = { DEF_GEN3_PE("s3d2c", R8A774C0_CLK_S3D2C, CLK_S3, 2, CLK_PE, 2), DEF_GEN3_PE("s3d4c", R8A774C0_CLK_S3D4C, CLK_S3, 4, CLK_PE, 4), + DEF_DIV6P1("canfd", R8A774C0_CLK_CANFD, CLK_PLL0D6, 0x244), DEF_DIV6P1("csi0", R8A774C0_CLK_CSI0, CLK_PLL1D2, 0x00c), DEF_DIV6P1("mso", R8A774C0_CLK_MSO, CLK_PLL1D2, 0x014), @@ -119,6 +122,11 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = { }; static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = { + DEF_MOD("tmu4", 121, R8A774C0_CLK_S0D6C), + DEF_MOD("tmu3", 122, R8A774C0_CLK_S3D2C), + DEF_MOD("tmu2", 123, R8A774C0_CLK_S3D2C), + DEF_MOD("tmu1", 124, R8A774C0_CLK_S3D2C), + DEF_MOD("tmu0", 125, R8A774C0_CLK_CP), DEF_MOD("scif5", 202, R8A774C0_CLK_S3D4C), DEF_MOD("scif4", 203, R8A774C0_CLK_S3D4C), DEF_MOD("scif3", 204, R8A774C0_CLK_S3D4C), @@ -172,8 +180,8 @@ static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = { DEF_MOD("ehci0", 703, R8A774C0_CLK_S3D4), DEF_MOD("hsusb", 704, R8A774C0_CLK_S3D4), DEF_MOD("csi40", 716, R8A774C0_CLK_CSI0), - DEF_MOD("du1", 723, R8A774C0_CLK_S2D1), - DEF_MOD("du0", 724, R8A774C0_CLK_S2D1), + DEF_MOD("du1", 723, R8A774C0_CLK_S1D1), + DEF_MOD("du0", 724, R8A774C0_CLK_S1D1), DEF_MOD("lvds", 727, R8A774C0_CLK_S2D1), DEF_MOD("vin5", 806, R8A774C0_CLK_S1D2), @@ -187,6 +195,7 @@ static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = { DEF_MOD("gpio2", 910, R8A774C0_CLK_S3D4), DEF_MOD("gpio1", 911, R8A774C0_CLK_S3D4), DEF_MOD("gpio0", 912, R8A774C0_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A774C0_CLK_S3D2), DEF_MOD("can-if1", 915, R8A774C0_CLK_S3D4), DEF_MOD("can-if0", 916, R8A774C0_CLK_S3D4), DEF_MOD("i2c6", 918, R8A774C0_CLK_S3D2), diff --git a/drivers/clk/renesas/r8a77980-cpg-mssr.c b/drivers/clk/renesas/r8a77980-cpg-mssr.c index 25a3083b6764..f9e07fcc0d96 100644 --- a/drivers/clk/renesas/r8a77980-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77980-cpg-mssr.c @@ -41,6 +41,7 @@ enum clk_ids { CLK_S2, CLK_S3, CLK_SDSRC, + CLK_RPCSRC, CLK_OCO, /* Module Clocks */ @@ -65,8 +66,14 @@ static const struct cpg_core_clk r8a77980_core_clks[] __initconst = { DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1), DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1), DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1), + DEF_BASE(".rpcsrc", CLK_RPCSRC, CLK_TYPE_GEN3_RPCSRC, CLK_PLL1), DEF_RATE(".oco", CLK_OCO, 32768), + DEF_BASE("rpc", R8A77980_CLK_RPC, CLK_TYPE_GEN3_RPC, + CLK_RPCSRC), + DEF_BASE("rpcd2", R8A77980_CLK_RPCD2, CLK_TYPE_GEN3_RPCD2, + R8A77980_CLK_RPC), + /* Core Clock Outputs */ DEF_FIXED("ztr", R8A77980_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), DEF_FIXED("ztrd2", R8A77980_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), @@ -164,6 +171,7 @@ static const struct mssr_mod_clk r8a77980_mod_clks[] __initconst = { DEF_MOD("gpio1", 911, R8A77980_CLK_CP), DEF_MOD("gpio0", 912, R8A77980_CLK_CP), DEF_MOD("can-fd", 914, R8A77980_CLK_S3D2), + DEF_MOD("rpc-if", 917, R8A77980_CLK_RPC), DEF_MOD("i2c4", 927, R8A77980_CLK_S0D6), DEF_MOD("i2c3", 928, R8A77980_CLK_S0D6), DEF_MOD("i2c2", 929, R8A77980_CLK_S3D2), diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index be2ccbd6d623..9a8071a8114d 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -30,6 +30,21 @@ #define CPG_RCKCR_CKSEL BIT(15) /* RCLK Clock Source Select */ +static spinlock_t cpg_lock; + +static void cpg_reg_modify(void __iomem *reg, u32 clear, u32 set) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&cpg_lock, flags); + val = readl(reg); + val &= ~clear; + val |= set; + writel(val, reg); + spin_unlock_irqrestore(&cpg_lock, flags); +}; + struct cpg_simple_notifier { struct notifier_block nb; void __iomem *reg; @@ -118,7 +133,6 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, struct cpg_z_clk *zclk = to_z_clk(hw); unsigned int mult; unsigned int i; - u32 val, kick; /* Factor of 2 is for fixed divider */ mult = DIV_ROUND_CLOSEST_ULL(rate * 32ULL * 2, parent_rate); @@ -127,17 +141,14 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK) return -EBUSY; - val = readl(zclk->reg) & ~zclk->mask; - val |= ((32 - mult) << __ffs(zclk->mask)) & zclk->mask; - writel(val, zclk->reg); + cpg_reg_modify(zclk->reg, zclk->mask, + ((32 - mult) << __ffs(zclk->mask)) & zclk->mask); /* * Set KICK bit in FRQCRB to update hardware setting and wait for * clock change completion. */ - kick = readl(zclk->kick_reg); - kick |= CPG_FRQCRB_KICK; - writel(kick, zclk->kick_reg); + cpg_reg_modify(zclk->kick_reg, 0, CPG_FRQCRB_KICK); /* * Note: There is no HW information about the worst case latency. @@ -266,12 +277,10 @@ static const struct sd_div_table cpg_sd_div_table[] = { static int cpg_sd_clock_enable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - u32 val = readl(clock->csn.reg); - - val &= ~(CPG_SD_STP_MASK); - val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK; - writel(val, clock->csn.reg); + cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK, + clock->div_table[clock->cur_div_idx].val & + CPG_SD_STP_MASK); return 0; } @@ -280,7 +289,7 @@ static void cpg_sd_clock_disable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - writel(readl(clock->csn.reg) | CPG_SD_STP_MASK, clock->csn.reg); + cpg_reg_modify(clock->csn.reg, 0, CPG_SD_STP_MASK); } static int cpg_sd_clock_is_enabled(struct clk_hw *hw) @@ -327,7 +336,6 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, { struct sd_clock *clock = to_sd_clock(hw); unsigned int div = cpg_sd_clock_calc_div(clock, rate, parent_rate); - u32 val; unsigned int i; for (i = 0; i < clock->div_num; i++) @@ -339,10 +347,9 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, clock->cur_div_idx = i; - val = readl(clock->csn.reg); - val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); - val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); - writel(val, clock->csn.reg); + cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK | CPG_SD_FC_MASK, + clock->div_table[i].val & + (CPG_SD_STP_MASK | CPG_SD_FC_MASK)); return 0; } @@ -415,6 +422,92 @@ free_clock: return clk; } +struct rpc_clock { + struct clk_divider div; + struct clk_gate gate; + /* + * One notifier covers both RPC and RPCD2 clocks as they are both + * controlled by the same RPCCKCR register... + */ + struct cpg_simple_notifier csn; +}; + +static const struct clk_div_table cpg_rpcsrc_div_table[] = { + { 2, 5 }, { 3, 6 }, { 0, 0 }, +}; + +static const struct clk_div_table cpg_rpc_div_table[] = { + { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 }, +}; + +static struct clk * __init cpg_rpc_clk_register(const char *name, + void __iomem *base, const char *parent_name, + struct raw_notifier_head *notifiers) +{ + struct rpc_clock *rpc; + struct clk *clk; + + rpc = kzalloc(sizeof(*rpc), GFP_KERNEL); + if (!rpc) + return ERR_PTR(-ENOMEM); + + rpc->div.reg = base + CPG_RPCCKCR; + rpc->div.width = 3; + rpc->div.table = cpg_rpc_div_table; + rpc->div.lock = &cpg_lock; + + rpc->gate.reg = base + CPG_RPCCKCR; + rpc->gate.bit_idx = 8; + rpc->gate.flags = CLK_GATE_SET_TO_DISABLE; + rpc->gate.lock = &cpg_lock; + + rpc->csn.reg = base + CPG_RPCCKCR; + + clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, + &rpc->div.hw, &clk_divider_ops, + &rpc->gate.hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) { + kfree(rpc); + return clk; + } + + cpg_simple_notifier_register(notifiers, &rpc->csn); + return clk; +} + +struct rpcd2_clock { + struct clk_fixed_factor fixed; + struct clk_gate gate; +}; + +static struct clk * __init cpg_rpcd2_clk_register(const char *name, + void __iomem *base, + const char *parent_name) +{ + struct rpcd2_clock *rpcd2; + struct clk *clk; + + rpcd2 = kzalloc(sizeof(*rpcd2), GFP_KERNEL); + if (!rpcd2) + return ERR_PTR(-ENOMEM); + + rpcd2->fixed.mult = 1; + rpcd2->fixed.div = 2; + + rpcd2->gate.reg = base + CPG_RPCCKCR; + rpcd2->gate.bit_idx = 9; + rpcd2->gate.flags = CLK_GATE_SET_TO_DISABLE; + rpcd2->gate.lock = &cpg_lock; + + clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, + &rpcd2->fixed.hw, &clk_fixed_factor_ops, + &rpcd2->gate.hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) + kfree(rpcd2); + + return clk; +} + static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata; static unsigned int cpg_clk_extalr __initdata; @@ -593,6 +686,21 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, } break; + case CLK_TYPE_GEN3_RPCSRC: + return clk_register_divider_table(NULL, core->name, + __clk_get_name(parent), 0, + base + CPG_RPCCKCR, 3, 2, 0, + cpg_rpcsrc_div_table, + &cpg_lock); + + case CLK_TYPE_GEN3_RPC: + return cpg_rpc_clk_register(core->name, base, + __clk_get_name(parent), notifiers); + + case CLK_TYPE_GEN3_RPCD2: + return cpg_rpcd2_clk_register(core->name, base, + __clk_get_name(parent)); + default: return ERR_PTR(-EINVAL); } @@ -613,5 +721,8 @@ int __init rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config, if (attr) cpg_quirks = (uintptr_t)attr->data; pr_debug("%s: mode = 0x%x quirks = 0x%x\n", __func__, mode, cpg_quirks); + + spin_lock_init(&cpg_lock); + return 0; } diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index f4fb6cf16688..eac1b057455a 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -23,6 +23,9 @@ enum rcar_gen3_clk_types { CLK_TYPE_GEN3_Z2, CLK_TYPE_GEN3_OSC, /* OSC EXTAL predivider and fixed divider */ CLK_TYPE_GEN3_RCKSEL, /* Select parent/divider using RCKCR.CKSEL */ + CLK_TYPE_GEN3_RPCSRC, + CLK_TYPE_GEN3_RPC, + CLK_TYPE_GEN3_RPCD2, /* SoC specific definitions start here */ CLK_TYPE_GEN3_SOC_BASE, @@ -57,6 +60,7 @@ struct rcar_gen3_cpg_pll_config { u8 osc_prediv; }; +#define CPG_RPCCKCR 0x238 #define CPG_RCKCR 0x240 struct clk *rcar_gen3_cpg_clk_register(struct device *dev, diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 7ea20341e870..5ecf28854876 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -586,12 +586,12 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { COMPOSITE(0, "dclk_lcdc0_src", mux_pll_src_cpll_gpll_p, 0, RK2928_CLKSEL_CON(27), 0, 1, MFLAGS, 8, 8, DFLAGS, RK2928_CLKGATE_CON(3), 1, GFLAGS), - MUX(DCLK_LCDC0, "dclk_lcdc0", mux_rk3066_lcdc0_p, 0, + MUX(DCLK_LCDC0, "dclk_lcdc0", mux_rk3066_lcdc0_p, CLK_SET_RATE_PARENT, RK2928_CLKSEL_CON(27), 4, 1, MFLAGS), COMPOSITE(0, "dclk_lcdc1_src", mux_pll_src_cpll_gpll_p, 0, RK2928_CLKSEL_CON(28), 0, 1, MFLAGS, 8, 8, DFLAGS, RK2928_CLKGATE_CON(3), 2, GFLAGS), - MUX(DCLK_LCDC1, "dclk_lcdc1", mux_rk3066_lcdc1_p, 0, + MUX(DCLK_LCDC1, "dclk_lcdc1", mux_rk3066_lcdc1_p, CLK_SET_RATE_PARENT, RK2928_CLKSEL_CON(28), 4, 1, MFLAGS), COMPOSITE_NOMUX(0, "cif1_pre", "cif_src", 0, diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index faa94adb2a37..65ab5c2f48b0 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -78,17 +78,17 @@ static struct rockchip_pll_rate_table rk3328_pll_rates[] = { static struct rockchip_pll_rate_table rk3328_pll_frac_rates[] = { /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ - RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134217), + RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134218), /* vco = 1016064000 */ - RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671088), + RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671089), /* vco = 983040000 */ - RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671088), + RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671089), /* vco = 983040000 */ - RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671088), + RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671089), /* vco = 860156000 */ - RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797894), + RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797895), /* vco = 903168000 */ - RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066329), + RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066330), /* vco = 819200000 */ { /* sentinel */ }, }; diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 59d4d46667ce..54066e6508d3 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1028,6 +1028,7 @@ static unsigned long __init exynos4_get_xom(void) xom = readl(chipid_base + 8); iounmap(chipid_base); + of_node_put(np); } return xom; diff --git a/drivers/clk/samsung/clk-exynos5-subcmu.c b/drivers/clk/samsung/clk-exynos5-subcmu.c index 93306283d764..8ae44b5db4c2 100644 --- a/drivers/clk/samsung/clk-exynos5-subcmu.c +++ b/drivers/clk/samsung/clk-exynos5-subcmu.c @@ -136,15 +136,20 @@ static int __init exynos5_clk_register_subcmu(struct device *parent, { struct of_phandle_args genpdspec = { .np = pd_node }; struct platform_device *pdev; + int ret; + + pdev = platform_device_alloc("exynos5-subcmu", PLATFORM_DEVID_AUTO); + if (!pdev) + return -ENOMEM; - pdev = platform_device_alloc(info->pd_name, -1); pdev->dev.parent = parent; - pdev->driver_override = "exynos5-subcmu"; platform_set_drvdata(pdev, (void *)info); of_genpd_add_device(&genpdspec, &pdev->dev); - platform_device_add(pdev); + ret = platform_device_add(pdev); + if (ret) + platform_device_put(pdev); - return 0; + return ret; } static int __init exynos5_clk_probe(struct platform_device *pdev) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 751e2c4fb65b..dae1c96de933 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -559,7 +559,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { /* ENABLE_ACLK_TOP */ GATE(CLK_ACLK_G3D_400, "aclk_g3d_400", "div_aclk_g3d_400", ENABLE_ACLK_TOP, 30, CLK_IS_CRITICAL, 0), - GATE(CLK_ACLK_IMEM_SSX_266, "aclk_imem_ssx_266", + GATE(CLK_ACLK_IMEM_SSSX_266, "aclk_imem_sssx_266", "div_aclk_imem_sssx_266", ENABLE_ACLK_TOP, 29, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_BUS0_400, "aclk_bus0_400", "div_aclk_bus0_400", @@ -568,10 +568,10 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { GATE(CLK_ACLK_BUS1_400, "aclk_bus1_400", "div_aclk_bus1_400", ENABLE_ACLK_TOP, 25, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), - GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_266", + GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_200", ENABLE_ACLK_TOP, 24, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), - GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_200", + GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_266", ENABLE_ACLK_TOP, 23, CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_PERIC_66, "aclk_peric_66", "div_aclk_peric_66_b", @@ -5467,6 +5467,35 @@ static const struct samsung_cmu_info cam1_cmu_info __initconst = { .clk_name = "aclk_cam1_400", }; +/* + * Register offset definitions for CMU_IMEM + */ +#define ENABLE_ACLK_IMEM_SLIMSSS 0x080c +#define ENABLE_PCLK_IMEM_SLIMSSS 0x0908 + +static const unsigned long imem_clk_regs[] __initconst = { + ENABLE_ACLK_IMEM_SLIMSSS, + ENABLE_PCLK_IMEM_SLIMSSS, +}; + +static const struct samsung_gate_clock imem_gate_clks[] __initconst = { + /* ENABLE_ACLK_IMEM_SLIMSSS */ + GATE(CLK_ACLK_SLIMSSS, "aclk_slimsss", "aclk_imem_sssx_266", + ENABLE_ACLK_IMEM_SLIMSSS, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_IMEM_SLIMSSS */ + GATE(CLK_PCLK_SLIMSSS, "pclk_slimsss", "aclk_imem_200", + ENABLE_PCLK_IMEM_SLIMSSS, 0, CLK_IGNORE_UNUSED, 0), +}; + +static const struct samsung_cmu_info imem_cmu_info __initconst = { + .gate_clks = imem_gate_clks, + .nr_gate_clks = ARRAY_SIZE(imem_gate_clks), + .nr_clk_ids = IMEM_NR_CLK, + .clk_regs = imem_clk_regs, + .nr_clk_regs = ARRAY_SIZE(imem_clk_regs), + .clk_name = "aclk_imem_200", +}; struct exynos5433_cmu_data { struct samsung_clk_reg_dump *clk_save; @@ -5655,6 +5684,9 @@ static const struct of_device_id exynos5433_cmu_of_match[] = { .compatible = "samsung,exynos5433-cmu-mscl", .data = &mscl_cmu_info, }, { + .compatible = "samsung,exynos5433-cmu-imem", + .data = &imem_cmu_info, + }, { }, }; diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index 884067e4f1a1..f38f0e24e3b6 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -389,7 +389,7 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, ARRAY_SIZE(s3c2450_gates)); samsung_clk_register_alias(ctx, s3c2450_aliases, ARRAY_SIZE(s3c2450_aliases)); - /* fall through, as s3c2450 extends the s3c2416 clocks */ + /* fall through - as s3c2450 extends the s3c2416 clocks */ case S3C2416: samsung_clk_register_div(ctx, s3c2416_dividers, ARRAY_SIZE(s3c2416_dividers)); diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index c3f309d7100d..9cfaca5fbcdb 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -26,7 +26,7 @@ struct samsung_clk_provider { void __iomem *reg_base; struct device *dev; spinlock_t lock; - /* clk_data must be the last entry due to variable lenght 'hws' array */ + /* clk_data must be the last entry due to variable length 'hws' array */ struct clk_hw_onecell_data clk_data; }; diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index aa7a6e6a15b6..73e03328d5c5 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -176,8 +176,7 @@ static struct clk_ops gateclk_ops = { .set_parent = socfpga_clk_set_parent, }; -static void __init __socfpga_gate_init(struct device_node *node, - const struct clk_ops *ops) +void __init socfpga_gate_init(struct device_node *node) { u32 clk_gate[2]; u32 div_reg[3]; @@ -188,12 +187,17 @@ static void __init __socfpga_gate_init(struct device_node *node, const char *clk_name = node->name; const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; + struct clk_ops *ops; int rc; socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); if (WARN_ON(!socfpga_clk)) return; + ops = kmemdup(&gateclk_ops, sizeof(gateclk_ops), GFP_KERNEL); + if (WARN_ON(!ops)) + return; + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); if (rc) clk_gate[0] = 0; @@ -202,8 +206,8 @@ static void __init __socfpga_gate_init(struct device_node *node, socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; socfpga_clk->hw.bit_idx = clk_gate[1]; - gateclk_ops.enable = clk_gate_ops.enable; - gateclk_ops.disable = clk_gate_ops.disable; + ops->enable = clk_gate_ops.enable; + ops->disable = clk_gate_ops.disable; } rc = of_property_read_u32(node, "fixed-divider", &fixed_div); @@ -234,6 +238,11 @@ static void __init __socfpga_gate_init(struct device_node *node, init.flags = 0; init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); + if (init.num_parents < 2) { + ops->get_parent = NULL; + ops->set_parent = NULL; + } + init.parent_names = parent_name; socfpga_clk->hw.hw.init = &init; @@ -246,8 +255,3 @@ static void __init __socfpga_gate_init(struct device_node *node, if (WARN_ON(rc)) return; } - -void __init socfpga_gate_init(struct device_node *node) -{ - __socfpga_gate_init(node, &gateclk_ops); -} diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c index 35fabe1a32c3..269467e8e07e 100644 --- a/drivers/clk/socfpga/clk-pll-a10.c +++ b/drivers/clk/socfpga/clk-pll-a10.c @@ -95,6 +95,7 @@ static struct clk * __init __socfpga_pll_init(struct device_node *node, clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0); + of_node_put(clkmgr_np); BUG_ON(!clk_mgr_a10_base_addr); pll_clk->hw.reg = clk_mgr_a10_base_addr + reg; diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index c7f463172e4b..b4b44e9b5901 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -100,6 +100,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); clk_mgr_base_addr = of_iomap(clkmgr_np, 0); + of_node_put(clkmgr_np); BUG_ON(!clk_mgr_base_addr); pll_clk->hw.reg = clk_mgr_base_addr + reg; diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index a4fa2945f230..4b5f8f4e4ab8 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -144,7 +144,7 @@ static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi", 8, 4, /* N */ 4, 2, /* K */ 0, 4, /* M */ - BIT(31), /* gate */ + BIT(31) | BIT(23) | BIT(22), /* gate */ BIT(28), /* lock */ CLK_SET_RATE_UNGATE); diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index 0400e5b1d627..1fc71baae13b 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c @@ -1293,8 +1293,8 @@ static int attr_enable_set(void *data, u64 val) return val ? dfll_enable(td) : dfll_disable(td); } -DEFINE_SIMPLE_ATTRIBUTE(enable_fops, attr_enable_get, attr_enable_set, - "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(enable_fops, attr_enable_get, attr_enable_set, + "%llu\n"); static int attr_lock_get(void *data, u64 *val) { @@ -1310,8 +1310,7 @@ static int attr_lock_set(void *data, u64 val) return val ? dfll_lock(td) : dfll_unlock(td); } -DEFINE_SIMPLE_ATTRIBUTE(lock_fops, attr_lock_get, attr_lock_set, - "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(lock_fops, attr_lock_get, attr_lock_set, "%llu\n"); static int attr_rate_get(void *data, u64 *val) { @@ -1328,7 +1327,7 @@ static int attr_rate_set(void *data, u64 val) return dfll_request_rate(td, val); } -DEFINE_SIMPLE_ATTRIBUTE(rate_fops, attr_rate_get, attr_rate_set, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(rate_fops, attr_rate_get, attr_rate_set, "%llu\n"); static int attr_registers_show(struct seq_file *s, void *data) { @@ -1379,10 +1378,11 @@ static void dfll_debug_init(struct tegra_dfll *td) root = debugfs_create_dir("tegra_dfll_fcpu", NULL); td->debugfs_dir = root; - debugfs_create_file("enable", S_IRUGO | S_IWUSR, root, td, &enable_fops); - debugfs_create_file("lock", S_IRUGO, root, td, &lock_fops); - debugfs_create_file("rate", S_IRUGO, root, td, &rate_fops); - debugfs_create_file("registers", S_IRUGO, root, td, &attr_registers_fops); + debugfs_create_file_unsafe("enable", 0644, root, td, + &enable_fops); + debugfs_create_file_unsafe("lock", 0444, root, td, &lock_fops); + debugfs_create_file_unsafe("rate", 0444, root, td, &rate_fops); + debugfs_create_file("registers", 0444, root, td, &attr_registers_fops); } #else diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index 688e403333b9..0c210984765a 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -614,7 +614,7 @@ static int ti_adpll_init_clkout(struct ti_adpll_data *d, init.name = child_name; init.ops = ops; - init.flags = CLK_IS_BASIC; + init.flags = 0; co->hw.init = &init; parent_names[0] = __clk_get_name(clk0); parent_names[1] = __clk_get_name(clk1); diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index 222f68bc3f2a..015a657d3382 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -165,7 +165,7 @@ static void __init omap_clk_register_apll(void *user, ad->clk_bypass = __clk_get_hw(clk); - clk = ti_clk_register(NULL, &clk_hw->hw, node->name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name); if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); kfree(clk_hw->hw.init->parent_names); @@ -402,7 +402,7 @@ static void __init of_omap2_apll_setup(struct device_node *node) if (ret) goto cleanup; - clk = clk_register(NULL, &clk_hw->hw); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name); if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); kfree(init); diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c index 7bb9afbe4058..1cae226759dd 100644 --- a/drivers/clk/ti/autoidle.c +++ b/drivers/clk/ti/autoidle.c @@ -35,7 +35,44 @@ struct clk_ti_autoidle { #define AUTOIDLE_LOW 0x1 static LIST_HEAD(autoidle_clks); -static LIST_HEAD(clk_hw_omap_clocks); + +/* + * we have some non-atomic read/write + * operations behind it, so lets + * take one lock for handling autoidle + * of all clocks + */ +static DEFINE_SPINLOCK(autoidle_spinlock); + +static int _omap2_clk_deny_idle(struct clk_hw_omap *clk) +{ + if (clk->ops && clk->ops->deny_idle) { + unsigned long irqflags; + + spin_lock_irqsave(&autoidle_spinlock, irqflags); + clk->autoidle_count++; + if (clk->autoidle_count == 1) + clk->ops->deny_idle(clk); + + spin_unlock_irqrestore(&autoidle_spinlock, irqflags); + } + return 0; +} + +static int _omap2_clk_allow_idle(struct clk_hw_omap *clk) +{ + if (clk->ops && clk->ops->allow_idle) { + unsigned long irqflags; + + spin_lock_irqsave(&autoidle_spinlock, irqflags); + clk->autoidle_count--; + if (clk->autoidle_count == 0) + clk->ops->allow_idle(clk); + + spin_unlock_irqrestore(&autoidle_spinlock, irqflags); + } + return 0; +} /** * omap2_clk_deny_idle - disable autoidle on an OMAP clock @@ -45,12 +82,15 @@ static LIST_HEAD(clk_hw_omap_clocks); */ int omap2_clk_deny_idle(struct clk *clk) { - struct clk_hw_omap *c; + struct clk_hw *hw = __clk_get_hw(clk); - c = to_clk_hw_omap(__clk_get_hw(clk)); - if (c->ops && c->ops->deny_idle) - c->ops->deny_idle(c); - return 0; + if (omap2_clk_is_hw_omap(hw)) { + struct clk_hw_omap *c = to_clk_hw_omap(hw); + + return _omap2_clk_deny_idle(c); + } + + return -EINVAL; } /** @@ -61,12 +101,15 @@ int omap2_clk_deny_idle(struct clk *clk) */ int omap2_clk_allow_idle(struct clk *clk) { - struct clk_hw_omap *c; + struct clk_hw *hw = __clk_get_hw(clk); - c = to_clk_hw_omap(__clk_get_hw(clk)); - if (c->ops && c->ops->allow_idle) - c->ops->allow_idle(c); - return 0; + if (omap2_clk_is_hw_omap(hw)) { + struct clk_hw_omap *c = to_clk_hw_omap(hw); + + return _omap2_clk_allow_idle(c); + } + + return -EINVAL; } static void _allow_autoidle(struct clk_ti_autoidle *clk) @@ -168,26 +211,6 @@ int __init of_ti_clk_autoidle_setup(struct device_node *node) } /** - * omap2_init_clk_hw_omap_clocks - initialize an OMAP clock - * @hw: struct clk_hw * to initialize - * - * Add an OMAP clock @clk to the internal list of OMAP clocks. Used - * temporarily for autoidle handling, until this support can be - * integrated into the common clock framework code in some way. No - * return value. - */ -void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw) -{ - struct clk_hw_omap *c; - - if (clk_hw_get_flags(hw) & CLK_IS_BASIC) - return; - - c = to_clk_hw_omap(hw); - list_add(&c->node, &clk_hw_omap_clocks); -} - -/** * omap2_clk_enable_autoidle_all - enable autoidle on all OMAP clocks that * support it * @@ -198,11 +221,11 @@ void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw) */ int omap2_clk_enable_autoidle_all(void) { - struct clk_hw_omap *c; + int ret; - list_for_each_entry(c, &clk_hw_omap_clocks, node) - if (c->ops && c->ops->allow_idle) - c->ops->allow_idle(c); + ret = omap2_clk_for_each(_omap2_clk_allow_idle); + if (ret) + return ret; _clk_generic_allow_autoidle_all(); @@ -220,11 +243,11 @@ int omap2_clk_enable_autoidle_all(void) */ int omap2_clk_disable_autoidle_all(void) { - struct clk_hw_omap *c; + int ret; - list_for_each_entry(c, &clk_hw_omap_clocks, node) - if (c->ops && c->ops->deny_idle) - c->ops->deny_idle(c); + ret = omap2_clk_for_each(_omap2_clk_deny_idle); + if (ret) + return ret; _clk_generic_deny_autoidle_all(); diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index d0cd58534781..ba17cc5bd04b 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -31,6 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "%s: " fmt, __func__ +static LIST_HEAD(clk_hw_omap_clocks); struct ti_clk_ll_ops *ti_clk_ll_ops; static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS]; @@ -191,9 +192,13 @@ void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) clkdev_add(&c->lk); } else { if (num_args && !has_clkctrl_data) { - if (of_find_compatible_node(NULL, NULL, - "ti,clkctrl")) { + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, + "ti,clkctrl"); + if (np) { has_clkctrl_data = true; + of_node_put(np); } else { clkctrl_nodes_missing = true; @@ -351,6 +356,9 @@ void __init omap2_clk_legacy_provider_init(int index, void __iomem *mem) struct clk_iomap *io; io = memblock_alloc(sizeof(*io), SMP_CACHE_BYTES); + if (!io) + panic("%s: Failed to allocate %zu bytes\n", __func__, + sizeof(*io)); io->mem = mem; @@ -517,3 +525,74 @@ struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw, return clk; } + +/** + * ti_clk_register_omap_hw - register a clk_hw_omap to the clock framework + * @dev: device for this clock + * @hw: hardware clock handle + * @con: connection ID for this clock + * + * Registers a clk_hw_omap clock to the clock framewor, adds a clock alias + * for it, and adds the list to the available clk_hw_omap type clocks. + * Returns a handle to the registered clock if successful, ERR_PTR value + * in failure. + */ +struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw, + const char *con) +{ + struct clk *clk; + struct clk_hw_omap *oclk; + + clk = ti_clk_register(dev, hw, con); + if (IS_ERR(clk)) + return clk; + + oclk = to_clk_hw_omap(hw); + + list_add(&oclk->node, &clk_hw_omap_clocks); + + return clk; +} + +/** + * omap2_clk_for_each - call function for each registered clk_hw_omap + * @fn: pointer to a callback function + * + * Call @fn for each registered clk_hw_omap, passing @hw to each + * function. @fn must return 0 for success or any other value for + * failure. If @fn returns non-zero, the iteration across clocks + * will stop and the non-zero return value will be passed to the + * caller of omap2_clk_for_each(). + */ +int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw)) +{ + int ret; + struct clk_hw_omap *hw; + + list_for_each_entry(hw, &clk_hw_omap_clocks, node) { + ret = (*fn)(hw); + if (ret) + break; + } + + return ret; +} + +/** + * omap2_clk_is_hw_omap - check if the provided clk_hw is OMAP clock + * @hw: clk_hw to check if it is an omap clock or not + * + * Checks if the provided clk_hw is OMAP clock or not. Returns true if + * it is, false otherwise. + */ +bool omap2_clk_is_hw_omap(struct clk_hw *hw) +{ + struct clk_hw_omap *oclk; + + list_for_each_entry(oclk, &clk_hw_omap_clocks, node) { + if (&oclk->hw == hw) + return true; + } + + return false; +} diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c index 40630eb950fc..639f515e08f0 100644 --- a/drivers/clk/ti/clkctrl.c +++ b/drivers/clk/ti/clkctrl.c @@ -276,7 +276,7 @@ _ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider, init.parent_names = parents; init.num_parents = num_parents; init.ops = ops; - init.flags = CLK_IS_BASIC; + init.flags = 0; clk = ti_clk_register(NULL, clk_hw, init.name); if (IS_ERR_OR_NULL(clk)) { @@ -530,7 +530,7 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node) * Create default clkdm name, replace _cm from end of parent * node name with _clkdm */ - provider->clkdm_name[strlen(provider->clkdm_name) - 5] = 0; + provider->clkdm_name[strlen(provider->clkdm_name) - 2] = 0; } else { provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFn", node); if (!provider->clkdm_name) { diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 9f312a219510..1c0fac59d809 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -203,6 +203,8 @@ typedef void (*ti_of_clk_init_cb_t)(void *, struct device_node *); struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw, const char *con); +struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw, + const char *con); int ti_clk_add_alias(struct device *dev, struct clk *clk, const char *con); void ti_clk_add_aliases(void); @@ -221,7 +223,6 @@ int ti_clk_retry_init(struct device_node *node, void *user, ti_of_clk_init_cb_t func); int ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type); -void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw); int of_ti_clk_autoidle_setup(struct device_node *node); void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks); @@ -301,6 +302,8 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, unsigned long *parent_rate); int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, struct clk_rate_request *req); +int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw)); +bool omap2_clk_is_hw_omap(struct clk_hw *hw); extern struct ti_clk_ll_ops *ti_clk_ll_ops; diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index 07a805125e98..423a99b9f10c 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -143,7 +143,7 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) continue; } clk_hw = __clk_get_hw(clk); - if (clk_hw_get_flags(clk_hw) & CLK_IS_BASIC) { + if (!omap2_clk_is_hw_omap(clk_hw)) { pr_warn("can't setup clkdm for basic clk %s\n", __clk_get_name(clk)); continue; diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index 0241450f3eb3..4786e0ebc2e8 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -336,7 +336,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, init.name = name; init.ops = &ti_clk_divider_ops; - init.flags = flags | CLK_IS_BASIC; + init.flags = flags; init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 6c3329bc116f..659dadb23279 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -192,10 +192,9 @@ static void __init _register_dpll(void *user, dd->clk_bypass = __clk_get_hw(clk); /* register the clock */ - clk = ti_clk_register(NULL, &clk_hw->hw, node->name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name); if (!IS_ERR(clk)) { - omap2_init_clk_hw_omap_clocks(&clk_hw->hw); of_clk_add_provider(node, of_clk_src_simple_get, clk); kfree(clk_hw->hw.init->parent_names); kfree(clk_hw->hw.init); @@ -265,14 +264,12 @@ static void _register_dpll_x2(struct device_node *node, #endif /* register the clock */ - clk = ti_clk_register(NULL, &clk_hw->hw, name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name); - if (IS_ERR(clk)) { + if (IS_ERR(clk)) kfree(clk_hw); - } else { - omap2_init_clk_hw_omap_clocks(&clk_hw->hw); + else of_clk_add_provider(node, of_clk_src_simple_get, clk); - } } #endif diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c index 44b6b6403753..3dde6c8c3354 100644 --- a/drivers/clk/ti/dpll3xxx.c +++ b/drivers/clk/ti/dpll3xxx.c @@ -731,7 +731,7 @@ static struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw) do { do { hw = clk_hw_get_parent(hw); - } while (hw && (clk_hw_get_flags(hw) & CLK_IS_BASIC)); + } while (hw && (!omap2_clk_is_hw_omap(hw))); if (!hw) break; pclk = to_clk_hw_omap(hw); diff --git a/drivers/clk/ti/gate.c b/drivers/clk/ti/gate.c index 1c78fff5513c..504c0e91cdc7 100644 --- a/drivers/clk/ti/gate.c +++ b/drivers/clk/ti/gate.c @@ -123,7 +123,7 @@ static struct clk *_register_gate(struct device *dev, const char *name, init.flags = flags; - clk = ti_clk_register(NULL, &clk_hw->hw, name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name); if (IS_ERR(clk)) kfree(clk_hw); diff --git a/drivers/clk/ti/interface.c b/drivers/clk/ti/interface.c index 87e00c2ee957..83e34429d3b1 100644 --- a/drivers/clk/ti/interface.c +++ b/drivers/clk/ti/interface.c @@ -57,12 +57,10 @@ static struct clk *_register_interface(struct device *dev, const char *name, init.num_parents = 1; init.parent_names = &parent_name; - clk = ti_clk_register(NULL, &clk_hw->hw, name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name); if (IS_ERR(clk)) kfree(clk_hw); - else - omap2_init_clk_hw_omap_clocks(&clk_hw->hw); return clk; } diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index 883bdde94d04..b7f9a4f068bf 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -143,7 +143,7 @@ static struct clk *_register_mux(struct device *dev, const char *name, init.name = name; init.ops = &ti_clk_mux_ops; - init.flags = flags | CLK_IS_BASIC; + init.flags = flags; init.parent_names = parent_names; init.num_parents = num_parents; diff --git a/drivers/clk/uniphier/clk-uniphier-cpugear.c b/drivers/clk/uniphier/clk-uniphier-cpugear.c index ec11f55594ad..5d2d42b7e182 100644 --- a/drivers/clk/uniphier/clk-uniphier-cpugear.c +++ b/drivers/clk/uniphier/clk-uniphier-cpugear.c @@ -47,7 +47,7 @@ static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index) return ret; ret = regmap_write_bits(gear->regmap, - gear->regbase + UNIPHIER_CLK_CPUGEAR_SET, + gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD, UNIPHIER_CLK_CPUGEAR_UPD_BIT, UNIPHIER_CLK_CPUGEAR_UPD_BIT); if (ret) diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c index 6b40eb89ae19..68bd3abaef2c 100644 --- a/drivers/clk/x86/clk-lpt.c +++ b/drivers/clk/x86/clk-lpt.c @@ -13,7 +13,7 @@ #include <linux/clk-provider.h> #include <linux/err.h> #include <linux/module.h> -#include <linux/platform_data/clk-lpss.h> +#include <linux/platform_data/x86/clk-lpss.h> #include <linux/platform_device.h> static int lpt_clk_probe(struct platform_device *pdev) diff --git a/drivers/clk/x86/clk-st.c b/drivers/clk/x86/clk-st.c index 3a0996f2d556..25d4b97aff9b 100644 --- a/drivers/clk/x86/clk-st.c +++ b/drivers/clk/x86/clk-st.c @@ -52,7 +52,8 @@ static int st_clk_probe(struct platform_device *pdev) 0, st_data->base + MISCCLKCNTL1, OSCCLKENB, CLK_GATE_SET_TO_DISABLE, NULL); - clk_hw_register_clkdev(hws[ST_CLK_GATE], "oscout1", NULL); + devm_clk_hw_register_clkdev(&pdev->dev, hws[ST_CLK_GATE], "oscout1", + NULL); return 0; } diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a8b20b65bd4b..aa4ec53281ce 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -1261,6 +1261,13 @@ static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void) return ARCH_TIMER_PHYS_SECURE_PPI; } +static void __init arch_timer_populate_kvm_info(void) +{ + arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI]; + if (is_kernel_in_hyp_mode()) + arch_timer_kvm_info.physical_irq = arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]; +} + static int __init arch_timer_of_init(struct device_node *np) { int i, ret; @@ -1275,7 +1282,7 @@ static int __init arch_timer_of_init(struct device_node *np) for (i = ARCH_TIMER_PHYS_SECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++) arch_timer_ppi[i] = irq_of_parse_and_map(np, i); - arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI]; + arch_timer_populate_kvm_info(); rate = arch_timer_get_cntfrq(); arch_timer_of_configure_rate(rate, np); @@ -1605,7 +1612,7 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table) arch_timer_ppi[ARCH_TIMER_HYP_PPI] = acpi_gtdt_map_ppi(ARCH_TIMER_HYP_PPI); - arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI]; + arch_timer_populate_kvm_info(); /* * When probing via ACPI, we have no mechanism to override the sysreg diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c index ed5e42461094..ad48fd52cb53 100644 --- a/drivers/connector/cn_proc.c +++ b/drivers/connector/cn_proc.c @@ -250,6 +250,7 @@ void proc_coredump_connector(struct task_struct *task) { struct cn_msg *msg; struct proc_event *ev; + struct task_struct *parent; __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); if (atomic_read(&proc_event_num_listeners) < 1) @@ -262,8 +263,14 @@ void proc_coredump_connector(struct task_struct *task) ev->what = PROC_EVENT_COREDUMP; ev->event_data.coredump.process_pid = task->pid; ev->event_data.coredump.process_tgid = task->tgid; - ev->event_data.coredump.parent_pid = task->real_parent->pid; - ev->event_data.coredump.parent_tgid = task->real_parent->tgid; + + rcu_read_lock(); + if (pid_alive(task)) { + parent = rcu_dereference(task->real_parent); + ev->event_data.coredump.parent_pid = parent->pid; + ev->event_data.coredump.parent_tgid = parent->tgid; + } + rcu_read_unlock(); memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); msg->ack = 0; /* not used */ @@ -276,6 +283,7 @@ void proc_exit_connector(struct task_struct *task) { struct cn_msg *msg; struct proc_event *ev; + struct task_struct *parent; __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); if (atomic_read(&proc_event_num_listeners) < 1) @@ -290,8 +298,14 @@ void proc_exit_connector(struct task_struct *task) ev->event_data.exit.process_tgid = task->tgid; ev->event_data.exit.exit_code = task->exit_code; ev->event_data.exit.exit_signal = task->exit_signal; - ev->event_data.exit.parent_pid = task->real_parent->pid; - ev->event_data.exit.parent_tgid = task->real_parent->tgid; + + rcu_read_lock(); + if (pid_alive(task)) { + parent = rcu_dereference(task->real_parent); + ev->event_data.exit.parent_pid = parent->pid; + ev->event_data.exit.parent_tgid = parent->tgid; + } + rcu_read_unlock(); memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); msg->ack = 0; /* not used */ diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 0e626b00053b..e10922709d13 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -206,17 +206,15 @@ unsigned int cpufreq_generic_get(unsigned int cpu) EXPORT_SYMBOL_GPL(cpufreq_generic_get); /** - * cpufreq_cpu_get: returns policy for a cpu and marks it busy. + * cpufreq_cpu_get - Return policy for a CPU and mark it as busy. + * @cpu: CPU to find the policy for. * - * @cpu: cpu to find policy for. + * Call cpufreq_cpu_get_raw() to obtain a cpufreq policy for @cpu and increment + * the kobject reference counter of that policy. Return a valid policy on + * success or NULL on failure. * - * This returns policy for 'cpu', returns NULL if it doesn't exist. - * It also increments the kobject reference count to mark it busy and so would - * require a corresponding call to cpufreq_cpu_put() to decrement it back. - * If corresponding call cpufreq_cpu_put() isn't made, the policy wouldn't be - * freed as that depends on the kobj count. - * - * Return: A valid policy on success, otherwise NULL on failure. + * The policy returned by this function has to be released with the help of + * cpufreq_cpu_put() to balance its kobject reference counter properly. */ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) { @@ -243,12 +241,8 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) EXPORT_SYMBOL_GPL(cpufreq_cpu_get); /** - * cpufreq_cpu_put: Decrements the usage count of a policy - * - * @policy: policy earlier returned by cpufreq_cpu_get(). - * - * This decrements the kobject reference count incremented earlier by calling - * cpufreq_cpu_get(). + * cpufreq_cpu_put - Decrement kobject usage counter for cpufreq policy. + * @policy: cpufreq policy returned by cpufreq_cpu_get(). */ void cpufreq_cpu_put(struct cpufreq_policy *policy) { diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 002f5169d4eb..e22f0dbaebb1 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1762,7 +1762,7 @@ static void intel_pstate_update_util(struct update_util_data *data, u64 time, /* Start over if the CPU may have been idle. */ if (delta_ns > TICK_NSEC) { cpu->iowait_boost = ONE_EIGHTH_FP; - } else if (cpu->iowait_boost) { + } else if (cpu->iowait_boost >= ONE_EIGHTH_FP) { cpu->iowait_boost <<= 1; if (cpu->iowait_boost > int_tofp(1)) cpu->iowait_boost = int_tofp(1); diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index 46254e583982..74e0e0c20c46 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -143,7 +143,7 @@ static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq) return ret; } -static void __init pxa_cpufreq_init_voltages(void) +static void pxa_cpufreq_init_voltages(void) { vcc_core = regulator_get(NULL, "vcc_core"); if (IS_ERR(vcc_core)) { @@ -159,7 +159,7 @@ static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq) return 0; } -static void __init pxa_cpufreq_init_voltages(void) { } +static void pxa_cpufreq_init_voltages(void) { } #endif static void find_freq_tables(struct cpufreq_frequency_table **freq_table, diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c index bb93e5cf6a4a..9fddf828a76f 100644 --- a/drivers/cpuidle/governor.c +++ b/drivers/cpuidle/governor.c @@ -89,6 +89,7 @@ int cpuidle_register_governor(struct cpuidle_governor *gov) mutex_lock(&cpuidle_lock); if (__cpuidle_find_governor(gov->name) == NULL) { ret = 0; + list_add_tail(&gov->governor_list, &cpuidle_governors); if (!cpuidle_curr_governor || !strncasecmp(param_governor, gov->name, CPUIDLE_NAME_LEN) || (cpuidle_curr_governor->rating < gov->rating && diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 61316fc51548..5951604e7d5c 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -186,7 +186,7 @@ static unsigned int get_typical_interval(struct menu_device *data, unsigned int min, max, thresh, avg; uint64_t sum, variance; - thresh = UINT_MAX; /* Discard outliers above this value */ + thresh = INT_MAX; /* Discard outliers above this value */ again: diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 9eac5099098e..579578498deb 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -3455,7 +3455,6 @@ static int __init caam_algapi_init(void) { struct device_node *dev_node; struct platform_device *pdev; - struct device *ctrldev; struct caam_drv_private *priv; int i = 0, err = 0; u32 aes_vid, aes_inst, des_inst, md_vid, md_inst, ccha_inst, ptha_inst; @@ -3476,16 +3475,17 @@ static int __init caam_algapi_init(void) return -ENODEV; } - ctrldev = &pdev->dev; - priv = dev_get_drvdata(ctrldev); + priv = dev_get_drvdata(&pdev->dev); of_node_put(dev_node); /* * If priv is NULL, it's probably because the caam driver wasn't * properly initialized (e.g. RNG4 init failed). Thus, bail out here. */ - if (!priv) - return -ENODEV; + if (!priv) { + err = -ENODEV; + goto out_put_dev; + } /* @@ -3626,6 +3626,8 @@ static int __init caam_algapi_init(void) if (registered) pr_info("caam algorithms registered in /proc/crypto\n"); +out_put_dev: + put_device(&pdev->dev); return err; } diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c index a15ce9213310..c61921d32489 100644 --- a/drivers/crypto/caam/caamalg_qi.c +++ b/drivers/crypto/caam/caamalg_qi.c @@ -2492,12 +2492,15 @@ static int __init caam_qi_algapi_init(void) * If priv is NULL, it's probably because the caam driver wasn't * properly initialized (e.g. RNG4 init failed). Thus, bail out here. */ - if (!priv || !priv->qi_present) - return -ENODEV; + if (!priv || !priv->qi_present) { + err = -ENODEV; + goto out_put_dev; + } if (caam_dpaa2) { dev_info(ctrldev, "caam/qi frontend driver not suitable for DPAA 2.x, aborting...\n"); - return -ENODEV; + err = -ENODEV; + goto out_put_dev; } /* @@ -2610,6 +2613,8 @@ static int __init caam_qi_algapi_init(void) if (registered) dev_info(priv->qidev, "algorithms registered in /proc/crypto\n"); +out_put_dev: + put_device(ctrldev); return err; } diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index d7483e4d0ce2..b1eadc6652b5 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -1993,7 +1993,6 @@ static int __init caam_algapi_hash_init(void) { struct device_node *dev_node; struct platform_device *pdev; - struct device *ctrldev; int i = 0, err = 0; struct caam_drv_private *priv; unsigned int md_limit = SHA512_DIGEST_SIZE; @@ -2012,16 +2011,17 @@ static int __init caam_algapi_hash_init(void) return -ENODEV; } - ctrldev = &pdev->dev; - priv = dev_get_drvdata(ctrldev); + priv = dev_get_drvdata(&pdev->dev); of_node_put(dev_node); /* * If priv is NULL, it's probably because the caam driver wasn't * properly initialized (e.g. RNG4 init failed). Thus, bail out here. */ - if (!priv) - return -ENODEV; + if (!priv) { + err = -ENODEV; + goto out_put_dev; + } /* * Register crypto algorithms the device supports. First, identify @@ -2043,8 +2043,10 @@ static int __init caam_algapi_hash_init(void) * Skip registration of any hashing algorithms if MD block * is not present. */ - if (!md_inst) - return -ENODEV; + if (!md_inst) { + err = -ENODEV; + goto out_put_dev; + } /* Limit digest size based on LP256 */ if (md_vid == CHA_VER_VID_MD_LP256) @@ -2101,6 +2103,8 @@ static int __init caam_algapi_hash_init(void) list_add_tail(&t_alg->entry, &hash_list); } +out_put_dev: + put_device(&pdev->dev); return err; } diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c index 77ab28a2811a..58285642306e 100644 --- a/drivers/crypto/caam/caampkc.c +++ b/drivers/crypto/caam/caampkc.c @@ -1042,8 +1042,10 @@ static int __init caam_pkc_init(void) * If priv is NULL, it's probably because the caam driver wasn't * properly initialized (e.g. RNG4 init failed). Thus, bail out here. */ - if (!priv) - return -ENODEV; + if (!priv) { + err = -ENODEV; + goto out_put_dev; + } /* Determine public key hardware accelerator presence. */ if (priv->era < 10) @@ -1053,8 +1055,10 @@ static int __init caam_pkc_init(void) pk_inst = rd_reg32(&priv->ctrl->vreg.pkha) & CHA_VER_NUM_MASK; /* Do not register algorithms if PKHA is not present. */ - if (!pk_inst) - return -ENODEV; + if (!pk_inst) { + err = -ENODEV; + goto out_put_dev; + } err = crypto_register_akcipher(&caam_rsa); if (err) @@ -1063,6 +1067,8 @@ static int __init caam_pkc_init(void) else dev_info(ctrldev, "caam pkc algorithms registered in /proc/crypto\n"); +out_put_dev: + put_device(ctrldev); return err; } diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index a387c8d49a62..95eb5402c59f 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -308,7 +308,6 @@ static int __init caam_rng_init(void) struct device *dev; struct device_node *dev_node; struct platform_device *pdev; - struct device *ctrldev; struct caam_drv_private *priv; u32 rng_inst; int err; @@ -326,16 +325,17 @@ static int __init caam_rng_init(void) return -ENODEV; } - ctrldev = &pdev->dev; - priv = dev_get_drvdata(ctrldev); + priv = dev_get_drvdata(&pdev->dev); of_node_put(dev_node); /* * If priv is NULL, it's probably because the caam driver wasn't * properly initialized (e.g. RNG4 init failed). Thus, bail out here. */ - if (!priv) - return -ENODEV; + if (!priv) { + err = -ENODEV; + goto out_put_dev; + } /* Check for an instantiated RNG before registration */ if (priv->era < 10) @@ -344,13 +344,16 @@ static int __init caam_rng_init(void) else rng_inst = rd_reg32(&priv->ctrl->vreg.rng) & CHA_VER_NUM_MASK; - if (!rng_inst) - return -ENODEV; + if (!rng_inst) { + err = -ENODEV; + goto out_put_dev; + } dev = caam_jr_alloc(); if (IS_ERR(dev)) { pr_err("Job Ring Device allocation for transform failed\n"); - return PTR_ERR(dev); + err = PTR_ERR(dev); + goto out_put_dev; } rng_ctx = kmalloc(sizeof(*rng_ctx), GFP_DMA | GFP_KERNEL); if (!rng_ctx) { @@ -361,6 +364,7 @@ static int __init caam_rng_init(void) if (err) goto free_rng_ctx; + put_device(&pdev->dev); dev_info(dev, "registering rng-caam\n"); return hwrng_register(&caam_rng); @@ -368,6 +372,8 @@ free_rng_ctx: kfree(rng_ctx); free_caam_alloc: caam_jr_free(dev); +out_put_dev: + put_device(&pdev->dev); return err; } diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c index f4e625cf53ca..1afdcb81d8ed 100644 --- a/drivers/crypto/s5p-sss.c +++ b/drivers/crypto/s5p-sss.c @@ -241,7 +241,7 @@ struct samsung_aes_variant { unsigned int aes_offset; unsigned int hash_offset; - const char *clk_names[]; + const char *clk_names[2]; }; struct s5p_aes_reqctx { diff --git a/drivers/dax/Kconfig b/drivers/dax/Kconfig index e0700bf4893a..5ef624fe3934 100644 --- a/drivers/dax/Kconfig +++ b/drivers/dax/Kconfig @@ -23,12 +23,38 @@ config DEV_DAX config DEV_DAX_PMEM tristate "PMEM DAX: direct access to persistent memory" depends on LIBNVDIMM && NVDIMM_DAX && DEV_DAX + depends on m # until we can kill DEV_DAX_PMEM_COMPAT default DEV_DAX help Support raw access to persistent memory. Note that this driver consumes memory ranges allocated and exported by the libnvdimm sub-system. - Say Y if unsure + Say M if unsure + +config DEV_DAX_KMEM + tristate "KMEM DAX: volatile-use of persistent memory" + default DEV_DAX + depends on DEV_DAX + depends on MEMORY_HOTPLUG # for add_memory() and friends + help + Support access to persistent memory as if it were RAM. This + allows easier use of persistent memory by unmodified + applications. + + To use this feature, a DAX device must be unbound from the + device_dax driver (PMEM DAX) and bound to this kmem driver + on each boot. + + Say N if unsure. + +config DEV_DAX_PMEM_COMPAT + tristate "PMEM DAX: support the deprecated /sys/class/dax interface" + depends on DEV_DAX_PMEM + default DEV_DAX_PMEM + help + Older versions of the libdaxctl library expect to find all + device-dax instances under /sys/class/dax. If libdaxctl in + your distribution is older than v58 say M, otherwise say N. endif diff --git a/drivers/dax/Makefile b/drivers/dax/Makefile index 574286fac87c..81f7d54dadfb 100644 --- a/drivers/dax/Makefile +++ b/drivers/dax/Makefile @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DAX) += dax.o obj-$(CONFIG_DEV_DAX) += device_dax.o -obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o +obj-$(CONFIG_DEV_DAX_KMEM) += kmem.o dax-y := super.o -dax_pmem-y := pmem.o +dax-y += bus.o device_dax-y := device.o + +obj-y += pmem/ diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c new file mode 100644 index 000000000000..2109cfe80219 --- /dev/null +++ b/drivers/dax/bus.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2017-2018 Intel Corporation. All rights reserved. */ +#include <linux/memremap.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/dax.h> +#include "dax-private.h" +#include "bus.h" + +static struct class *dax_class; + +static DEFINE_MUTEX(dax_bus_lock); + +#define DAX_NAME_LEN 30 +struct dax_id { + struct list_head list; + char dev_name[DAX_NAME_LEN]; +}; + +static int dax_bus_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* + * We only ever expect to handle device-dax instances, i.e. the + * @type argument to MODULE_ALIAS_DAX_DEVICE() is always zero + */ + return add_uevent_var(env, "MODALIAS=" DAX_DEVICE_MODALIAS_FMT, 0); +} + +static struct dax_device_driver *to_dax_drv(struct device_driver *drv) +{ + return container_of(drv, struct dax_device_driver, drv); +} + +static struct dax_id *__dax_match_id(struct dax_device_driver *dax_drv, + const char *dev_name) +{ + struct dax_id *dax_id; + + lockdep_assert_held(&dax_bus_lock); + + list_for_each_entry(dax_id, &dax_drv->ids, list) + if (sysfs_streq(dax_id->dev_name, dev_name)) + return dax_id; + return NULL; +} + +static int dax_match_id(struct dax_device_driver *dax_drv, struct device *dev) +{ + int match; + + mutex_lock(&dax_bus_lock); + match = !!__dax_match_id(dax_drv, dev_name(dev)); + mutex_unlock(&dax_bus_lock); + + return match; +} + +enum id_action { + ID_REMOVE, + ID_ADD, +}; + +static ssize_t do_id_store(struct device_driver *drv, const char *buf, + size_t count, enum id_action action) +{ + struct dax_device_driver *dax_drv = to_dax_drv(drv); + unsigned int region_id, id; + char devname[DAX_NAME_LEN]; + struct dax_id *dax_id; + ssize_t rc = count; + int fields; + + fields = sscanf(buf, "dax%d.%d", ®ion_id, &id); + if (fields != 2) + return -EINVAL; + sprintf(devname, "dax%d.%d", region_id, id); + if (!sysfs_streq(buf, devname)) + return -EINVAL; + + mutex_lock(&dax_bus_lock); + dax_id = __dax_match_id(dax_drv, buf); + if (!dax_id) { + if (action == ID_ADD) { + dax_id = kzalloc(sizeof(*dax_id), GFP_KERNEL); + if (dax_id) { + strncpy(dax_id->dev_name, buf, DAX_NAME_LEN); + list_add(&dax_id->list, &dax_drv->ids); + } else + rc = -ENOMEM; + } else + /* nothing to remove */; + } else if (action == ID_REMOVE) { + list_del(&dax_id->list); + kfree(dax_id); + } else + /* dax_id already added */; + mutex_unlock(&dax_bus_lock); + + if (rc < 0) + return rc; + if (action == ID_ADD) + rc = driver_attach(drv); + if (rc) + return rc; + return count; +} + +static ssize_t new_id_store(struct device_driver *drv, const char *buf, + size_t count) +{ + return do_id_store(drv, buf, count, ID_ADD); +} +static DRIVER_ATTR_WO(new_id); + +static ssize_t remove_id_store(struct device_driver *drv, const char *buf, + size_t count) +{ + return do_id_store(drv, buf, count, ID_REMOVE); +} +static DRIVER_ATTR_WO(remove_id); + +static struct attribute *dax_drv_attrs[] = { + &driver_attr_new_id.attr, + &driver_attr_remove_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(dax_drv); + +static int dax_bus_match(struct device *dev, struct device_driver *drv); + +static struct bus_type dax_bus_type = { + .name = "dax", + .uevent = dax_bus_uevent, + .match = dax_bus_match, + .drv_groups = dax_drv_groups, +}; + +static int dax_bus_match(struct device *dev, struct device_driver *drv) +{ + struct dax_device_driver *dax_drv = to_dax_drv(drv); + + /* + * All but the 'device-dax' driver, which has 'match_always' + * set, requires an exact id match. + */ + if (dax_drv->match_always) + return 1; + + return dax_match_id(dax_drv, dev); +} + +/* + * Rely on the fact that drvdata is set before the attributes are + * registered, and that the attributes are unregistered before drvdata + * is cleared to assume that drvdata is always valid. + */ +static ssize_t id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dax_region *dax_region = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", dax_region->id); +} +static DEVICE_ATTR_RO(id); + +static ssize_t region_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dax_region *dax_region = dev_get_drvdata(dev); + + return sprintf(buf, "%llu\n", (unsigned long long) + resource_size(&dax_region->res)); +} +static struct device_attribute dev_attr_region_size = __ATTR(size, 0444, + region_size_show, NULL); + +static ssize_t align_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dax_region *dax_region = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", dax_region->align); +} +static DEVICE_ATTR_RO(align); + +static struct attribute *dax_region_attributes[] = { + &dev_attr_region_size.attr, + &dev_attr_align.attr, + &dev_attr_id.attr, + NULL, +}; + +static const struct attribute_group dax_region_attribute_group = { + .name = "dax_region", + .attrs = dax_region_attributes, +}; + +static const struct attribute_group *dax_region_attribute_groups[] = { + &dax_region_attribute_group, + NULL, +}; + +static void dax_region_free(struct kref *kref) +{ + struct dax_region *dax_region; + + dax_region = container_of(kref, struct dax_region, kref); + kfree(dax_region); +} + +void dax_region_put(struct dax_region *dax_region) +{ + kref_put(&dax_region->kref, dax_region_free); +} +EXPORT_SYMBOL_GPL(dax_region_put); + +static void dax_region_unregister(void *region) +{ + struct dax_region *dax_region = region; + + sysfs_remove_groups(&dax_region->dev->kobj, + dax_region_attribute_groups); + dax_region_put(dax_region); +} + +struct dax_region *alloc_dax_region(struct device *parent, int region_id, + struct resource *res, int target_node, unsigned int align, + unsigned long pfn_flags) +{ + struct dax_region *dax_region; + + /* + * The DAX core assumes that it can store its private data in + * parent->driver_data. This WARN is a reminder / safeguard for + * developers of device-dax drivers. + */ + if (dev_get_drvdata(parent)) { + dev_WARN(parent, "dax core failed to setup private data\n"); + return NULL; + } + + if (!IS_ALIGNED(res->start, align) + || !IS_ALIGNED(resource_size(res), align)) + return NULL; + + dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL); + if (!dax_region) + return NULL; + + dev_set_drvdata(parent, dax_region); + memcpy(&dax_region->res, res, sizeof(*res)); + dax_region->pfn_flags = pfn_flags; + kref_init(&dax_region->kref); + dax_region->id = region_id; + dax_region->align = align; + dax_region->dev = parent; + dax_region->target_node = target_node; + if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { + kfree(dax_region); + return NULL; + } + + kref_get(&dax_region->kref); + if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region)) + return NULL; + return dax_region; +} +EXPORT_SYMBOL_GPL(alloc_dax_region); + +static ssize_t size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + unsigned long long size = resource_size(&dev_dax->region->res); + + return sprintf(buf, "%llu\n", size); +} +static DEVICE_ATTR_RO(size); + +static int dev_dax_target_node(struct dev_dax *dev_dax) +{ + struct dax_region *dax_region = dev_dax->region; + + return dax_region->target_node; +} + +static ssize_t target_node_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + + return sprintf(buf, "%d\n", dev_dax_target_node(dev_dax)); +} +static DEVICE_ATTR_RO(target_node); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + /* + * We only ever expect to handle device-dax instances, i.e. the + * @type argument to MODULE_ALIAS_DAX_DEVICE() is always zero + */ + return sprintf(buf, DAX_DEVICE_MODALIAS_FMT "\n", 0); +} +static DEVICE_ATTR_RO(modalias); + +static umode_t dev_dax_visible(struct kobject *kobj, struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct dev_dax *dev_dax = to_dev_dax(dev); + + if (a == &dev_attr_target_node.attr && dev_dax_target_node(dev_dax) < 0) + return 0; + return a->mode; +} + +static struct attribute *dev_dax_attributes[] = { + &dev_attr_modalias.attr, + &dev_attr_size.attr, + &dev_attr_target_node.attr, + NULL, +}; + +static const struct attribute_group dev_dax_attribute_group = { + .attrs = dev_dax_attributes, + .is_visible = dev_dax_visible, +}; + +static const struct attribute_group *dax_attribute_groups[] = { + &dev_dax_attribute_group, + NULL, +}; + +void kill_dev_dax(struct dev_dax *dev_dax) +{ + struct dax_device *dax_dev = dev_dax->dax_dev; + struct inode *inode = dax_inode(dax_dev); + + kill_dax(dax_dev); + unmap_mapping_range(inode->i_mapping, 0, 0, 1); +} +EXPORT_SYMBOL_GPL(kill_dev_dax); + +static void dev_dax_release(struct device *dev) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + struct dax_region *dax_region = dev_dax->region; + struct dax_device *dax_dev = dev_dax->dax_dev; + + dax_region_put(dax_region); + put_dax(dax_dev); + kfree(dev_dax); +} + +static void unregister_dev_dax(void *dev) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + + dev_dbg(dev, "%s\n", __func__); + + kill_dev_dax(dev_dax); + device_del(dev); + put_device(dev); +} + +struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id, + struct dev_pagemap *pgmap, enum dev_dax_subsys subsys) +{ + struct device *parent = dax_region->dev; + struct dax_device *dax_dev; + struct dev_dax *dev_dax; + struct inode *inode; + struct device *dev; + int rc = -ENOMEM; + + if (id < 0) + return ERR_PTR(-EINVAL); + + dev_dax = kzalloc(sizeof(*dev_dax), GFP_KERNEL); + if (!dev_dax) + return ERR_PTR(-ENOMEM); + + memcpy(&dev_dax->pgmap, pgmap, sizeof(*pgmap)); + + /* + * No 'host' or dax_operations since there is no access to this + * device outside of mmap of the resulting character device. + */ + dax_dev = alloc_dax(dev_dax, NULL, NULL); + if (!dax_dev) + goto err; + + /* a device_dax instance is dead while the driver is not attached */ + kill_dax(dax_dev); + + /* from here on we're committed to teardown via dax_dev_release() */ + dev = &dev_dax->dev; + device_initialize(dev); + + dev_dax->dax_dev = dax_dev; + dev_dax->region = dax_region; + dev_dax->target_node = dax_region->target_node; + kref_get(&dax_region->kref); + + inode = dax_inode(dax_dev); + dev->devt = inode->i_rdev; + if (subsys == DEV_DAX_BUS) + dev->bus = &dax_bus_type; + else + dev->class = dax_class; + dev->parent = parent; + dev->groups = dax_attribute_groups; + dev->release = dev_dax_release; + dev_set_name(dev, "dax%d.%d", dax_region->id, id); + + rc = device_add(dev); + if (rc) { + kill_dev_dax(dev_dax); + put_device(dev); + return ERR_PTR(rc); + } + + rc = devm_add_action_or_reset(dax_region->dev, unregister_dev_dax, dev); + if (rc) + return ERR_PTR(rc); + + return dev_dax; + + err: + kfree(dev_dax); + + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(__devm_create_dev_dax); + +static int match_always_count; + +int __dax_driver_register(struct dax_device_driver *dax_drv, + struct module *module, const char *mod_name) +{ + struct device_driver *drv = &dax_drv->drv; + int rc = 0; + + INIT_LIST_HEAD(&dax_drv->ids); + drv->owner = module; + drv->name = mod_name; + drv->mod_name = mod_name; + drv->bus = &dax_bus_type; + + /* there can only be one default driver */ + mutex_lock(&dax_bus_lock); + match_always_count += dax_drv->match_always; + if (match_always_count > 1) { + match_always_count--; + WARN_ON(1); + rc = -EINVAL; + } + mutex_unlock(&dax_bus_lock); + if (rc) + return rc; + return driver_register(drv); +} +EXPORT_SYMBOL_GPL(__dax_driver_register); + +void dax_driver_unregister(struct dax_device_driver *dax_drv) +{ + struct device_driver *drv = &dax_drv->drv; + struct dax_id *dax_id, *_id; + + mutex_lock(&dax_bus_lock); + match_always_count -= dax_drv->match_always; + list_for_each_entry_safe(dax_id, _id, &dax_drv->ids, list) { + list_del(&dax_id->list); + kfree(dax_id); + } + mutex_unlock(&dax_bus_lock); + driver_unregister(drv); +} +EXPORT_SYMBOL_GPL(dax_driver_unregister); + +int __init dax_bus_init(void) +{ + int rc; + + if (IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)) { + dax_class = class_create(THIS_MODULE, "dax"); + if (IS_ERR(dax_class)) + return PTR_ERR(dax_class); + } + + rc = bus_register(&dax_bus_type); + if (rc) + class_destroy(dax_class); + return rc; +} + +void __exit dax_bus_exit(void) +{ + bus_unregister(&dax_bus_type); + class_destroy(dax_class); +} diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h new file mode 100644 index 000000000000..8619e3299943 --- /dev/null +++ b/drivers/dax/bus.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ +#ifndef __DAX_BUS_H__ +#define __DAX_BUS_H__ +#include <linux/device.h> + +struct dev_dax; +struct resource; +struct dax_device; +struct dax_region; +void dax_region_put(struct dax_region *dax_region); +struct dax_region *alloc_dax_region(struct device *parent, int region_id, + struct resource *res, int target_node, unsigned int align, + unsigned long flags); + +enum dev_dax_subsys { + DEV_DAX_BUS, + DEV_DAX_CLASS, +}; + +struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id, + struct dev_pagemap *pgmap, enum dev_dax_subsys subsys); + +static inline struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, + int id, struct dev_pagemap *pgmap) +{ + return __devm_create_dev_dax(dax_region, id, pgmap, DEV_DAX_BUS); +} + +/* to be deleted when DEV_DAX_CLASS is removed */ +struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys); + +struct dax_device_driver { + struct device_driver drv; + struct list_head ids; + int match_always; +}; + +int __dax_driver_register(struct dax_device_driver *dax_drv, + struct module *module, const char *mod_name); +#define dax_driver_register(driver) \ + __dax_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) +void dax_driver_unregister(struct dax_device_driver *dax_drv); +void kill_dev_dax(struct dev_dax *dev_dax); + +#if IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT) +int dev_dax_probe(struct device *dev); +#endif + +/* + * While run_dax() is potentially a generic operation that could be + * defined in include/linux/dax.h we don't want to grow any users + * outside of drivers/dax/ + */ +void run_dax(struct dax_device *dax_dev); + +#define MODULE_ALIAS_DAX_DEVICE(type) \ + MODULE_ALIAS("dax:t" __stringify(type) "*") +#define DAX_DEVICE_MODALIAS_FMT "dax:t%d" + +#endif /* __DAX_BUS_H__ */ diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index b6fc4f04636d..a45612148ca0 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -16,10 +16,17 @@ #include <linux/device.h> #include <linux/cdev.h> +/* private routines between core files */ +struct dax_device; +struct dax_device *inode_dax(struct inode *inode); +struct inode *dax_inode(struct dax_device *dax_dev); +int dax_bus_init(void); +void dax_bus_exit(void); + /** * struct dax_region - mapping infrastructure for dax devices * @id: kernel-wide unique region for a memory range - * @base: linear address corresponding to @res + * @target_node: effective numa node if this memory range is onlined * @kref: to pin while other agents have a need to do lookups * @dev: parent device backing this region * @align: allocation and mapping alignment for child dax devices @@ -28,8 +35,7 @@ */ struct dax_region { int id; - struct ida ida; - void *base; + int target_node; struct kref kref; struct device *dev; unsigned int align; @@ -38,20 +44,28 @@ struct dax_region { }; /** - * struct dev_dax - instance data for a subdivision of a dax region + * struct dev_dax - instance data for a subdivision of a dax region, and + * data while the device is activated in the driver. * @region - parent region * @dax_dev - core dax functionality + * @target_node: effective numa node if dev_dax memory range is onlined * @dev - device core - * @id - child id in the region - * @num_resources - number of physical address extents in this device - * @res - array of physical address ranges + * @pgmap - pgmap for memmap setup / lifetime (driver owned) + * @ref: pgmap reference count (driver owned) + * @cmp: @ref final put completion (driver owned) */ struct dev_dax { struct dax_region *region; struct dax_device *dax_dev; + int target_node; struct device dev; - int id; - int num_resources; - struct resource res[0]; + struct dev_pagemap pgmap; + struct percpu_ref ref; + struct completion cmp; }; + +static inline struct dev_dax *to_dev_dax(struct device *dev) +{ + return container_of(dev, struct dev_dax, dev); +} #endif diff --git a/drivers/dax/dax.h b/drivers/dax/dax.h deleted file mode 100644 index f9e5feea742c..000000000000 --- a/drivers/dax/dax.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright(c) 2016 - 2017 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#ifndef __DAX_H__ -#define __DAX_H__ -struct dax_device; -struct dax_device *inode_dax(struct inode *inode); -struct inode *dax_inode(struct dax_device *dax_dev); -#endif /* __DAX_H__ */ diff --git a/drivers/dax/device-dax.h b/drivers/dax/device-dax.h deleted file mode 100644 index 688b051750bd..000000000000 --- a/drivers/dax/device-dax.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#ifndef __DEVICE_DAX_H__ -#define __DEVICE_DAX_H__ -struct device; -struct dev_dax; -struct resource; -struct dax_region; -void dax_region_put(struct dax_region *dax_region); -struct dax_region *alloc_dax_region(struct device *parent, - int region_id, struct resource *res, unsigned int align, - void *addr, unsigned long flags); -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, - int id, struct resource *res, int count); -#endif /* __DEVICE_DAX_H__ */ diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 948806e57cee..e428468ab661 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -1,15 +1,6 @@ -/* - * Copyright(c) 2016 - 2017 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-2018 Intel Corporation. All rights reserved. */ +#include <linux/memremap.h> #include <linux/pagemap.h> #include <linux/module.h> #include <linux/device.h> @@ -21,161 +12,39 @@ #include <linux/mm.h> #include <linux/mman.h> #include "dax-private.h" -#include "dax.h" +#include "bus.h" -static struct class *dax_class; - -/* - * Rely on the fact that drvdata is set before the attributes are - * registered, and that the attributes are unregistered before drvdata - * is cleared to assume that drvdata is always valid. - */ -static ssize_t id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dax_region *dax_region = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", dax_region->id); -} -static DEVICE_ATTR_RO(id); - -static ssize_t region_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dax_region *dax_region = dev_get_drvdata(dev); - - return sprintf(buf, "%llu\n", (unsigned long long) - resource_size(&dax_region->res)); -} -static struct device_attribute dev_attr_region_size = __ATTR(size, 0444, - region_size_show, NULL); - -static ssize_t align_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dax_region *dax_region = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", dax_region->align); -} -static DEVICE_ATTR_RO(align); - -static struct attribute *dax_region_attributes[] = { - &dev_attr_region_size.attr, - &dev_attr_align.attr, - &dev_attr_id.attr, - NULL, -}; - -static const struct attribute_group dax_region_attribute_group = { - .name = "dax_region", - .attrs = dax_region_attributes, -}; - -static const struct attribute_group *dax_region_attribute_groups[] = { - &dax_region_attribute_group, - NULL, -}; - -static void dax_region_free(struct kref *kref) -{ - struct dax_region *dax_region; - - dax_region = container_of(kref, struct dax_region, kref); - kfree(dax_region); -} - -void dax_region_put(struct dax_region *dax_region) +static struct dev_dax *ref_to_dev_dax(struct percpu_ref *ref) { - kref_put(&dax_region->kref, dax_region_free); + return container_of(ref, struct dev_dax, ref); } -EXPORT_SYMBOL_GPL(dax_region_put); -static void dax_region_unregister(void *region) +static void dev_dax_percpu_release(struct percpu_ref *ref) { - struct dax_region *dax_region = region; + struct dev_dax *dev_dax = ref_to_dev_dax(ref); - sysfs_remove_groups(&dax_region->dev->kobj, - dax_region_attribute_groups); - dax_region_put(dax_region); + dev_dbg(&dev_dax->dev, "%s\n", __func__); + complete(&dev_dax->cmp); } -struct dax_region *alloc_dax_region(struct device *parent, int region_id, - struct resource *res, unsigned int align, void *addr, - unsigned long pfn_flags) +static void dev_dax_percpu_exit(void *data) { - struct dax_region *dax_region; - - /* - * The DAX core assumes that it can store its private data in - * parent->driver_data. This WARN is a reminder / safeguard for - * developers of device-dax drivers. - */ - if (dev_get_drvdata(parent)) { - dev_WARN(parent, "dax core failed to setup private data\n"); - return NULL; - } - - if (!IS_ALIGNED(res->start, align) - || !IS_ALIGNED(resource_size(res), align)) - return NULL; - - dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL); - if (!dax_region) - return NULL; - - dev_set_drvdata(parent, dax_region); - memcpy(&dax_region->res, res, sizeof(*res)); - dax_region->pfn_flags = pfn_flags; - kref_init(&dax_region->kref); - dax_region->id = region_id; - ida_init(&dax_region->ida); - dax_region->align = align; - dax_region->dev = parent; - dax_region->base = addr; - if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { - kfree(dax_region); - return NULL; - } + struct percpu_ref *ref = data; + struct dev_dax *dev_dax = ref_to_dev_dax(ref); - kref_get(&dax_region->kref); - if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region)) - return NULL; - return dax_region; + dev_dbg(&dev_dax->dev, "%s\n", __func__); + wait_for_completion(&dev_dax->cmp); + percpu_ref_exit(ref); } -EXPORT_SYMBOL_GPL(alloc_dax_region); -static struct dev_dax *to_dev_dax(struct device *dev) +static void dev_dax_percpu_kill(struct percpu_ref *data) { - return container_of(dev, struct dev_dax, dev); -} - -static ssize_t size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dev_dax *dev_dax = to_dev_dax(dev); - unsigned long long size = 0; - int i; + struct percpu_ref *ref = data; + struct dev_dax *dev_dax = ref_to_dev_dax(ref); - for (i = 0; i < dev_dax->num_resources; i++) - size += resource_size(&dev_dax->res[i]); - - return sprintf(buf, "%llu\n", size); + dev_dbg(&dev_dax->dev, "%s\n", __func__); + percpu_ref_kill(ref); } -static DEVICE_ATTR_RO(size); - -static struct attribute *dev_dax_attributes[] = { - &dev_attr_size.attr, - NULL, -}; - -static const struct attribute_group dev_dax_attribute_group = { - .attrs = dev_dax_attributes, -}; - -static const struct attribute_group *dax_attribute_groups[] = { - &dev_dax_attribute_group, - NULL, -}; static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, const char *func) @@ -226,21 +95,11 @@ static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, __weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, unsigned long size) { - struct resource *res; - /* gcc-4.6.3-nolibc for i386 complains that this is uninitialized */ - phys_addr_t uninitialized_var(phys); - int i; - - for (i = 0; i < dev_dax->num_resources; i++) { - res = &dev_dax->res[i]; - phys = pgoff * PAGE_SIZE + res->start; - if (phys >= res->start && phys <= res->end) - break; - pgoff -= PHYS_PFN(resource_size(res)); - } + struct resource *res = &dev_dax->region->res; + phys_addr_t phys; - if (i < dev_dax->num_resources) { - res = &dev_dax->res[i]; + phys = pgoff * PAGE_SIZE + res->start; + if (phys >= res->start && phys <= res->end) { if (phys + size - 1 <= res->end) return phys; } @@ -576,152 +435,100 @@ static const struct file_operations dax_fops = { .mmap_supported_flags = MAP_SYNC, }; -static void dev_dax_release(struct device *dev) +static void dev_dax_cdev_del(void *cdev) { - struct dev_dax *dev_dax = to_dev_dax(dev); - struct dax_region *dax_region = dev_dax->region; - struct dax_device *dax_dev = dev_dax->dax_dev; - - if (dev_dax->id >= 0) - ida_simple_remove(&dax_region->ida, dev_dax->id); - dax_region_put(dax_region); - put_dax(dax_dev); - kfree(dev_dax); + cdev_del(cdev); } -static void kill_dev_dax(struct dev_dax *dev_dax) +static void dev_dax_kill(void *dev_dax) { - struct dax_device *dax_dev = dev_dax->dax_dev; - struct inode *inode = dax_inode(dax_dev); - - kill_dax(dax_dev); - unmap_mapping_range(inode->i_mapping, 0, 0, 1); + kill_dev_dax(dev_dax); } -static void unregister_dev_dax(void *dev) +int dev_dax_probe(struct device *dev) { struct dev_dax *dev_dax = to_dev_dax(dev); struct dax_device *dax_dev = dev_dax->dax_dev; - struct inode *inode = dax_inode(dax_dev); - struct cdev *cdev = inode->i_cdev; - - dev_dbg(dev, "trace\n"); - - kill_dev_dax(dev_dax); - cdev_device_del(cdev, dev); - put_device(dev); -} - -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, - int id, struct resource *res, int count) -{ - struct device *parent = dax_region->dev; - struct dax_device *dax_dev; - struct dev_dax *dev_dax; + struct resource *res = &dev_dax->region->res; struct inode *inode; - struct device *dev; struct cdev *cdev; - int rc, i; - - if (!count) - return ERR_PTR(-EINVAL); - - dev_dax = kzalloc(struct_size(dev_dax, res, count), GFP_KERNEL); - if (!dev_dax) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < count; i++) { - if (!IS_ALIGNED(res[i].start, dax_region->align) - || !IS_ALIGNED(resource_size(&res[i]), - dax_region->align)) { - rc = -EINVAL; - break; - } - dev_dax->res[i].start = res[i].start; - dev_dax->res[i].end = res[i].end; + void *addr; + int rc; + + /* 1:1 map region resource range to device-dax instance range */ + if (!devm_request_mem_region(dev, res->start, resource_size(res), + dev_name(dev))) { + dev_warn(dev, "could not reserve region %pR\n", res); + return -EBUSY; } - if (i < count) - goto err_id; + init_completion(&dev_dax->cmp); + rc = percpu_ref_init(&dev_dax->ref, dev_dax_percpu_release, 0, + GFP_KERNEL); + if (rc) + return rc; - if (id < 0) { - id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL); - dev_dax->id = id; - if (id < 0) { - rc = id; - goto err_id; - } - } else { - /* region provider owns @id lifetime */ - dev_dax->id = -1; - } + rc = devm_add_action_or_reset(dev, dev_dax_percpu_exit, &dev_dax->ref); + if (rc) + return rc; - /* - * No 'host' or dax_operations since there is no access to this - * device outside of mmap of the resulting character device. - */ - dax_dev = alloc_dax(dev_dax, NULL, NULL); - if (!dax_dev) { - rc = -ENOMEM; - goto err_dax; + dev_dax->pgmap.ref = &dev_dax->ref; + dev_dax->pgmap.kill = dev_dax_percpu_kill; + addr = devm_memremap_pages(dev, &dev_dax->pgmap); + if (IS_ERR(addr)) { + devm_remove_action(dev, dev_dax_percpu_exit, &dev_dax->ref); + percpu_ref_exit(&dev_dax->ref); + return PTR_ERR(addr); } - /* from here on we're committed to teardown via dax_dev_release() */ - dev = &dev_dax->dev; - device_initialize(dev); - inode = dax_inode(dax_dev); cdev = inode->i_cdev; cdev_init(cdev, &dax_fops); - cdev->owner = parent->driver->owner; - - dev_dax->num_resources = count; - dev_dax->dax_dev = dax_dev; - dev_dax->region = dax_region; - kref_get(&dax_region->kref); - - dev->devt = inode->i_rdev; - dev->class = dax_class; - dev->parent = parent; - dev->groups = dax_attribute_groups; - dev->release = dev_dax_release; - dev_set_name(dev, "dax%d.%d", dax_region->id, id); - - rc = cdev_device_add(cdev, dev); - if (rc) { - kill_dev_dax(dev_dax); - put_device(dev); - return ERR_PTR(rc); - } - - rc = devm_add_action_or_reset(dax_region->dev, unregister_dev_dax, dev); + if (dev->class) { + /* for the CONFIG_DEV_DAX_PMEM_COMPAT case */ + cdev->owner = dev->parent->driver->owner; + } else + cdev->owner = dev->driver->owner; + cdev_set_parent(cdev, &dev->kobj); + rc = cdev_add(cdev, dev->devt, 1); if (rc) - return ERR_PTR(rc); + return rc; - return dev_dax; + rc = devm_add_action_or_reset(dev, dev_dax_cdev_del, cdev); + if (rc) + return rc; - err_dax: - if (dev_dax->id >= 0) - ida_simple_remove(&dax_region->ida, dev_dax->id); - err_id: - kfree(dev_dax); + run_dax(dax_dev); + return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax); +} +EXPORT_SYMBOL_GPL(dev_dax_probe); - return ERR_PTR(rc); +static int dev_dax_remove(struct device *dev) +{ + /* all probe actions are unwound by devm */ + return 0; } -EXPORT_SYMBOL_GPL(devm_create_dev_dax); + +static struct dax_device_driver device_dax_driver = { + .drv = { + .probe = dev_dax_probe, + .remove = dev_dax_remove, + }, + .match_always = 1, +}; static int __init dax_init(void) { - dax_class = class_create(THIS_MODULE, "dax"); - return PTR_ERR_OR_ZERO(dax_class); + return dax_driver_register(&device_dax_driver); } static void __exit dax_exit(void) { - class_destroy(dax_class); + dax_driver_unregister(&device_dax_driver); } MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); -subsys_initcall(dax_init); +module_init(dax_init); module_exit(dax_exit); +MODULE_ALIAS_DAX_DEVICE(0); diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c new file mode 100644 index 000000000000..a02318c6d28a --- /dev/null +++ b/drivers/dax/kmem.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-2019 Intel Corporation. All rights reserved. */ +#include <linux/memremap.h> +#include <linux/pagemap.h> +#include <linux/memory.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/pfn_t.h> +#include <linux/slab.h> +#include <linux/dax.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include "dax-private.h" +#include "bus.h" + +int dev_dax_kmem_probe(struct device *dev) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + struct resource *res = &dev_dax->region->res; + resource_size_t kmem_start; + resource_size_t kmem_size; + resource_size_t kmem_end; + struct resource *new_res; + int numa_node; + int rc; + + /* + * Ensure good NUMA information for the persistent memory. + * Without this check, there is a risk that slow memory + * could be mixed in a node with faster memory, causing + * unavoidable performance issues. + */ + numa_node = dev_dax->target_node; + if (numa_node < 0) { + dev_warn(dev, "rejecting DAX region %pR with invalid node: %d\n", + res, numa_node); + return -EINVAL; + } + + /* Hotplug starting at the beginning of the next block: */ + kmem_start = ALIGN(res->start, memory_block_size_bytes()); + + kmem_size = resource_size(res); + /* Adjust the size down to compensate for moving up kmem_start: */ + kmem_size -= kmem_start - res->start; + /* Align the size down to cover only complete blocks: */ + kmem_size &= ~(memory_block_size_bytes() - 1); + kmem_end = kmem_start + kmem_size; + + /* Region is permanently reserved. Hot-remove not yet implemented. */ + new_res = request_mem_region(kmem_start, kmem_size, dev_name(dev)); + if (!new_res) { + dev_warn(dev, "could not reserve region [%pa-%pa]\n", + &kmem_start, &kmem_end); + return -EBUSY; + } + + /* + * Set flags appropriate for System RAM. Leave ..._BUSY clear + * so that add_memory() can add a child resource. Do not + * inherit flags from the parent since it may set new flags + * unknown to us that will break add_memory() below. + */ + new_res->flags = IORESOURCE_SYSTEM_RAM; + new_res->name = dev_name(dev); + + rc = add_memory(numa_node, new_res->start, resource_size(new_res)); + if (rc) + return rc; + + return 0; +} + +static int dev_dax_kmem_remove(struct device *dev) +{ + /* + * Purposely leak the request_mem_region() for the device-dax + * range and return '0' to ->remove() attempts. The removal of + * the device from the driver always succeeds, but the region + * is permanently pinned as reserved by the unreleased + * request_mem_region(). + */ + return 0; +} + +static struct dax_device_driver device_dax_kmem_driver = { + .drv = { + .probe = dev_dax_kmem_probe, + .remove = dev_dax_kmem_remove, + }, +}; + +static int __init dax_kmem_init(void) +{ + return dax_driver_register(&device_dax_kmem_driver); +} + +static void __exit dax_kmem_exit(void) +{ + dax_driver_unregister(&device_dax_kmem_driver); +} + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +module_init(dax_kmem_init); +module_exit(dax_kmem_exit); +MODULE_ALIAS_DAX_DEVICE(0); diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c deleted file mode 100644 index 2c1f459c0c63..000000000000 --- a/drivers/dax/pmem.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#include <linux/percpu-refcount.h> -#include <linux/memremap.h> -#include <linux/module.h> -#include <linux/pfn_t.h> -#include "../nvdimm/pfn.h" -#include "../nvdimm/nd.h" -#include "device-dax.h" - -struct dax_pmem { - struct device *dev; - struct percpu_ref ref; - struct dev_pagemap pgmap; - struct completion cmp; -}; - -static struct dax_pmem *to_dax_pmem(struct percpu_ref *ref) -{ - return container_of(ref, struct dax_pmem, ref); -} - -static void dax_pmem_percpu_release(struct percpu_ref *ref) -{ - struct dax_pmem *dax_pmem = to_dax_pmem(ref); - - dev_dbg(dax_pmem->dev, "trace\n"); - complete(&dax_pmem->cmp); -} - -static void dax_pmem_percpu_exit(void *data) -{ - struct percpu_ref *ref = data; - struct dax_pmem *dax_pmem = to_dax_pmem(ref); - - dev_dbg(dax_pmem->dev, "trace\n"); - wait_for_completion(&dax_pmem->cmp); - percpu_ref_exit(ref); -} - -static void dax_pmem_percpu_kill(struct percpu_ref *ref) -{ - struct dax_pmem *dax_pmem = to_dax_pmem(ref); - - dev_dbg(dax_pmem->dev, "trace\n"); - percpu_ref_kill(ref); -} - -static int dax_pmem_probe(struct device *dev) -{ - void *addr; - struct resource res; - int rc, id, region_id; - struct nd_pfn_sb *pfn_sb; - struct dev_dax *dev_dax; - struct dax_pmem *dax_pmem; - struct nd_namespace_io *nsio; - struct dax_region *dax_region; - struct nd_namespace_common *ndns; - struct nd_dax *nd_dax = to_nd_dax(dev); - struct nd_pfn *nd_pfn = &nd_dax->nd_pfn; - - ndns = nvdimm_namespace_common_probe(dev); - if (IS_ERR(ndns)) - return PTR_ERR(ndns); - nsio = to_nd_namespace_io(&ndns->dev); - - dax_pmem = devm_kzalloc(dev, sizeof(*dax_pmem), GFP_KERNEL); - if (!dax_pmem) - return -ENOMEM; - - /* parse the 'pfn' info block via ->rw_bytes */ - rc = devm_nsio_enable(dev, nsio); - if (rc) - return rc; - rc = nvdimm_setup_pfn(nd_pfn, &dax_pmem->pgmap); - if (rc) - return rc; - devm_nsio_disable(dev, nsio); - - pfn_sb = nd_pfn->pfn_sb; - - if (!devm_request_mem_region(dev, nsio->res.start, - resource_size(&nsio->res), - dev_name(&ndns->dev))) { - dev_warn(dev, "could not reserve region %pR\n", &nsio->res); - return -EBUSY; - } - - dax_pmem->dev = dev; - init_completion(&dax_pmem->cmp); - rc = percpu_ref_init(&dax_pmem->ref, dax_pmem_percpu_release, 0, - GFP_KERNEL); - if (rc) - return rc; - - rc = devm_add_action(dev, dax_pmem_percpu_exit, &dax_pmem->ref); - if (rc) { - percpu_ref_exit(&dax_pmem->ref); - return rc; - } - - dax_pmem->pgmap.ref = &dax_pmem->ref; - dax_pmem->pgmap.kill = dax_pmem_percpu_kill; - addr = devm_memremap_pages(dev, &dax_pmem->pgmap); - if (IS_ERR(addr)) - return PTR_ERR(addr); - - /* adjust the dax_region resource to the start of data */ - memcpy(&res, &dax_pmem->pgmap.res, sizeof(res)); - res.start += le64_to_cpu(pfn_sb->dataoff); - - rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", ®ion_id, &id); - if (rc != 2) - return -EINVAL; - - dax_region = alloc_dax_region(dev, region_id, &res, - le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP); - if (!dax_region) - return -ENOMEM; - - /* TODO: support for subdividing a dax region... */ - dev_dax = devm_create_dev_dax(dax_region, id, &res, 1); - - /* child dev_dax instances now own the lifetime of the dax_region */ - dax_region_put(dax_region); - - return PTR_ERR_OR_ZERO(dev_dax); -} - -static struct nd_device_driver dax_pmem_driver = { - .probe = dax_pmem_probe, - .drv = { - .name = "dax_pmem", - }, - .type = ND_DRIVER_DAX_PMEM, -}; - -module_nd_driver(dax_pmem_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Intel Corporation"); -MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM); diff --git a/drivers/dax/pmem/Makefile b/drivers/dax/pmem/Makefile new file mode 100644 index 000000000000..e2e79bd3fdcf --- /dev/null +++ b/drivers/dax/pmem/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o +obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem_core.o +obj-$(CONFIG_DEV_DAX_PMEM_COMPAT) += dax_pmem_compat.o + +dax_pmem-y := pmem.o +dax_pmem_core-y := core.o +dax_pmem_compat-y := compat.o diff --git a/drivers/dax/pmem/compat.c b/drivers/dax/pmem/compat.c new file mode 100644 index 000000000000..d7b15e6f30c5 --- /dev/null +++ b/drivers/dax/pmem/compat.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ +#include <linux/percpu-refcount.h> +#include <linux/memremap.h> +#include <linux/module.h> +#include <linux/pfn_t.h> +#include <linux/nd.h> +#include "../bus.h" + +/* we need the private definitions to implement compat suport */ +#include "../dax-private.h" + +static int dax_pmem_compat_probe(struct device *dev) +{ + struct dev_dax *dev_dax = __dax_pmem_probe(dev, DEV_DAX_CLASS); + int rc; + + if (IS_ERR(dev_dax)) + return PTR_ERR(dev_dax); + + if (!devres_open_group(&dev_dax->dev, dev_dax, GFP_KERNEL)) + return -ENOMEM; + + device_lock(&dev_dax->dev); + rc = dev_dax_probe(&dev_dax->dev); + device_unlock(&dev_dax->dev); + + devres_close_group(&dev_dax->dev, dev_dax); + if (rc) + devres_release_group(&dev_dax->dev, dev_dax); + + return rc; +} + +static int dax_pmem_compat_release(struct device *dev, void *data) +{ + device_lock(dev); + devres_release_group(dev, to_dev_dax(dev)); + device_unlock(dev); + + return 0; +} + +static int dax_pmem_compat_remove(struct device *dev) +{ + device_for_each_child(dev, NULL, dax_pmem_compat_release); + return 0; +} + +static struct nd_device_driver dax_pmem_compat_driver = { + .probe = dax_pmem_compat_probe, + .remove = dax_pmem_compat_remove, + .drv = { + .name = "dax_pmem_compat", + }, + .type = ND_DRIVER_DAX_PMEM, +}; + +static int __init dax_pmem_compat_init(void) +{ + return nd_driver_register(&dax_pmem_compat_driver); +} +module_init(dax_pmem_compat_init); + +static void __exit dax_pmem_compat_exit(void) +{ + driver_unregister(&dax_pmem_compat_driver.drv); +} +module_exit(dax_pmem_compat_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM); diff --git a/drivers/dax/pmem/core.c b/drivers/dax/pmem/core.c new file mode 100644 index 000000000000..f71019ce0647 --- /dev/null +++ b/drivers/dax/pmem/core.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ +#include <linux/memremap.h> +#include <linux/module.h> +#include <linux/pfn_t.h> +#include "../../nvdimm/pfn.h" +#include "../../nvdimm/nd.h" +#include "../bus.h" + +struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys) +{ + struct resource res; + int rc, id, region_id; + resource_size_t offset; + struct nd_pfn_sb *pfn_sb; + struct dev_dax *dev_dax; + struct nd_namespace_io *nsio; + struct dax_region *dax_region; + struct dev_pagemap pgmap = { 0 }; + struct nd_namespace_common *ndns; + struct nd_dax *nd_dax = to_nd_dax(dev); + struct nd_pfn *nd_pfn = &nd_dax->nd_pfn; + struct nd_region *nd_region = to_nd_region(dev->parent); + + ndns = nvdimm_namespace_common_probe(dev); + if (IS_ERR(ndns)) + return ERR_CAST(ndns); + nsio = to_nd_namespace_io(&ndns->dev); + + /* parse the 'pfn' info block via ->rw_bytes */ + rc = devm_nsio_enable(dev, nsio); + if (rc) + return ERR_PTR(rc); + rc = nvdimm_setup_pfn(nd_pfn, &pgmap); + if (rc) + return ERR_PTR(rc); + devm_nsio_disable(dev, nsio); + + /* reserve the metadata area, device-dax will reserve the data */ + pfn_sb = nd_pfn->pfn_sb; + offset = le64_to_cpu(pfn_sb->dataoff); + if (!devm_request_mem_region(dev, nsio->res.start, offset, + dev_name(&ndns->dev))) { + dev_warn(dev, "could not reserve metadata\n"); + return ERR_PTR(-EBUSY); + } + + rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", ®ion_id, &id); + if (rc != 2) + return ERR_PTR(-EINVAL); + + /* adjust the dax_region resource to the start of data */ + memcpy(&res, &pgmap.res, sizeof(res)); + res.start += offset; + dax_region = alloc_dax_region(dev, region_id, &res, + nd_region->target_node, le32_to_cpu(pfn_sb->align), + PFN_DEV|PFN_MAP); + if (!dax_region) + return ERR_PTR(-ENOMEM); + + dev_dax = __devm_create_dev_dax(dax_region, id, &pgmap, subsys); + + /* child dev_dax instances now own the lifetime of the dax_region */ + dax_region_put(dax_region); + + return dev_dax; +} +EXPORT_SYMBOL_GPL(__dax_pmem_probe); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Intel Corporation"); diff --git a/drivers/dax/pmem/pmem.c b/drivers/dax/pmem/pmem.c new file mode 100644 index 000000000000..0ae4238a0ef8 --- /dev/null +++ b/drivers/dax/pmem/pmem.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ +#include <linux/percpu-refcount.h> +#include <linux/memremap.h> +#include <linux/module.h> +#include <linux/pfn_t.h> +#include <linux/nd.h> +#include "../bus.h" + +static int dax_pmem_probe(struct device *dev) +{ + return PTR_ERR_OR_ZERO(__dax_pmem_probe(dev, DEV_DAX_BUS)); +} + +static struct nd_device_driver dax_pmem_driver = { + .probe = dax_pmem_probe, + .drv = { + .name = "dax_pmem", + }, + .type = ND_DRIVER_DAX_PMEM, +}; + +static int __init dax_pmem_init(void) +{ + return nd_driver_register(&dax_pmem_driver); +} +module_init(dax_pmem_init); + +static void __exit dax_pmem_exit(void) +{ + driver_unregister(&dax_pmem_driver.drv); +} +module_exit(dax_pmem_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Intel Corporation"); +#if !IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT) +/* For compat builds, don't load this module by default */ +MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM); +#endif diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 6e928f37d084..0a339b85133e 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -22,6 +22,7 @@ #include <linux/uio.h> #include <linux/dax.h> #include <linux/fs.h> +#include "dax-private.h" static dev_t dax_devt; DEFINE_STATIC_SRCU(dax_srcu); @@ -86,12 +87,14 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize) { struct dax_device *dax_dev; bool dax_enabled = false; + pgoff_t pgoff, pgoff_end; struct request_queue *q; - pgoff_t pgoff; - int err, id; - pfn_t pfn; - long len; char buf[BDEVNAME_SIZE]; + void *kaddr, *end_kaddr; + pfn_t pfn, end_pfn; + sector_t last_page; + long len, len2; + int err, id; if (blocksize != PAGE_SIZE) { pr_debug("%s: error: unsupported blocksize for dax\n", @@ -113,6 +116,14 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize) return false; } + last_page = PFN_DOWN(i_size_read(bdev->bd_inode) - 1) * 8; + err = bdev_dax_pgoff(bdev, last_page, PAGE_SIZE, &pgoff_end); + if (err) { + pr_debug("%s: error: unaligned partition for dax\n", + bdevname(bdev, buf)); + return false; + } + dax_dev = dax_get_by_host(bdev->bd_disk->disk_name); if (!dax_dev) { pr_debug("%s: error: device does not support dax\n", @@ -121,14 +132,15 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize) } id = dax_read_lock(); - len = dax_direct_access(dax_dev, pgoff, 1, NULL, &pfn); + len = dax_direct_access(dax_dev, pgoff, 1, &kaddr, &pfn); + len2 = dax_direct_access(dax_dev, pgoff_end, 1, &end_kaddr, &end_pfn); dax_read_unlock(id); put_dax(dax_dev); - if (len < 1) { + if (len < 1 || len2 < 1) { pr_debug("%s: error: dax access failed (%ld)\n", - bdevname(bdev, buf), len); + bdevname(bdev, buf), len < 1 ? len : len2); return false; } @@ -143,13 +155,20 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize) */ WARN_ON(IS_ENABLED(CONFIG_ARCH_HAS_PMEM_API)); dax_enabled = true; - } else if (pfn_t_devmap(pfn)) { - struct dev_pagemap *pgmap; + } else if (pfn_t_devmap(pfn) && pfn_t_devmap(end_pfn)) { + struct dev_pagemap *pgmap, *end_pgmap; pgmap = get_dev_pagemap(pfn_t_to_pfn(pfn), NULL); - if (pgmap && pgmap->type == MEMORY_DEVICE_FS_DAX) + end_pgmap = get_dev_pagemap(pfn_t_to_pfn(end_pfn), NULL); + if (pgmap && pgmap == end_pgmap && pgmap->type == MEMORY_DEVICE_FS_DAX + && pfn_t_to_page(pfn)->pgmap == pgmap + && pfn_t_to_page(end_pfn)->pgmap == pgmap + && pfn_t_to_pfn(pfn) == PHYS_PFN(__pa(kaddr)) + && pfn_t_to_pfn(end_pfn) == PHYS_PFN(__pa(end_kaddr))) dax_enabled = true; put_dev_pagemap(pgmap); + put_dev_pagemap(end_pgmap); + } if (!dax_enabled) { @@ -365,11 +384,15 @@ void kill_dax(struct dax_device *dax_dev) spin_lock(&dax_host_lock); hlist_del_init(&dax_dev->list); spin_unlock(&dax_host_lock); - - dax_dev->private = NULL; } EXPORT_SYMBOL_GPL(kill_dax); +void run_dax(struct dax_device *dax_dev) +{ + set_bit(DAXDEV_ALIVE, &dax_dev->flags); +} +EXPORT_SYMBOL_GPL(run_dax); + static struct inode *dax_alloc_inode(struct super_block *sb) { struct dax_device *dax_dev; @@ -584,6 +607,8 @@ EXPORT_SYMBOL_GPL(dax_inode); void *dax_get_private(struct dax_device *dax_dev) { + if (!test_bit(DAXDEV_ALIVE, &dax_dev->flags)) + return NULL; return dax_dev->private; } EXPORT_SYMBOL_GPL(dax_get_private); @@ -597,7 +622,7 @@ static void init_once(void *_dax_dev) inode_init_once(inode); } -static int __dax_fs_init(void) +static int dax_fs_init(void) { int rc; @@ -629,35 +654,45 @@ static int __dax_fs_init(void) return rc; } -static void __dax_fs_exit(void) +static void dax_fs_exit(void) { kern_unmount(dax_mnt); unregister_filesystem(&dax_fs_type); kmem_cache_destroy(dax_cache); } -static int __init dax_fs_init(void) +static int __init dax_core_init(void) { int rc; - rc = __dax_fs_init(); + rc = dax_fs_init(); if (rc) return rc; rc = alloc_chrdev_region(&dax_devt, 0, MINORMASK+1, "dax"); if (rc) - __dax_fs_exit(); - return rc; + goto err_chrdev; + + rc = dax_bus_init(); + if (rc) + goto err_bus; + return 0; + +err_bus: + unregister_chrdev_region(dax_devt, MINORMASK+1); +err_chrdev: + dax_fs_exit(); + return 0; } -static void __exit dax_fs_exit(void) +static void __exit dax_core_exit(void) { unregister_chrdev_region(dax_devt, MINORMASK+1); ida_destroy(&dax_minor_ida); - __dax_fs_exit(); + dax_fs_exit(); } MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); -subsys_initcall(dax_fs_init); -module_exit(dax_fs_exit); +subsys_initcall(dax_core_init); +module_exit(dax_core_exit); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d2286c7f7222..0b1dfb5bf2d9 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -218,6 +218,20 @@ config FSL_EDMA multiplexing capability for DMA request sources(slot). This module can be found on Freescale Vybrid and LS-1 SoCs. +config FSL_QDMA + tristate "NXP Layerscape qDMA engine support" + depends on ARM || ARM64 + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + select DMA_ENGINE_RAID + select ASYNC_TX_ENABLE_CHANNEL_SWITCH + help + Support the NXP Layerscape qDMA engine with command queue and legacy mode. + Channel virtualization is supported through enqueuing of DMA jobs to, + or dequeuing DMA jobs from, different work queues. + This module can be found on NXP Layerscape SoCs. + The qdma driver only work on SoCs with a DPAA hardware block. + config FSL_RAID tristate "Freescale RAID engine Support" depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 09571a81353d..6126e1c3a875 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o +obj-$(CONFIG_FSL_QDMA) += fsl-qdma.o obj-$(CONFIG_FSL_RAID) += fsl_raid.o obj-$(CONFIG_HSU_DMA) += hsu/ obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 01d936c9fe89..a0a9cd76c1d4 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -134,7 +134,6 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) struct at_desc *ret = NULL; unsigned long flags; unsigned int i = 0; - LIST_HEAD(tmp_list); spin_lock_irqsave(&atchan->lock, flags); list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) { @@ -1387,8 +1386,6 @@ static int atc_pause(struct dma_chan *chan) int chan_id = atchan->chan_common.chan_id; unsigned long flags; - LIST_HEAD(list); - dev_vdbg(chan2dev(chan), "%s\n", __func__); spin_lock_irqsave(&atchan->lock, flags); @@ -1408,8 +1405,6 @@ static int atc_resume(struct dma_chan *chan) int chan_id = atchan->chan_common.chan_id; unsigned long flags; - LIST_HEAD(list); - dev_vdbg(chan2dev(chan), "%s\n", __func__); if (!atc_chan_is_paused(atchan)) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index ae10f5614f95..ec8a291d62ba 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -2,9 +2,6 @@ /* * BCM2835 DMA engine support * - * This driver only supports cyclic DMA transfers - * as needed for the I2S module. - * * Author: Florian Meier <florian.meier@koalo.de> * Copyright 2013 * @@ -42,7 +39,6 @@ struct bcm2835_dmadev { struct dma_device ddev; - spinlock_t lock; void __iomem *base; struct device_dma_parameters dma_parms; }; @@ -64,7 +60,6 @@ struct bcm2835_cb_entry { struct bcm2835_chan { struct virt_dma_chan vc; - struct list_head node; struct dma_slave_config cfg; unsigned int dreq; @@ -312,8 +307,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain( return NULL; /* allocate and setup the descriptor. */ - d = kzalloc(sizeof(*d) + frames * sizeof(struct bcm2835_cb_entry), - gfp); + d = kzalloc(struct_size(d, cb_list, frames), gfp); if (!d) return NULL; @@ -406,7 +400,7 @@ static void bcm2835_dma_fill_cb_chain_with_sg( } } -static int bcm2835_dma_abort(struct bcm2835_chan *c) +static void bcm2835_dma_abort(struct bcm2835_chan *c) { void __iomem *chan_base = c->chan_base; long int timeout = 10000; @@ -416,7 +410,7 @@ static int bcm2835_dma_abort(struct bcm2835_chan *c) * (The ACTIVE flag in the CS register is not a reliable indicator.) */ if (!readl(chan_base + BCM2835_DMA_ADDR)) - return 0; + return; /* Write 0 to the active bit - Pause the DMA */ writel(0, chan_base + BCM2835_DMA_CS); @@ -432,7 +426,6 @@ static int bcm2835_dma_abort(struct bcm2835_chan *c) "failed to complete outstanding writes\n"); writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS); - return 0; } static void bcm2835_dma_start_desc(struct bcm2835_chan *c) @@ -504,8 +497,12 @@ static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan) dev_dbg(dev, "Allocating DMA channel %d\n", c->ch); + /* + * Control blocks are 256 bit in length and must start at a 256 bit + * (32 byte) aligned address (BCM2835 ARM Peripherals, sec. 4.2.1.1). + */ c->cb_pool = dma_pool_create(dev_name(dev), dev, - sizeof(struct bcm2835_dma_cb), 0, 0); + sizeof(struct bcm2835_dma_cb), 32, 0); if (!c->cb_pool) { dev_err(dev, "unable to allocate descriptor pool\n"); return -ENOMEM; @@ -774,17 +771,11 @@ static int bcm2835_dma_slave_config(struct dma_chan *chan, static int bcm2835_dma_terminate_all(struct dma_chan *chan) { struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); - struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); unsigned long flags; LIST_HEAD(head); spin_lock_irqsave(&c->vc.lock, flags); - /* Prevent this channel being scheduled */ - spin_lock(&d->lock); - list_del_init(&c->node); - spin_unlock(&d->lock); - /* stop DMA activity */ if (c->desc) { vchan_terminate_vdesc(&c->desc->vd); @@ -817,7 +808,6 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, c->vc.desc_free = bcm2835_dma_desc_free; vchan_init(&c->vc, &d->ddev); - INIT_LIST_HEAD(&c->node); c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id); c->ch = chan_id; @@ -920,7 +910,6 @@ static int bcm2835_dma_probe(struct platform_device *pdev) od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); - spin_lock_init(&od->lock); platform_set_drvdata(pdev, od); diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 15b2453d2647..ffc0adc2f6ce 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -367,8 +367,7 @@ static struct axi_dmac_desc *axi_dmac_alloc_desc(unsigned int num_sgs) struct axi_dmac_desc *desc; unsigned int i; - desc = kzalloc(sizeof(struct axi_dmac_desc) + - sizeof(struct axi_dmac_sg) * num_sgs, GFP_NOWAIT); + desc = kzalloc(struct_size(desc, sg, num_sgs), GFP_NOWAIT); if (!desc) return NULL; diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index a8b6225faa12..9ce0a386225b 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -838,9 +838,8 @@ static int jz4780_dma_probe(struct platform_device *pdev) if (!soc_data) return -EINVAL; - jzdma = devm_kzalloc(dev, sizeof(*jzdma) - + sizeof(*jzdma->chan) * soc_data->nb_channels, - GFP_KERNEL); + jzdma = devm_kzalloc(dev, struct_size(jzdma, chan, + soc_data->nb_channels), GFP_KERNEL); if (!jzdma) return -ENOMEM; diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 6511928b4cdf..b96814a7dceb 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -200,15 +200,20 @@ struct dmatest_done { wait_queue_head_t *wait; }; +struct dmatest_data { + u8 **raw; + u8 **aligned; + unsigned int cnt; + unsigned int off; +}; + struct dmatest_thread { struct list_head node; struct dmatest_info *info; struct task_struct *task; struct dma_chan *chan; - u8 **srcs; - u8 **usrcs; - u8 **dsts; - u8 **udsts; + struct dmatest_data src; + struct dmatest_data dst; enum dma_transaction_type type; wait_queue_head_t done_wait; struct dmatest_done test_done; @@ -481,6 +486,53 @@ static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len) return FIXPT_TO_INT(dmatest_persec(runtime, len >> 10)); } +static void __dmatest_free_test_data(struct dmatest_data *d, unsigned int cnt) +{ + unsigned int i; + + for (i = 0; i < cnt; i++) + kfree(d->raw[i]); + + kfree(d->aligned); + kfree(d->raw); +} + +static void dmatest_free_test_data(struct dmatest_data *d) +{ + __dmatest_free_test_data(d, d->cnt); +} + +static int dmatest_alloc_test_data(struct dmatest_data *d, + unsigned int buf_size, u8 align) +{ + unsigned int i = 0; + + d->raw = kcalloc(d->cnt + 1, sizeof(u8 *), GFP_KERNEL); + if (!d->raw) + return -ENOMEM; + + d->aligned = kcalloc(d->cnt + 1, sizeof(u8 *), GFP_KERNEL); + if (!d->aligned) + goto err; + + for (i = 0; i < d->cnt; i++) { + d->raw[i] = kmalloc(buf_size + align, GFP_KERNEL); + if (!d->raw[i]) + goto err; + + /* align to alignment restriction */ + if (align) + d->aligned[i] = PTR_ALIGN(d->raw[i], align); + else + d->aligned[i] = d->raw[i]; + } + + return 0; +err: + __dmatest_free_test_data(d, i); + return -ENOMEM; +} + /* * This function repeatedly tests DMA transfers of various lengths and * offsets for a given operation type until it is told to exit by @@ -511,8 +563,9 @@ static int dmatest_func(void *data) enum dma_ctrl_flags flags; u8 *pq_coefs = NULL; int ret; - int src_cnt; - int dst_cnt; + unsigned int buf_size; + struct dmatest_data *src; + struct dmatest_data *dst; int i; ktime_t ktime, start, diff; ktime_t filltime = 0; @@ -535,25 +588,27 @@ static int dmatest_func(void *data) params = &info->params; chan = thread->chan; dev = chan->device; + src = &thread->src; + dst = &thread->dst; if (thread->type == DMA_MEMCPY) { align = params->alignment < 0 ? dev->copy_align : params->alignment; - src_cnt = dst_cnt = 1; + src->cnt = dst->cnt = 1; } else if (thread->type == DMA_MEMSET) { align = params->alignment < 0 ? dev->fill_align : params->alignment; - src_cnt = dst_cnt = 1; + src->cnt = dst->cnt = 1; is_memset = true; } else if (thread->type == DMA_XOR) { /* force odd to ensure dst = src */ - src_cnt = min_odd(params->xor_sources | 1, dev->max_xor); - dst_cnt = 1; + src->cnt = min_odd(params->xor_sources | 1, dev->max_xor); + dst->cnt = 1; align = params->alignment < 0 ? dev->xor_align : params->alignment; } else if (thread->type == DMA_PQ) { /* force odd to ensure dst = src */ - src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0)); - dst_cnt = 2; + src->cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0)); + dst->cnt = 2; align = params->alignment < 0 ? dev->pq_align : params->alignment; @@ -561,75 +616,38 @@ static int dmatest_func(void *data) if (!pq_coefs) goto err_thread_type; - for (i = 0; i < src_cnt; i++) + for (i = 0; i < src->cnt; i++) pq_coefs[i] = 1; } else goto err_thread_type; /* Check if buffer count fits into map count variable (u8) */ - if ((src_cnt + dst_cnt) >= 255) { + if ((src->cnt + dst->cnt) >= 255) { pr_err("too many buffers (%d of 255 supported)\n", - src_cnt + dst_cnt); + src->cnt + dst->cnt); goto err_free_coefs; } - if (1 << align > params->buf_size) { + buf_size = params->buf_size; + if (1 << align > buf_size) { pr_err("%u-byte buffer too small for %d-byte alignment\n", - params->buf_size, 1 << align); + buf_size, 1 << align); goto err_free_coefs; } - thread->srcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL); - if (!thread->srcs) + if (dmatest_alloc_test_data(src, buf_size, align) < 0) goto err_free_coefs; - thread->usrcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL); - if (!thread->usrcs) - goto err_usrcs; - - for (i = 0; i < src_cnt; i++) { - thread->usrcs[i] = kmalloc(params->buf_size + align, - GFP_KERNEL); - if (!thread->usrcs[i]) - goto err_srcbuf; - - /* align srcs to alignment restriction */ - if (align) - thread->srcs[i] = PTR_ALIGN(thread->usrcs[i], align); - else - thread->srcs[i] = thread->usrcs[i]; - } - thread->srcs[i] = NULL; - - thread->dsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL); - if (!thread->dsts) - goto err_dsts; - - thread->udsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL); - if (!thread->udsts) - goto err_udsts; - - for (i = 0; i < dst_cnt; i++) { - thread->udsts[i] = kmalloc(params->buf_size + align, - GFP_KERNEL); - if (!thread->udsts[i]) - goto err_dstbuf; - - /* align dsts to alignment restriction */ - if (align) - thread->dsts[i] = PTR_ALIGN(thread->udsts[i], align); - else - thread->dsts[i] = thread->udsts[i]; - } - thread->dsts[i] = NULL; + if (dmatest_alloc_test_data(dst, buf_size, align) < 0) + goto err_src; set_user_nice(current, 10); - srcs = kcalloc(src_cnt, sizeof(dma_addr_t), GFP_KERNEL); + srcs = kcalloc(src->cnt, sizeof(dma_addr_t), GFP_KERNEL); if (!srcs) - goto err_dstbuf; + goto err_dst; - dma_pq = kcalloc(dst_cnt, sizeof(dma_addr_t), GFP_KERNEL); + dma_pq = kcalloc(dst->cnt, sizeof(dma_addr_t), GFP_KERNEL); if (!dma_pq) goto err_srcs_array; @@ -644,21 +662,21 @@ static int dmatest_func(void *data) struct dma_async_tx_descriptor *tx = NULL; struct dmaengine_unmap_data *um; dma_addr_t *dsts; - unsigned int src_off, dst_off, len; + unsigned int len; total_tests++; if (params->transfer_size) { - if (params->transfer_size >= params->buf_size) { + if (params->transfer_size >= buf_size) { pr_err("%u-byte transfer size must be lower than %u-buffer size\n", - params->transfer_size, params->buf_size); + params->transfer_size, buf_size); break; } len = params->transfer_size; } else if (params->norandom) { - len = params->buf_size; + len = buf_size; } else { - len = dmatest_random() % params->buf_size + 1; + len = dmatest_random() % buf_size + 1; } /* Do not alter transfer size explicitly defined by user */ @@ -670,57 +688,57 @@ static int dmatest_func(void *data) total_len += len; if (params->norandom) { - src_off = 0; - dst_off = 0; + src->off = 0; + dst->off = 0; } else { - src_off = dmatest_random() % (params->buf_size - len + 1); - dst_off = dmatest_random() % (params->buf_size - len + 1); + src->off = dmatest_random() % (buf_size - len + 1); + dst->off = dmatest_random() % (buf_size - len + 1); - src_off = (src_off >> align) << align; - dst_off = (dst_off >> align) << align; + src->off = (src->off >> align) << align; + dst->off = (dst->off >> align) << align; } if (!params->noverify) { start = ktime_get(); - dmatest_init_srcs(thread->srcs, src_off, len, - params->buf_size, is_memset); - dmatest_init_dsts(thread->dsts, dst_off, len, - params->buf_size, is_memset); + dmatest_init_srcs(src->aligned, src->off, len, + buf_size, is_memset); + dmatest_init_dsts(dst->aligned, dst->off, len, + buf_size, is_memset); diff = ktime_sub(ktime_get(), start); filltime = ktime_add(filltime, diff); } - um = dmaengine_get_unmap_data(dev->dev, src_cnt + dst_cnt, + um = dmaengine_get_unmap_data(dev->dev, src->cnt + dst->cnt, GFP_KERNEL); if (!um) { failed_tests++; result("unmap data NULL", total_tests, - src_off, dst_off, len, ret); + src->off, dst->off, len, ret); continue; } - um->len = params->buf_size; - for (i = 0; i < src_cnt; i++) { - void *buf = thread->srcs[i]; + um->len = buf_size; + for (i = 0; i < src->cnt; i++) { + void *buf = src->aligned[i]; struct page *pg = virt_to_page(buf); unsigned long pg_off = offset_in_page(buf); um->addr[i] = dma_map_page(dev->dev, pg, pg_off, um->len, DMA_TO_DEVICE); - srcs[i] = um->addr[i] + src_off; + srcs[i] = um->addr[i] + src->off; ret = dma_mapping_error(dev->dev, um->addr[i]); if (ret) { result("src mapping error", total_tests, - src_off, dst_off, len, ret); + src->off, dst->off, len, ret); goto error_unmap_continue; } um->to_cnt++; } /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */ - dsts = &um->addr[src_cnt]; - for (i = 0; i < dst_cnt; i++) { - void *buf = thread->dsts[i]; + dsts = &um->addr[src->cnt]; + for (i = 0; i < dst->cnt; i++) { + void *buf = dst->aligned[i]; struct page *pg = virt_to_page(buf); unsigned long pg_off = offset_in_page(buf); @@ -729,7 +747,7 @@ static int dmatest_func(void *data) ret = dma_mapping_error(dev->dev, dsts[i]); if (ret) { result("dst mapping error", total_tests, - src_off, dst_off, len, ret); + src->off, dst->off, len, ret); goto error_unmap_continue; } um->bidi_cnt++; @@ -737,29 +755,29 @@ static int dmatest_func(void *data) if (thread->type == DMA_MEMCPY) tx = dev->device_prep_dma_memcpy(chan, - dsts[0] + dst_off, + dsts[0] + dst->off, srcs[0], len, flags); else if (thread->type == DMA_MEMSET) tx = dev->device_prep_dma_memset(chan, - dsts[0] + dst_off, - *(thread->srcs[0] + src_off), + dsts[0] + dst->off, + *(src->aligned[0] + src->off), len, flags); else if (thread->type == DMA_XOR) tx = dev->device_prep_dma_xor(chan, - dsts[0] + dst_off, - srcs, src_cnt, + dsts[0] + dst->off, + srcs, src->cnt, len, flags); else if (thread->type == DMA_PQ) { - for (i = 0; i < dst_cnt; i++) - dma_pq[i] = dsts[i] + dst_off; + for (i = 0; i < dst->cnt; i++) + dma_pq[i] = dsts[i] + dst->off; tx = dev->device_prep_dma_pq(chan, dma_pq, srcs, - src_cnt, pq_coefs, + src->cnt, pq_coefs, len, flags); } if (!tx) { - result("prep error", total_tests, src_off, - dst_off, len, ret); + result("prep error", total_tests, src->off, + dst->off, len, ret); msleep(100); goto error_unmap_continue; } @@ -770,8 +788,8 @@ static int dmatest_func(void *data) cookie = tx->tx_submit(tx); if (dma_submit_error(cookie)) { - result("submit error", total_tests, src_off, - dst_off, len, ret); + result("submit error", total_tests, src->off, + dst->off, len, ret); msleep(100); goto error_unmap_continue; } @@ -783,58 +801,58 @@ static int dmatest_func(void *data) status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); if (!done->done) { - result("test timed out", total_tests, src_off, dst_off, + result("test timed out", total_tests, src->off, dst->off, len, 0); goto error_unmap_continue; } else if (status != DMA_COMPLETE) { result(status == DMA_ERROR ? "completion error status" : - "completion busy status", total_tests, src_off, - dst_off, len, ret); + "completion busy status", total_tests, src->off, + dst->off, len, ret); goto error_unmap_continue; } dmaengine_unmap_put(um); if (params->noverify) { - verbose_result("test passed", total_tests, src_off, - dst_off, len, 0); + verbose_result("test passed", total_tests, src->off, + dst->off, len, 0); continue; } start = ktime_get(); pr_debug("%s: verifying source buffer...\n", current->comm); - error_count = dmatest_verify(thread->srcs, 0, src_off, + error_count = dmatest_verify(src->aligned, 0, src->off, 0, PATTERN_SRC, true, is_memset); - error_count += dmatest_verify(thread->srcs, src_off, - src_off + len, src_off, + error_count += dmatest_verify(src->aligned, src->off, + src->off + len, src->off, PATTERN_SRC | PATTERN_COPY, true, is_memset); - error_count += dmatest_verify(thread->srcs, src_off + len, - params->buf_size, src_off + len, + error_count += dmatest_verify(src->aligned, src->off + len, + buf_size, src->off + len, PATTERN_SRC, true, is_memset); pr_debug("%s: verifying dest buffer...\n", current->comm); - error_count += dmatest_verify(thread->dsts, 0, dst_off, + error_count += dmatest_verify(dst->aligned, 0, dst->off, 0, PATTERN_DST, false, is_memset); - error_count += dmatest_verify(thread->dsts, dst_off, - dst_off + len, src_off, + error_count += dmatest_verify(dst->aligned, dst->off, + dst->off + len, src->off, PATTERN_SRC | PATTERN_COPY, false, is_memset); - error_count += dmatest_verify(thread->dsts, dst_off + len, - params->buf_size, dst_off + len, + error_count += dmatest_verify(dst->aligned, dst->off + len, + buf_size, dst->off + len, PATTERN_DST, false, is_memset); diff = ktime_sub(ktime_get(), start); comparetime = ktime_add(comparetime, diff); if (error_count) { - result("data error", total_tests, src_off, dst_off, + result("data error", total_tests, src->off, dst->off, len, error_count); failed_tests++; } else { - verbose_result("test passed", total_tests, src_off, - dst_off, len, 0); + verbose_result("test passed", total_tests, src->off, + dst->off, len, 0); } continue; @@ -852,19 +870,10 @@ error_unmap_continue: kfree(dma_pq); err_srcs_array: kfree(srcs); -err_dstbuf: - for (i = 0; thread->udsts[i]; i++) - kfree(thread->udsts[i]); - kfree(thread->udsts); -err_udsts: - kfree(thread->dsts); -err_dsts: -err_srcbuf: - for (i = 0; thread->usrcs[i]; i++) - kfree(thread->usrcs[i]); - kfree(thread->usrcs); -err_usrcs: - kfree(thread->srcs); +err_dst: + dmatest_free_test_data(dst); +err_src: + dmatest_free_test_data(src); err_free_coefs: kfree(pq_coefs); err_thread_type: diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index f8888dc0b8dc..18b6014cf9b4 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -75,7 +75,7 @@ struct __packed axi_dma_lli { __le32 sstat; __le32 dstat; __le32 status_lo; - __le32 ststus_hi; + __le32 status_hi; __le32 reserved_lo; __le32 reserved_hi; }; diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig index 04b9728c1d26..e5162690de8f 100644 --- a/drivers/dma/dw/Kconfig +++ b/drivers/dma/dw/Kconfig @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + # # DMA engine configuration for dw # diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile index 2b949c2e4504..63ed895c09aa 100644 --- a/drivers/dma/dw/Makefile +++ b/drivers/dma/dw/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o -dw_dmac_core-objs := core.o +dw_dmac_core-objs := core.o dw.o idma32.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o dw_dmac-objs := platform.o diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index dc053e62f894..21cb2a58dbd2 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Core driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2007-2008 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics * Copyright (C) 2013 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/bitops.h> @@ -37,27 +34,6 @@ * support descriptor writeback. */ -#define DWC_DEFAULT_CTLLO(_chan) ({ \ - struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ - struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ - bool _is_slave = is_slave_direction(_dwc->direction); \ - u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ - DW_DMA_MSIZE_16; \ - u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ - DW_DMA_MSIZE_16; \ - u8 _dms = (_dwc->direction == DMA_MEM_TO_DEV) ? \ - _dwc->dws.p_master : _dwc->dws.m_master; \ - u8 _sms = (_dwc->direction == DMA_DEV_TO_MEM) ? \ - _dwc->dws.p_master : _dwc->dws.m_master; \ - \ - (DWC_CTLL_DST_MSIZE(_dmsize) \ - | DWC_CTLL_SRC_MSIZE(_smsize) \ - | DWC_CTLL_LLP_D_EN \ - | DWC_CTLL_LLP_S_EN \ - | DWC_CTLL_DMS(_dms) \ - | DWC_CTLL_SMS(_sms)); \ - }) - /* The set of bus widths supported by the DMA controller */ #define DW_DMA_BUSWIDTHS \ BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ @@ -138,44 +114,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) dwc->descs_allocated--; } -static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc) -{ - u32 cfghi = 0; - u32 cfglo = 0; - - /* Set default burst alignment */ - cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; - - /* Low 4 bits of the request lines */ - cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); - cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); - - /* Request line extension (2 bits) */ - cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); - cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); -} - -static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - u32 cfghi = DWC_CFGH_FIFO_MODE; - u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); - bool hs_polarity = dwc->dws.hs_polarity; - - cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); - cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); - cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl); - - /* Set polarity of handshake interface */ - cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); -} - static void dwc_initialize(struct dw_dma_chan *dwc) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); @@ -183,10 +121,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc) if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags)) return; - if (dw->pdata->is_idma32) - dwc_initialize_chan_idma32(dwc); - else - dwc_initialize_chan_dw(dwc); + dw->initialize_chan(dwc); /* Enable interrupts */ channel_set_bit(dw, MASK.XFER, dwc->mask); @@ -215,37 +150,6 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) cpu_relax(); } -static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes, - unsigned int width, size_t *len) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - u32 block; - - /* Always in bytes for iDMA 32-bit */ - if (dw->pdata->is_idma32) - width = 0; - - if ((bytes >> width) > dwc->block_size) { - block = dwc->block_size; - *len = block << width; - } else { - block = bytes >> width; - *len = bytes; - } - - return block; -} - -static size_t block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - - if (dw->pdata->is_idma32) - return IDMA32C_CTLH_BLOCK_TS(block); - - return DWC_CTLH_BLOCK_TS(block) << width; -} - /*----------------------------------------------------------------------*/ /* Perform single block transfer */ @@ -391,10 +295,11 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) /* Returns how many bytes were already received from source */ static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) { + struct dw_dma *dw = to_dw_dma(dwc->chan.device); u32 ctlhi = channel_readl(dwc, CTL_HI); u32 ctllo = channel_readl(dwc, CTL_LO); - return block2bytes(dwc, ctlhi, ctllo >> 4 & 7); + return dw->block2bytes(dwc, ctlhi, ctllo >> 4 & 7); } static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) @@ -651,7 +556,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, unsigned int src_width; unsigned int dst_width; unsigned int data_width = dw->pdata->data_width[m_master]; - u32 ctllo; + u32 ctllo, ctlhi; u8 lms = DWC_LLP_LMS(m_master); dev_vdbg(chan2dev(chan), @@ -667,7 +572,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, src_width = dst_width = __ffs(data_width | src | dest | len); - ctllo = DWC_DEFAULT_CTLLO(chan) + ctllo = dw->prepare_ctllo(dwc) | DWC_CTLL_DST_WIDTH(dst_width) | DWC_CTLL_SRC_WIDTH(src_width) | DWC_CTLL_DST_INC @@ -680,10 +585,12 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len - offset, src_width, &xfer_count); + lli_write(desc, sar, src + offset); lli_write(desc, dar, dest + offset); lli_write(desc, ctllo, ctllo); - lli_write(desc, ctlhi, bytes2block(dwc, len - offset, src_width, &xfer_count)); + lli_write(desc, ctlhi, ctlhi); desc->len = xfer_count; if (!first) { @@ -721,7 +628,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; - u32 ctllo; + u32 ctllo, ctlhi; u8 m_master = dwc->dws.m_master; u8 lms = DWC_LLP_LMS(m_master); dma_addr_t reg; @@ -745,10 +652,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, case DMA_MEM_TO_DEV: reg_width = __ffs(sconfig->dst_addr_width); reg = sconfig->dst_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) + ctllo = dw->prepare_ctllo(dwc) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC); + | DWC_CTLL_SRC_INC; ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); @@ -768,9 +675,11 @@ slave_sg_todev_fill_desc: if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len, mem_width, &dlen); + lli_write(desc, sar, mem); lli_write(desc, dar, reg); - lli_write(desc, ctlhi, bytes2block(dwc, len, mem_width, &dlen)); + lli_write(desc, ctlhi, ctlhi); lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width)); desc->len = dlen; @@ -793,10 +702,10 @@ slave_sg_todev_fill_desc: case DMA_DEV_TO_MEM: reg_width = __ffs(sconfig->src_addr_width); reg = sconfig->src_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) + ctllo = dw->prepare_ctllo(dwc) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX); + | DWC_CTLL_SRC_FIX; ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); @@ -814,9 +723,11 @@ slave_sg_fromdev_fill_desc: if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len, reg_width, &dlen); + lli_write(desc, sar, reg); lli_write(desc, dar, mem); - lli_write(desc, ctlhi, bytes2block(dwc, len, reg_width, &dlen)); + lli_write(desc, ctlhi, ctlhi); mem_width = __ffs(data_width | mem | dlen); lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width)); desc->len = dlen; @@ -876,22 +787,12 @@ EXPORT_SYMBOL_GPL(dw_dma_filter); static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dma_slave_config *sc = &dwc->dma_sconfig; struct dw_dma *dw = to_dw_dma(chan->device); - /* - * Fix sconfig's burst size according to dw_dmac. We need to convert - * them as: - * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. - * - * NOTE: burst size 2 is not supported by DesignWare controller. - * iDMA 32-bit supports it. - */ - u32 s = dw->pdata->is_idma32 ? 1 : 2; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); - sc->src_maxburst = sc->src_maxburst > 1 ? fls(sc->src_maxburst) - s : 0; - sc->dst_maxburst = sc->dst_maxburst > 1 ? fls(sc->dst_maxburst) - s : 0; + dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst); + dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst); return 0; } @@ -900,16 +801,9 @@ static void dwc_chan_pause(struct dw_dma_chan *dwc, bool drain) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); unsigned int count = 20; /* timeout iterations */ - u32 cfglo; - cfglo = channel_readl(dwc, CFG_LO); - if (dw->pdata->is_idma32) { - if (drain) - cfglo |= IDMA32C_CFGL_CH_DRAIN; - else - cfglo &= ~IDMA32C_CFGL_CH_DRAIN; - } - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + dw->suspend_chan(dwc, drain); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) udelay(2); @@ -928,11 +822,11 @@ static int dwc_pause(struct dma_chan *chan) return 0; } -static inline void dwc_chan_resume(struct dw_dma_chan *dwc) +static inline void dwc_chan_resume(struct dw_dma_chan *dwc, bool drain) { - u32 cfglo = channel_readl(dwc, CFG_LO); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); + dw->resume_chan(dwc, drain); clear_bit(DW_DMA_IS_PAUSED, &dwc->flags); } @@ -945,7 +839,7 @@ static int dwc_resume(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags)) - dwc_chan_resume(dwc); + dwc_chan_resume(dwc, false); spin_unlock_irqrestore(&dwc->lock, flags); @@ -968,7 +862,7 @@ static int dwc_terminate_all(struct dma_chan *chan) dwc_chan_disable(dw, dwc); - dwc_chan_resume(dwc); + dwc_chan_resume(dwc, true); /* active_list entries will end up before queued entries */ list_splice_init(&dwc->queue, &list); @@ -1058,33 +952,7 @@ static void dwc_issue_pending(struct dma_chan *chan) /*----------------------------------------------------------------------*/ -/* - * Program FIFO size of channels. - * - * By default full FIFO (512 bytes) is assigned to channel 0. Here we - * slice FIFO on equal parts between channels. - */ -static void idma32_fifo_partition(struct dw_dma *dw) -{ - u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | - IDMA32C_FP_UPDATE; - u64 fifo_partition = 0; - - if (!dw->pdata->is_idma32) - return; - - /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ - fifo_partition |= value << 0; - - /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ - fifo_partition |= value << 32; - - /* Program FIFO Partition registers - 64 bytes per channel */ - idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); - idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); -} - -static void dw_dma_off(struct dw_dma *dw) +void do_dw_dma_off(struct dw_dma *dw) { unsigned int i; @@ -1103,7 +971,7 @@ static void dw_dma_off(struct dw_dma *dw) clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags); } -static void dw_dma_on(struct dw_dma *dw) +void do_dw_dma_on(struct dw_dma *dw) { dma_writel(dw, CFG, DW_CFG_DMA_EN); } @@ -1139,7 +1007,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) /* Enable controller here if needed */ if (!dw->in_use) - dw_dma_on(dw); + do_dw_dma_on(dw); dw->in_use |= dwc->mask; return 0; @@ -1150,7 +1018,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma *dw = to_dw_dma(chan->device); unsigned long flags; - LIST_HEAD(list); dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, dwc->descs_allocated); @@ -1177,30 +1044,25 @@ static void dwc_free_chan_resources(struct dma_chan *chan) /* Disable controller in case it was a last user */ dw->in_use &= ~dwc->mask; if (!dw->in_use) - dw_dma_off(dw); + do_dw_dma_off(dw); dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } -int dw_dma_probe(struct dw_dma_chip *chip) +int do_dma_probe(struct dw_dma_chip *chip) { + struct dw_dma *dw = chip->dw; struct dw_dma_platform_data *pdata; - struct dw_dma *dw; bool autocfg = false; unsigned int dw_params; unsigned int i; int err; - dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); - if (!dw) - return -ENOMEM; - dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL); if (!dw->pdata) return -ENOMEM; dw->regs = chip->regs; - chip->dw = dw; pm_runtime_get_sync(chip->dev); @@ -1227,8 +1089,6 @@ int dw_dma_probe(struct dw_dma_chip *chip) pdata->block_size = dma_readl(dw, MAX_BLK_SIZE); /* Fill platform data with the default values */ - pdata->is_private = true; - pdata->is_memcpy = true; pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; pdata->chan_priority = CHAN_PRIORITY_ASCENDING; } else if (chip->pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) { @@ -1252,15 +1112,10 @@ int dw_dma_probe(struct dw_dma_chip *chip) dw->all_chan_mask = (1 << pdata->nr_channels) - 1; /* Force dma off, just in case */ - dw_dma_off(dw); - - idma32_fifo_partition(dw); + dw->disable(dw); /* Device and instance ID for IRQ and DMA pool */ - if (pdata->is_idma32) - snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", chip->id); - else - snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", chip->id); + dw->set_device_name(dw, chip->id); /* Create a pool of consistent memory blocks for hardware descriptors */ dw->desc_pool = dmam_pool_create(dw->name, chip->dev, @@ -1340,10 +1195,8 @@ int dw_dma_probe(struct dw_dma_chip *chip) /* Set capabilities */ dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); - if (pdata->is_private) - dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); - if (pdata->is_memcpy) - dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); + dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); + dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); dw->dma.dev = chip->dev; dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; @@ -1384,16 +1237,15 @@ err_pdata: pm_runtime_put_sync_suspend(chip->dev); return err; } -EXPORT_SYMBOL_GPL(dw_dma_probe); -int dw_dma_remove(struct dw_dma_chip *chip) +int do_dma_remove(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; struct dw_dma_chan *dwc, *_dwc; pm_runtime_get_sync(chip->dev); - dw_dma_off(dw); + do_dw_dma_off(dw); dma_async_device_unregister(&dw->dma); free_irq(chip->irq, dw); @@ -1408,27 +1260,24 @@ int dw_dma_remove(struct dw_dma_chip *chip) pm_runtime_put_sync_suspend(chip->dev); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_remove); -int dw_dma_disable(struct dw_dma_chip *chip) +int do_dw_dma_disable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; - dw_dma_off(dw); + dw->disable(dw); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_disable); +EXPORT_SYMBOL_GPL(do_dw_dma_disable); -int dw_dma_enable(struct dw_dma_chip *chip) +int do_dw_dma_enable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; - idma32_fifo_partition(dw); - - dw_dma_on(dw); + dw->enable(dw); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_enable); +EXPORT_SYMBOL_GPL(do_dw_dma_enable); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver"); diff --git a/drivers/dma/dw/dw.c b/drivers/dma/dw/dw.c new file mode 100644 index 000000000000..7a085b3c1854 --- /dev/null +++ b/drivers/dma/dw/dw.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2007-2008 Atmel Corporation +// Copyright (C) 2010-2011 ST Microelectronics +// Copyright (C) 2013,2018 Intel Corporation + +#include <linux/bitops.h> +#include <linux/dmaengine.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "internal.h" + +static void dw_dma_initialize_chan(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + u32 cfghi = DWC_CFGH_FIFO_MODE; + u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); + bool hs_polarity = dwc->dws.hs_polarity; + + cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); + cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); + cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl); + + /* Set polarity of handshake interface */ + cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); +} + +static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); +} + +static void dw_dma_resume_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); +} + +static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc, + size_t bytes, unsigned int width, size_t *len) +{ + u32 block; + + if ((bytes >> width) > dwc->block_size) { + block = dwc->block_size; + *len = dwc->block_size << width; + } else { + block = bytes >> width; + *len = bytes; + } + + return block; +} + +static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) +{ + return DWC_CTLH_BLOCK_TS(block) << width; +} + +static u32 dw_dma_prepare_ctllo(struct dw_dma_chan *dwc) +{ + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + bool is_slave = is_slave_direction(dwc->direction); + u8 smsize = is_slave ? sconfig->src_maxburst : DW_DMA_MSIZE_16; + u8 dmsize = is_slave ? sconfig->dst_maxburst : DW_DMA_MSIZE_16; + u8 p_master = dwc->dws.p_master; + u8 m_master = dwc->dws.m_master; + u8 dms = (dwc->direction == DMA_MEM_TO_DEV) ? p_master : m_master; + u8 sms = (dwc->direction == DMA_DEV_TO_MEM) ? p_master : m_master; + + return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN | + DWC_CTLL_DST_MSIZE(dmsize) | DWC_CTLL_SRC_MSIZE(smsize) | + DWC_CTLL_DMS(dms) | DWC_CTLL_SMS(sms); +} + +static void dw_dma_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) +{ + /* + * Fix burst size according to dw_dmac. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. + */ + *maxburst = *maxburst > 1 ? fls(*maxburst) - 2 : 0; +} + +static void dw_dma_set_device_name(struct dw_dma *dw, int id) +{ + snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", id); +} + +static void dw_dma_disable(struct dw_dma *dw) +{ + do_dw_dma_off(dw); +} + +static void dw_dma_enable(struct dw_dma *dw) +{ + do_dw_dma_on(dw); +} + +int dw_dma_probe(struct dw_dma_chip *chip) +{ + struct dw_dma *dw; + + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + /* Channel operations */ + dw->initialize_chan = dw_dma_initialize_chan; + dw->suspend_chan = dw_dma_suspend_chan; + dw->resume_chan = dw_dma_resume_chan; + dw->prepare_ctllo = dw_dma_prepare_ctllo; + dw->encode_maxburst = dw_dma_encode_maxburst; + dw->bytes2block = dw_dma_bytes2block; + dw->block2bytes = dw_dma_block2bytes; + + /* Device operations */ + dw->set_device_name = dw_dma_set_device_name; + dw->disable = dw_dma_disable; + dw->enable = dw_dma_enable; + + chip->dw = dw; + return do_dma_probe(chip); +} +EXPORT_SYMBOL_GPL(dw_dma_probe); + +int dw_dma_remove(struct dw_dma_chip *chip) +{ + return do_dma_remove(chip); +} +EXPORT_SYMBOL_GPL(dw_dma_remove); diff --git a/drivers/dma/dw/idma32.c b/drivers/dma/dw/idma32.c new file mode 100644 index 000000000000..f00657308811 --- /dev/null +++ b/drivers/dma/dw/idma32.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013,2018 Intel Corporation + +#include <linux/bitops.h> +#include <linux/dmaengine.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "internal.h" + +static void idma32_initialize_chan(struct dw_dma_chan *dwc) +{ + u32 cfghi = 0; + u32 cfglo = 0; + + /* Set default burst alignment */ + cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; + + /* Low 4 bits of the request lines */ + cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); + cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); + + /* Request line extension (2 bits) */ + cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); + cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); +} + +static void idma32_suspend_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + if (drain) + cfglo |= IDMA32C_CFGL_CH_DRAIN; + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); +} + +static void idma32_resume_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + if (drain) + cfglo &= ~IDMA32C_CFGL_CH_DRAIN; + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); +} + +static u32 idma32_bytes2block(struct dw_dma_chan *dwc, + size_t bytes, unsigned int width, size_t *len) +{ + u32 block; + + if (bytes > dwc->block_size) { + block = dwc->block_size; + *len = dwc->block_size; + } else { + block = bytes; + *len = bytes; + } + + return block; +} + +static size_t idma32_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) +{ + return IDMA32C_CTLH_BLOCK_TS(block); +} + +static u32 idma32_prepare_ctllo(struct dw_dma_chan *dwc) +{ + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + bool is_slave = is_slave_direction(dwc->direction); + u8 smsize = is_slave ? sconfig->src_maxburst : IDMA32_MSIZE_8; + u8 dmsize = is_slave ? sconfig->dst_maxburst : IDMA32_MSIZE_8; + + return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN | + DWC_CTLL_DST_MSIZE(dmsize) | DWC_CTLL_SRC_MSIZE(smsize); +} + +static void idma32_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) +{ + *maxburst = *maxburst > 1 ? fls(*maxburst) - 1 : 0; +} + +static void idma32_set_device_name(struct dw_dma *dw, int id) +{ + snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", id); +} + +/* + * Program FIFO size of channels. + * + * By default full FIFO (512 bytes) is assigned to channel 0. Here we + * slice FIFO on equal parts between channels. + */ +static void idma32_fifo_partition(struct dw_dma *dw) +{ + u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | + IDMA32C_FP_UPDATE; + u64 fifo_partition = 0; + + /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ + fifo_partition |= value << 0; + + /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ + fifo_partition |= value << 32; + + /* Program FIFO Partition registers - 64 bytes per channel */ + idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); + idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); +} + +static void idma32_disable(struct dw_dma *dw) +{ + do_dw_dma_off(dw); + idma32_fifo_partition(dw); +} + +static void idma32_enable(struct dw_dma *dw) +{ + idma32_fifo_partition(dw); + do_dw_dma_on(dw); +} + +int idma32_dma_probe(struct dw_dma_chip *chip) +{ + struct dw_dma *dw; + + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + /* Channel operations */ + dw->initialize_chan = idma32_initialize_chan; + dw->suspend_chan = idma32_suspend_chan; + dw->resume_chan = idma32_resume_chan; + dw->prepare_ctllo = idma32_prepare_ctllo; + dw->encode_maxburst = idma32_encode_maxburst; + dw->bytes2block = idma32_bytes2block; + dw->block2bytes = idma32_block2bytes; + + /* Device operations */ + dw->set_device_name = idma32_set_device_name; + dw->disable = idma32_disable; + dw->enable = idma32_enable; + + chip->dw = dw; + return do_dma_probe(chip); +} +EXPORT_SYMBOL_GPL(idma32_dma_probe); + +int idma32_dma_remove(struct dw_dma_chip *chip) +{ + return do_dma_remove(chip); +} +EXPORT_SYMBOL_GPL(idma32_dma_remove); diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h index 41439732ff6b..1dd7a4e6dd23 100644 --- a/drivers/dma/dw/internal.h +++ b/drivers/dma/dw/internal.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2013 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _DMA_DW_INTERNAL_H @@ -15,8 +12,14 @@ #include "regs.h" -int dw_dma_disable(struct dw_dma_chip *chip); -int dw_dma_enable(struct dw_dma_chip *chip); +int do_dma_probe(struct dw_dma_chip *chip); +int do_dma_remove(struct dw_dma_chip *chip); + +void do_dw_dma_on(struct dw_dma *dw); +void do_dw_dma_off(struct dw_dma *dw); + +int do_dw_dma_disable(struct dw_dma_chip *chip); +int do_dw_dma_enable(struct dw_dma_chip *chip); extern bool dw_dma_filter(struct dma_chan *chan, void *param); diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index 7778ed705a1a..e79a75db0852 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PCI driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2013 Intel Corporation * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/module.h> @@ -15,21 +12,33 @@ #include "internal.h" -static struct dw_dma_platform_data mrfld_pdata = { +struct dw_dma_pci_data { + const struct dw_dma_platform_data *pdata; + int (*probe)(struct dw_dma_chip *chip); +}; + +static const struct dw_dma_pci_data dw_pci_data = { + .probe = dw_dma_probe, +}; + +static const struct dw_dma_platform_data idma32_pdata = { .nr_channels = 8, - .is_private = true, - .is_memcpy = true, - .is_idma32 = true, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, .chan_priority = CHAN_PRIORITY_ASCENDING, .block_size = 131071, .nr_masters = 1, .data_width = {4}, + .multi_block = {1, 1, 1, 1, 1, 1, 1, 1}, +}; + +static const struct dw_dma_pci_data idma32_pci_data = { + .pdata = &idma32_pdata, + .probe = idma32_dma_probe, }; static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) { - const struct dw_dma_platform_data *pdata = (void *)pid->driver_data; + const struct dw_dma_pci_data *data = (void *)pid->driver_data; struct dw_dma_chip *chip; int ret; @@ -62,9 +71,9 @@ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) chip->id = pdev->devfn; chip->regs = pcim_iomap_table(pdev)[0]; chip->irq = pdev->irq; - chip->pdata = pdata; + chip->pdata = data->pdata; - ret = dw_dma_probe(chip); + ret = data->probe(chip); if (ret) return ret; @@ -90,7 +99,7 @@ static int dw_pci_suspend_late(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_disable(chip); + return do_dw_dma_disable(chip); }; static int dw_pci_resume_early(struct device *dev) @@ -98,7 +107,7 @@ static int dw_pci_resume_early(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_enable(chip); + return do_dw_dma_enable(chip); }; #endif /* CONFIG_PM_SLEEP */ @@ -109,24 +118,24 @@ static const struct dev_pm_ops dw_pci_dev_pm_ops = { static const struct pci_device_id dw_pci_id_table[] = { /* Medfield (GPDMA) */ - { PCI_VDEVICE(INTEL, 0x0827) }, + { PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_data }, /* BayTrail */ - { PCI_VDEVICE(INTEL, 0x0f06) }, - { PCI_VDEVICE(INTEL, 0x0f40) }, + { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_data }, + { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_data }, - /* Merrifield iDMA 32-bit (GPDMA) */ - { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&mrfld_pdata }, + /* Merrifield */ + { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&idma32_pci_data }, /* Braswell */ - { PCI_VDEVICE(INTEL, 0x2286) }, - { PCI_VDEVICE(INTEL, 0x22c0) }, + { PCI_VDEVICE(INTEL, 0x2286), (kernel_ulong_t)&dw_pci_data }, + { PCI_VDEVICE(INTEL, 0x22c0), (kernel_ulong_t)&dw_pci_data }, /* Haswell */ - { PCI_VDEVICE(INTEL, 0x9c60) }, + { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_data }, /* Broadwell */ - { PCI_VDEVICE(INTEL, 0x9ce0) }, + { PCI_VDEVICE(INTEL, 0x9ce0), (kernel_ulong_t)&dw_pci_data }, { } }; diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 31ff8113c3de..382dfd9e9600 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Platform driver for the Synopsys DesignWare DMA Controller * @@ -6,10 +7,6 @@ * Copyright (C) 2013 Intel Corporation * * Some parts of this driver are derived from the original dw_dmac. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/module.h> @@ -128,15 +125,6 @@ dw_dma_parse_dt(struct platform_device *pdev) pdata->nr_masters = nr_masters; pdata->nr_channels = nr_channels; - if (of_property_read_bool(np, "is_private")) - pdata->is_private = true; - - /* - * All known devices, which use DT for configuration, support - * memory-to-memory transfers. So enable it by default. - */ - pdata->is_memcpy = true; - if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) pdata->chan_allocation_order = (unsigned char)tmp; @@ -264,7 +252,7 @@ static void dw_shutdown(struct platform_device *pdev) struct dw_dma_chip *chip = platform_get_drvdata(pdev); /* - * We have to call dw_dma_disable() to stop any ongoing transfer. On + * We have to call do_dw_dma_disable() to stop any ongoing transfer. On * some platforms we can't do that since DMA device is powered off. * Moreover we have no possibility to check if the platform is affected * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put() @@ -273,7 +261,7 @@ static void dw_shutdown(struct platform_device *pdev) * used by the driver. */ pm_runtime_get_sync(chip->dev); - dw_dma_disable(chip); + do_dw_dma_disable(chip); pm_runtime_put_sync_suspend(chip->dev); clk_disable_unprepare(chip->clk); @@ -303,7 +291,7 @@ static int dw_suspend_late(struct device *dev) { struct dw_dma_chip *chip = dev_get_drvdata(dev); - dw_dma_disable(chip); + do_dw_dma_disable(chip); clk_disable_unprepare(chip->clk); return 0; @@ -318,7 +306,7 @@ static int dw_resume_early(struct device *dev) if (ret) return ret; - return dw_dma_enable(chip); + return do_dw_dma_enable(chip); } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 646c9c960c07..3fce66ecee7a 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver for the Synopsys DesignWare AHB DMA Controller * * Copyright (C) 2005-2007 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics * Copyright (C) 2016 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/bitops.h> @@ -222,6 +219,16 @@ enum dw_dma_msize { /* iDMA 32-bit support */ +/* bursts size */ +enum idma32_msize { + IDMA32_MSIZE_1, + IDMA32_MSIZE_2, + IDMA32_MSIZE_4, + IDMA32_MSIZE_8, + IDMA32_MSIZE_16, + IDMA32_MSIZE_32, +}; + /* Bitfields in CTL_HI */ #define IDMA32C_CTLH_BLOCK_TS_MASK GENMASK(16, 0) #define IDMA32C_CTLH_BLOCK_TS(x) ((x) & IDMA32C_CTLH_BLOCK_TS_MASK) @@ -312,6 +319,21 @@ struct dw_dma { u8 all_chan_mask; u8 in_use; + /* Channel operations */ + void (*initialize_chan)(struct dw_dma_chan *dwc); + void (*suspend_chan)(struct dw_dma_chan *dwc, bool drain); + void (*resume_chan)(struct dw_dma_chan *dwc, bool drain); + u32 (*prepare_ctllo)(struct dw_dma_chan *dwc); + void (*encode_maxburst)(struct dw_dma_chan *dwc, u32 *maxburst); + u32 (*bytes2block)(struct dw_dma_chan *dwc, size_t bytes, + unsigned int width, size_t *len); + size_t (*block2bytes)(struct dw_dma_chan *dwc, u32 block, u32 width); + + /* Device operations */ + void (*set_device_name)(struct dw_dma *dw, int id); + void (*disable)(struct dw_dma *dw); + void (*enable)(struct dw_dma *dw); + /* platform data */ struct dw_dma_platform_data *pdata; }; diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index 8876c4c1bb2c..680b2a00a953 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -6,6 +6,7 @@ #include <linux/dmapool.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/dma-mapping.h> #include "fsl-edma-common.h" @@ -173,12 +174,62 @@ int fsl_edma_resume(struct dma_chan *chan) } EXPORT_SYMBOL_GPL(fsl_edma_resume); +static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan) +{ + if (fsl_chan->dma_dir != DMA_NONE) + dma_unmap_resource(fsl_chan->vchan.chan.device->dev, + fsl_chan->dma_dev_addr, + fsl_chan->dma_dev_size, + fsl_chan->dma_dir, 0); + fsl_chan->dma_dir = DMA_NONE; +} + +static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan, + enum dma_transfer_direction dir) +{ + struct device *dev = fsl_chan->vchan.chan.device->dev; + enum dma_data_direction dma_dir; + phys_addr_t addr = 0; + u32 size = 0; + + switch (dir) { + case DMA_MEM_TO_DEV: + dma_dir = DMA_FROM_DEVICE; + addr = fsl_chan->cfg.dst_addr; + size = fsl_chan->cfg.dst_maxburst; + break; + case DMA_DEV_TO_MEM: + dma_dir = DMA_TO_DEVICE; + addr = fsl_chan->cfg.src_addr; + size = fsl_chan->cfg.src_maxburst; + break; + default: + dma_dir = DMA_NONE; + break; + } + + /* Already mapped for this config? */ + if (fsl_chan->dma_dir == dma_dir) + return true; + + fsl_edma_unprep_slave_dma(fsl_chan); + + fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0); + if (dma_mapping_error(dev, fsl_chan->dma_dev_addr)) + return false; + fsl_chan->dma_dev_size = size; + fsl_chan->dma_dir = dma_dir; + + return true; +} + int fsl_edma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg)); + fsl_edma_unprep_slave_dma(fsl_chan); return 0; } @@ -339,9 +390,7 @@ static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, struct fsl_edma_desc *fsl_desc; int i; - fsl_desc = kzalloc(sizeof(*fsl_desc) + - sizeof(struct fsl_edma_sw_tcd) * - sg_len, GFP_NOWAIT); + fsl_desc = kzalloc(struct_size(fsl_desc, tcd, sg_len), GFP_NOWAIT); if (!fsl_desc) return NULL; @@ -378,6 +427,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( if (!is_slave_direction(direction)) return NULL; + if (!fsl_edma_prep_slave_dma(fsl_chan, direction)) + return NULL; + sg_len = buf_len / period_len; fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); if (!fsl_desc) @@ -409,11 +461,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( if (direction == DMA_MEM_TO_DEV) { src_addr = dma_buf_next; - dst_addr = fsl_chan->cfg.dst_addr; + dst_addr = fsl_chan->dma_dev_addr; soff = fsl_chan->cfg.dst_addr_width; doff = 0; } else { - src_addr = fsl_chan->cfg.src_addr; + src_addr = fsl_chan->dma_dev_addr; dst_addr = dma_buf_next; soff = 0; doff = fsl_chan->cfg.src_addr_width; @@ -444,6 +496,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( if (!is_slave_direction(direction)) return NULL; + if (!fsl_edma_prep_slave_dma(fsl_chan, direction)) + return NULL; + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); if (!fsl_desc) return NULL; @@ -468,11 +523,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( if (direction == DMA_MEM_TO_DEV) { src_addr = sg_dma_address(sg); - dst_addr = fsl_chan->cfg.dst_addr; + dst_addr = fsl_chan->dma_dev_addr; soff = fsl_chan->cfg.dst_addr_width; doff = 0; } else { - src_addr = fsl_chan->cfg.src_addr; + src_addr = fsl_chan->dma_dev_addr; dst_addr = sg_dma_address(sg); soff = 0; doff = fsl_chan->cfg.src_addr_width; @@ -555,6 +610,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan) fsl_edma_chan_mux(fsl_chan, 0, false); fsl_chan->edesc = NULL; vchan_get_all_descriptors(&fsl_chan->vchan, &head); + fsl_edma_unprep_slave_dma(fsl_chan); spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); vchan_dma_desc_free_list(&fsl_chan->vchan, &head); diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index 8917e8865959..b435d8e1e3a1 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -6,6 +6,7 @@ #ifndef _FSL_EDMA_COMMON_H_ #define _FSL_EDMA_COMMON_H_ +#include <linux/dma-direction.h> #include "virt-dma.h" #define EDMA_CR_EDBG BIT(1) @@ -120,6 +121,9 @@ struct fsl_edma_chan { struct dma_slave_config cfg; u32 attr; struct dma_pool *tcd_pool; + dma_addr_t dma_dev_addr; + u32 dma_dev_size; + enum dma_data_direction dma_dir; }; struct fsl_edma_desc { diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 34d70112fcc9..75e8a7ba3a22 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -254,6 +254,7 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_chan->pm_state = RUNNING; fsl_chan->slave_id = 0; fsl_chan->idle = true; + fsl_chan->dma_dir = DMA_NONE; fsl_chan->vchan.desc_free = fsl_edma_free_desc; vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c new file mode 100644 index 000000000000..aa1d0ae3d207 --- /dev/null +++ b/drivers/dma/fsl-qdma.c @@ -0,0 +1,1259 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2014-2015 Freescale +// Copyright 2018 NXP + +/* + * Driver for NXP Layerscape Queue Direct Memory Access Controller + * + * Author: + * Wen He <wen.he_1@nxp.com> + * Jiaheng Fan <jiaheng.fan@nxp.com> + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of_dma.h> +#include <linux/dma-mapping.h> + +#include "virt-dma.h" +#include "fsldma.h" + +/* Register related definition */ +#define FSL_QDMA_DMR 0x0 +#define FSL_QDMA_DSR 0x4 +#define FSL_QDMA_DEIER 0xe00 +#define FSL_QDMA_DEDR 0xe04 +#define FSL_QDMA_DECFDW0R 0xe10 +#define FSL_QDMA_DECFDW1R 0xe14 +#define FSL_QDMA_DECFDW2R 0xe18 +#define FSL_QDMA_DECFDW3R 0xe1c +#define FSL_QDMA_DECFQIDR 0xe30 +#define FSL_QDMA_DECBR 0xe34 + +#define FSL_QDMA_BCQMR(x) (0xc0 + 0x100 * (x)) +#define FSL_QDMA_BCQSR(x) (0xc4 + 0x100 * (x)) +#define FSL_QDMA_BCQEDPA_SADDR(x) (0xc8 + 0x100 * (x)) +#define FSL_QDMA_BCQDPA_SADDR(x) (0xcc + 0x100 * (x)) +#define FSL_QDMA_BCQEEPA_SADDR(x) (0xd0 + 0x100 * (x)) +#define FSL_QDMA_BCQEPA_SADDR(x) (0xd4 + 0x100 * (x)) +#define FSL_QDMA_BCQIER(x) (0xe0 + 0x100 * (x)) +#define FSL_QDMA_BCQIDR(x) (0xe4 + 0x100 * (x)) + +#define FSL_QDMA_SQDPAR 0x80c +#define FSL_QDMA_SQEPAR 0x814 +#define FSL_QDMA_BSQMR 0x800 +#define FSL_QDMA_BSQSR 0x804 +#define FSL_QDMA_BSQICR 0x828 +#define FSL_QDMA_CQMR 0xa00 +#define FSL_QDMA_CQDSCR1 0xa08 +#define FSL_QDMA_CQDSCR2 0xa0c +#define FSL_QDMA_CQIER 0xa10 +#define FSL_QDMA_CQEDR 0xa14 +#define FSL_QDMA_SQCCMR 0xa20 + +/* Registers for bit and genmask */ +#define FSL_QDMA_CQIDR_SQT BIT(15) +#define QDMA_CCDF_FOTMAT BIT(29) +#define QDMA_CCDF_SER BIT(30) +#define QDMA_SG_FIN BIT(30) +#define QDMA_SG_LEN_MASK GENMASK(29, 0) +#define QDMA_CCDF_MASK GENMASK(28, 20) + +#define FSL_QDMA_DEDR_CLEAR GENMASK(31, 0) +#define FSL_QDMA_BCQIDR_CLEAR GENMASK(31, 0) +#define FSL_QDMA_DEIER_CLEAR GENMASK(31, 0) + +#define FSL_QDMA_BCQIER_CQTIE BIT(15) +#define FSL_QDMA_BCQIER_CQPEIE BIT(23) +#define FSL_QDMA_BSQICR_ICEN BIT(31) + +#define FSL_QDMA_BSQICR_ICST(x) ((x) << 16) +#define FSL_QDMA_CQIER_MEIE BIT(31) +#define FSL_QDMA_CQIER_TEIE BIT(0) +#define FSL_QDMA_SQCCMR_ENTER_WM BIT(21) + +#define FSL_QDMA_BCQMR_EN BIT(31) +#define FSL_QDMA_BCQMR_EI BIT(30) +#define FSL_QDMA_BCQMR_CD_THLD(x) ((x) << 20) +#define FSL_QDMA_BCQMR_CQ_SIZE(x) ((x) << 16) + +#define FSL_QDMA_BCQSR_QF BIT(16) +#define FSL_QDMA_BCQSR_XOFF BIT(0) + +#define FSL_QDMA_BSQMR_EN BIT(31) +#define FSL_QDMA_BSQMR_DI BIT(30) +#define FSL_QDMA_BSQMR_CQ_SIZE(x) ((x) << 16) + +#define FSL_QDMA_BSQSR_QE BIT(17) + +#define FSL_QDMA_DMR_DQD BIT(30) +#define FSL_QDMA_DSR_DB BIT(31) + +/* Size related definition */ +#define FSL_QDMA_QUEUE_MAX 8 +#define FSL_QDMA_COMMAND_BUFFER_SIZE 64 +#define FSL_QDMA_DESCRIPTOR_BUFFER_SIZE 32 +#define FSL_QDMA_CIRCULAR_DESC_SIZE_MIN 64 +#define FSL_QDMA_CIRCULAR_DESC_SIZE_MAX 16384 +#define FSL_QDMA_QUEUE_NUM_MAX 8 + +/* Field definition for CMD */ +#define FSL_QDMA_CMD_RWTTYPE 0x4 +#define FSL_QDMA_CMD_LWC 0x2 +#define FSL_QDMA_CMD_RWTTYPE_OFFSET 28 +#define FSL_QDMA_CMD_NS_OFFSET 27 +#define FSL_QDMA_CMD_DQOS_OFFSET 24 +#define FSL_QDMA_CMD_WTHROTL_OFFSET 20 +#define FSL_QDMA_CMD_DSEN_OFFSET 19 +#define FSL_QDMA_CMD_LWC_OFFSET 16 + +/* Field definition for Descriptor offset */ +#define QDMA_CCDF_STATUS 20 +#define QDMA_CCDF_OFFSET 20 + +/* Field definition for safe loop count*/ +#define FSL_QDMA_HALT_COUNT 1500 +#define FSL_QDMA_MAX_SIZE 16385 +#define FSL_QDMA_COMP_TIMEOUT 1000 +#define FSL_COMMAND_QUEUE_OVERFLLOW 10 + +#define FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma_engine, x) \ + (((fsl_qdma_engine)->block_offset) * (x)) + +/** + * struct fsl_qdma_format - This is the struct holding describing compound + * descriptor format with qDMA. + * @status: Command status and enqueue status notification. + * @cfg: Frame offset and frame format. + * @addr_lo: Holding the compound descriptor of the lower + * 32-bits address in memory 40-bit address. + * @addr_hi: Same as above member, but point high 8-bits in + * memory 40-bit address. + * @__reserved1: Reserved field. + * @cfg8b_w1: Compound descriptor command queue origin produced + * by qDMA and dynamic debug field. + * @data Pointer to the memory 40-bit address, describes DMA + * source information and DMA destination information. + */ +struct fsl_qdma_format { + __le32 status; + __le32 cfg; + union { + struct { + __le32 addr_lo; + u8 addr_hi; + u8 __reserved1[2]; + u8 cfg8b_w1; + } __packed; + __le64 data; + }; +} __packed; + +/* qDMA status notification pre information */ +struct fsl_pre_status { + u64 addr; + u8 queue; +}; + +static DEFINE_PER_CPU(struct fsl_pre_status, pre); + +struct fsl_qdma_chan { + struct virt_dma_chan vchan; + struct virt_dma_desc vdesc; + enum dma_status status; + struct fsl_qdma_engine *qdma; + struct fsl_qdma_queue *queue; +}; + +struct fsl_qdma_queue { + struct fsl_qdma_format *virt_head; + struct fsl_qdma_format *virt_tail; + struct list_head comp_used; + struct list_head comp_free; + struct dma_pool *comp_pool; + struct dma_pool *desc_pool; + spinlock_t queue_lock; + dma_addr_t bus_addr; + u32 n_cq; + u32 id; + struct fsl_qdma_format *cq; + void __iomem *block_base; +}; + +struct fsl_qdma_comp { + dma_addr_t bus_addr; + dma_addr_t desc_bus_addr; + struct fsl_qdma_format *virt_addr; + struct fsl_qdma_format *desc_virt_addr; + struct fsl_qdma_chan *qchan; + struct virt_dma_desc vdesc; + struct list_head list; +}; + +struct fsl_qdma_engine { + struct dma_device dma_dev; + void __iomem *ctrl_base; + void __iomem *status_base; + void __iomem *block_base; + u32 n_chans; + u32 n_queues; + struct mutex fsl_qdma_mutex; + int error_irq; + int *queue_irq; + u32 feature; + struct fsl_qdma_queue *queue; + struct fsl_qdma_queue **status; + struct fsl_qdma_chan *chans; + int block_number; + int block_offset; + int irq_base; + int desc_allocated; + +}; + +static inline u64 +qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf) +{ + return le64_to_cpu(ccdf->data) & (U64_MAX >> 24); +} + +static inline void +qdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr) +{ + ccdf->addr_hi = upper_32_bits(addr); + ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr)); +} + +static inline u8 +qdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf) +{ + return ccdf->cfg8b_w1 & U8_MAX; +} + +static inline int +qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf) +{ + return (le32_to_cpu(ccdf->cfg) & QDMA_CCDF_MASK) >> QDMA_CCDF_OFFSET; +} + +static inline void +qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset) +{ + ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset); +} + +static inline int +qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf) +{ + return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >> QDMA_CCDF_STATUS; +} + +static inline void +qdma_ccdf_set_ser(struct fsl_qdma_format *ccdf, int status) +{ + ccdf->status = cpu_to_le32(QDMA_CCDF_SER | status); +} + +static inline void qdma_csgf_set_len(struct fsl_qdma_format *csgf, int len) +{ + csgf->cfg = cpu_to_le32(len & QDMA_SG_LEN_MASK); +} + +static inline void qdma_csgf_set_f(struct fsl_qdma_format *csgf, int len) +{ + csgf->cfg = cpu_to_le32(QDMA_SG_FIN | (len & QDMA_SG_LEN_MASK)); +} + +static u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr) +{ + return FSL_DMA_IN(qdma, addr, 32); +} + +static void qdma_writel(struct fsl_qdma_engine *qdma, u32 val, + void __iomem *addr) +{ + FSL_DMA_OUT(qdma, addr, val, 32); +} + +static struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct fsl_qdma_chan, vchan.chan); +} + +static struct fsl_qdma_comp *to_fsl_qdma_comp(struct virt_dma_desc *vd) +{ + return container_of(vd, struct fsl_qdma_comp, vdesc); +} + +static void fsl_qdma_free_chan_resources(struct dma_chan *chan) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma; + struct fsl_qdma_comp *comp_temp, *_comp_temp; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + + if (!fsl_queue->comp_pool && !fsl_queue->comp_pool) + return; + + list_for_each_entry_safe(comp_temp, _comp_temp, + &fsl_queue->comp_used, list) { + dma_pool_free(fsl_queue->comp_pool, + comp_temp->virt_addr, + comp_temp->bus_addr); + dma_pool_free(fsl_queue->desc_pool, + comp_temp->desc_virt_addr, + comp_temp->desc_bus_addr); + list_del(&comp_temp->list); + kfree(comp_temp); + } + + list_for_each_entry_safe(comp_temp, _comp_temp, + &fsl_queue->comp_free, list) { + dma_pool_free(fsl_queue->comp_pool, + comp_temp->virt_addr, + comp_temp->bus_addr); + dma_pool_free(fsl_queue->desc_pool, + comp_temp->desc_virt_addr, + comp_temp->desc_bus_addr); + list_del(&comp_temp->list); + kfree(comp_temp); + } + + dma_pool_destroy(fsl_queue->comp_pool); + dma_pool_destroy(fsl_queue->desc_pool); + + fsl_qdma->desc_allocated--; + fsl_queue->comp_pool = NULL; + fsl_queue->desc_pool = NULL; +} + +static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp, + dma_addr_t dst, dma_addr_t src, u32 len) +{ + struct fsl_qdma_format *sdf, *ddf; + struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest; + + ccdf = fsl_comp->virt_addr; + csgf_desc = fsl_comp->virt_addr + 1; + csgf_src = fsl_comp->virt_addr + 2; + csgf_dest = fsl_comp->virt_addr + 3; + sdf = fsl_comp->desc_virt_addr; + ddf = fsl_comp->desc_virt_addr + 1; + + memset(fsl_comp->virt_addr, 0, FSL_QDMA_COMMAND_BUFFER_SIZE); + memset(fsl_comp->desc_virt_addr, 0, FSL_QDMA_DESCRIPTOR_BUFFER_SIZE); + /* Head Command Descriptor(Frame Descriptor) */ + qdma_desc_addr_set64(ccdf, fsl_comp->bus_addr + 16); + qdma_ccdf_set_format(ccdf, qdma_ccdf_get_offset(ccdf)); + qdma_ccdf_set_ser(ccdf, qdma_ccdf_get_status(ccdf)); + /* Status notification is enqueued to status queue. */ + /* Compound Command Descriptor(Frame List Table) */ + qdma_desc_addr_set64(csgf_desc, fsl_comp->desc_bus_addr); + /* It must be 32 as Compound S/G Descriptor */ + qdma_csgf_set_len(csgf_desc, 32); + qdma_desc_addr_set64(csgf_src, src); + qdma_csgf_set_len(csgf_src, len); + qdma_desc_addr_set64(csgf_dest, dst); + qdma_csgf_set_len(csgf_dest, len); + /* This entry is the last entry. */ + qdma_csgf_set_f(csgf_dest, len); + /* Descriptor Buffer */ + sdf->data = + cpu_to_le64(FSL_QDMA_CMD_RWTTYPE << + FSL_QDMA_CMD_RWTTYPE_OFFSET); + ddf->data = + cpu_to_le64(FSL_QDMA_CMD_RWTTYPE << + FSL_QDMA_CMD_RWTTYPE_OFFSET); + ddf->data |= + cpu_to_le64(FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET); +} + +/* + * Pre-request full command descriptor for enqueue. + */ +static int fsl_qdma_pre_request_enqueue_desc(struct fsl_qdma_queue *queue) +{ + int i; + struct fsl_qdma_comp *comp_temp, *_comp_temp; + + for (i = 0; i < queue->n_cq + FSL_COMMAND_QUEUE_OVERFLLOW; i++) { + comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL); + if (!comp_temp) + goto err_alloc; + comp_temp->virt_addr = + dma_pool_alloc(queue->comp_pool, GFP_KERNEL, + &comp_temp->bus_addr); + if (!comp_temp->virt_addr) + goto err_dma_alloc; + + comp_temp->desc_virt_addr = + dma_pool_alloc(queue->desc_pool, GFP_KERNEL, + &comp_temp->desc_bus_addr); + if (!comp_temp->desc_virt_addr) + goto err_desc_dma_alloc; + + list_add_tail(&comp_temp->list, &queue->comp_free); + } + + return 0; + +err_desc_dma_alloc: + dma_pool_free(queue->comp_pool, comp_temp->virt_addr, + comp_temp->bus_addr); + +err_dma_alloc: + kfree(comp_temp); + +err_alloc: + list_for_each_entry_safe(comp_temp, _comp_temp, + &queue->comp_free, list) { + if (comp_temp->virt_addr) + dma_pool_free(queue->comp_pool, + comp_temp->virt_addr, + comp_temp->bus_addr); + if (comp_temp->desc_virt_addr) + dma_pool_free(queue->desc_pool, + comp_temp->desc_virt_addr, + comp_temp->desc_bus_addr); + + list_del(&comp_temp->list); + kfree(comp_temp); + } + + return -ENOMEM; +} + +/* + * Request a command descriptor for enqueue. + */ +static struct fsl_qdma_comp +*fsl_qdma_request_enqueue_desc(struct fsl_qdma_chan *fsl_chan) +{ + unsigned long flags; + struct fsl_qdma_comp *comp_temp; + int timeout = FSL_QDMA_COMP_TIMEOUT; + struct fsl_qdma_queue *queue = fsl_chan->queue; + + while (timeout--) { + spin_lock_irqsave(&queue->queue_lock, flags); + if (!list_empty(&queue->comp_free)) { + comp_temp = list_first_entry(&queue->comp_free, + struct fsl_qdma_comp, + list); + list_del(&comp_temp->list); + + spin_unlock_irqrestore(&queue->queue_lock, flags); + comp_temp->qchan = fsl_chan; + return comp_temp; + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + udelay(1); + } + + return NULL; +} + +static struct fsl_qdma_queue +*fsl_qdma_alloc_queue_resources(struct platform_device *pdev, + struct fsl_qdma_engine *fsl_qdma) +{ + int ret, len, i, j; + int queue_num, block_number; + unsigned int queue_size[FSL_QDMA_QUEUE_MAX]; + struct fsl_qdma_queue *queue_head, *queue_temp; + + queue_num = fsl_qdma->n_queues; + block_number = fsl_qdma->block_number; + + if (queue_num > FSL_QDMA_QUEUE_MAX) + queue_num = FSL_QDMA_QUEUE_MAX; + len = sizeof(*queue_head) * queue_num * block_number; + queue_head = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!queue_head) + return NULL; + + ret = device_property_read_u32_array(&pdev->dev, "queue-sizes", + queue_size, queue_num); + if (ret) { + dev_err(&pdev->dev, "Can't get queue-sizes.\n"); + return NULL; + } + for (j = 0; j < block_number; j++) { + for (i = 0; i < queue_num; i++) { + if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX || + queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) { + dev_err(&pdev->dev, + "Get wrong queue-sizes.\n"); + return NULL; + } + queue_temp = queue_head + i + (j * queue_num); + + queue_temp->cq = + dma_alloc_coherent(&pdev->dev, + sizeof(struct fsl_qdma_format) * + queue_size[i], + &queue_temp->bus_addr, + GFP_KERNEL); + if (!queue_temp->cq) + return NULL; + queue_temp->block_base = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + queue_temp->n_cq = queue_size[i]; + queue_temp->id = i; + queue_temp->virt_head = queue_temp->cq; + queue_temp->virt_tail = queue_temp->cq; + /* + * List for queue command buffer + */ + INIT_LIST_HEAD(&queue_temp->comp_used); + spin_lock_init(&queue_temp->queue_lock); + } + } + return queue_head; +} + +static struct fsl_qdma_queue +*fsl_qdma_prep_status_queue(struct platform_device *pdev) +{ + int ret; + unsigned int status_size; + struct fsl_qdma_queue *status_head; + struct device_node *np = pdev->dev.of_node; + + ret = of_property_read_u32(np, "status-sizes", &status_size); + if (ret) { + dev_err(&pdev->dev, "Can't get status-sizes.\n"); + return NULL; + } + if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX || + status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) { + dev_err(&pdev->dev, "Get wrong status_size.\n"); + return NULL; + } + status_head = devm_kzalloc(&pdev->dev, + sizeof(*status_head), GFP_KERNEL); + if (!status_head) + return NULL; + + /* + * Buffer for queue command + */ + status_head->cq = dma_alloc_coherent(&pdev->dev, + sizeof(struct fsl_qdma_format) * + status_size, + &status_head->bus_addr, + GFP_KERNEL); + if (!status_head->cq) { + devm_kfree(&pdev->dev, status_head); + return NULL; + } + status_head->n_cq = status_size; + status_head->virt_head = status_head->cq; + status_head->virt_tail = status_head->cq; + status_head->comp_pool = NULL; + + return status_head; +} + +static int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma) +{ + u32 reg; + int i, j, count = FSL_QDMA_HALT_COUNT; + void __iomem *block, *ctrl = fsl_qdma->ctrl_base; + + /* Disable the command queue and wait for idle state. */ + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); + reg |= FSL_QDMA_DMR_DQD; + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); + for (j = 0; j < fsl_qdma->block_number; j++) { + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + for (i = 0; i < FSL_QDMA_QUEUE_NUM_MAX; i++) + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQMR(i)); + } + while (1) { + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DSR); + if (!(reg & FSL_QDMA_DSR_DB)) + break; + if (count-- < 0) + return -EBUSY; + udelay(100); + } + + for (j = 0; j < fsl_qdma->block_number; j++) { + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + + /* Disable status queue. */ + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR); + + /* + * clear the command queue interrupt detect register for + * all queues. + */ + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, + block + FSL_QDMA_BCQIDR(0)); + } + + return 0; +} + +static int +fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma, + void *block, + int id) +{ + bool duplicate; + u32 reg, i, count; + struct fsl_qdma_queue *temp_queue; + struct fsl_qdma_format *status_addr; + struct fsl_qdma_comp *fsl_comp = NULL; + struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue; + struct fsl_qdma_queue *fsl_status = fsl_qdma->status[id]; + + count = FSL_QDMA_MAX_SIZE; + + while (count--) { + duplicate = 0; + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR); + if (reg & FSL_QDMA_BSQSR_QE) + return 0; + + status_addr = fsl_status->virt_head; + + if (qdma_ccdf_get_queue(status_addr) == + __this_cpu_read(pre.queue) && + qdma_ccdf_addr_get64(status_addr) == + __this_cpu_read(pre.addr)) + duplicate = 1; + i = qdma_ccdf_get_queue(status_addr) + + id * fsl_qdma->n_queues; + __this_cpu_write(pre.addr, qdma_ccdf_addr_get64(status_addr)); + __this_cpu_write(pre.queue, qdma_ccdf_get_queue(status_addr)); + temp_queue = fsl_queue + i; + + spin_lock(&temp_queue->queue_lock); + if (list_empty(&temp_queue->comp_used)) { + if (!duplicate) { + spin_unlock(&temp_queue->queue_lock); + return -EAGAIN; + } + } else { + fsl_comp = list_first_entry(&temp_queue->comp_used, + struct fsl_qdma_comp, list); + if (fsl_comp->bus_addr + 16 != + __this_cpu_read(pre.addr)) { + if (!duplicate) { + spin_unlock(&temp_queue->queue_lock); + return -EAGAIN; + } + } + } + + if (duplicate) { + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); + reg |= FSL_QDMA_BSQMR_DI; + qdma_desc_addr_set64(status_addr, 0x0); + fsl_status->virt_head++; + if (fsl_status->virt_head == fsl_status->cq + + fsl_status->n_cq) + fsl_status->virt_head = fsl_status->cq; + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); + spin_unlock(&temp_queue->queue_lock); + continue; + } + list_del(&fsl_comp->list); + + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); + reg |= FSL_QDMA_BSQMR_DI; + qdma_desc_addr_set64(status_addr, 0x0); + fsl_status->virt_head++; + if (fsl_status->virt_head == fsl_status->cq + fsl_status->n_cq) + fsl_status->virt_head = fsl_status->cq; + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); + spin_unlock(&temp_queue->queue_lock); + + spin_lock(&fsl_comp->qchan->vchan.lock); + vchan_cookie_complete(&fsl_comp->vdesc); + fsl_comp->qchan->status = DMA_COMPLETE; + spin_unlock(&fsl_comp->qchan->vchan.lock); + } + + return 0; +} + +static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id) +{ + unsigned int intr; + struct fsl_qdma_engine *fsl_qdma = dev_id; + void __iomem *status = fsl_qdma->status_base; + + intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR); + + if (intr) { + dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n"); + return IRQ_NONE; + } + + qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR); + return IRQ_HANDLED; +} + +static irqreturn_t fsl_qdma_queue_handler(int irq, void *dev_id) +{ + int id; + unsigned int intr, reg; + struct fsl_qdma_engine *fsl_qdma = dev_id; + void __iomem *block, *ctrl = fsl_qdma->ctrl_base; + + id = irq - fsl_qdma->irq_base; + if (id < 0 && id > fsl_qdma->block_number) { + dev_err(fsl_qdma->dma_dev.dev, + "irq %d is wrong irq_base is %d\n", + irq, fsl_qdma->irq_base); + } + + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, id); + + intr = qdma_readl(fsl_qdma, block + FSL_QDMA_BCQIDR(0)); + + if ((intr & FSL_QDMA_CQIDR_SQT) != 0) + intr = fsl_qdma_queue_transfer_complete(fsl_qdma, block, id); + + if (intr != 0) { + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); + reg |= FSL_QDMA_DMR_DQD; + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQIER(0)); + dev_err(fsl_qdma->dma_dev.dev, "QDMA: status err!\n"); + } + + /* Clear all detected events and interrupts. */ + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, + block + FSL_QDMA_BCQIDR(0)); + + return IRQ_HANDLED; +} + +static int +fsl_qdma_irq_init(struct platform_device *pdev, + struct fsl_qdma_engine *fsl_qdma) +{ + int i; + int cpu; + int ret; + char irq_name[20]; + + fsl_qdma->error_irq = + platform_get_irq_byname(pdev, "qdma-error"); + if (fsl_qdma->error_irq < 0) { + dev_err(&pdev->dev, "Can't get qdma controller irq.\n"); + return fsl_qdma->error_irq; + } + + ret = devm_request_irq(&pdev->dev, fsl_qdma->error_irq, + fsl_qdma_error_handler, 0, + "qDMA error", fsl_qdma); + if (ret) { + dev_err(&pdev->dev, "Can't register qDMA controller IRQ.\n"); + return ret; + } + + for (i = 0; i < fsl_qdma->block_number; i++) { + sprintf(irq_name, "qdma-queue%d", i); + fsl_qdma->queue_irq[i] = + platform_get_irq_byname(pdev, irq_name); + + if (fsl_qdma->queue_irq[i] < 0) { + dev_err(&pdev->dev, + "Can't get qdma queue %d irq.\n", i); + return fsl_qdma->queue_irq[i]; + } + + ret = devm_request_irq(&pdev->dev, + fsl_qdma->queue_irq[i], + fsl_qdma_queue_handler, + 0, + "qDMA queue", + fsl_qdma); + if (ret) { + dev_err(&pdev->dev, + "Can't register qDMA queue IRQ.\n"); + return ret; + } + + cpu = i % num_online_cpus(); + ret = irq_set_affinity_hint(fsl_qdma->queue_irq[i], + get_cpu_mask(cpu)); + if (ret) { + dev_err(&pdev->dev, + "Can't set cpu %d affinity to IRQ %d.\n", + cpu, + fsl_qdma->queue_irq[i]); + return ret; + } + } + + return 0; +} + +static void fsl_qdma_irq_exit(struct platform_device *pdev, + struct fsl_qdma_engine *fsl_qdma) +{ + int i; + + devm_free_irq(&pdev->dev, fsl_qdma->error_irq, fsl_qdma); + for (i = 0; i < fsl_qdma->block_number; i++) + devm_free_irq(&pdev->dev, fsl_qdma->queue_irq[i], fsl_qdma); +} + +static int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma) +{ + u32 reg; + int i, j, ret; + struct fsl_qdma_queue *temp; + void __iomem *status = fsl_qdma->status_base; + void __iomem *block, *ctrl = fsl_qdma->ctrl_base; + struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue; + + /* Try to halt the qDMA engine first. */ + ret = fsl_qdma_halt(fsl_qdma); + if (ret) { + dev_err(fsl_qdma->dma_dev.dev, "DMA halt failed!"); + return ret; + } + + for (i = 0; i < fsl_qdma->block_number; i++) { + /* + * Clear the command queue interrupt detect register for + * all queues. + */ + + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, i); + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, + block + FSL_QDMA_BCQIDR(0)); + } + + for (j = 0; j < fsl_qdma->block_number; j++) { + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + for (i = 0; i < fsl_qdma->n_queues; i++) { + temp = fsl_queue + i + (j * fsl_qdma->n_queues); + /* + * Initialize Command Queue registers to + * point to the first + * command descriptor in memory. + * Dequeue Pointer Address Registers + * Enqueue Pointer Address Registers + */ + + qdma_writel(fsl_qdma, temp->bus_addr, + block + FSL_QDMA_BCQDPA_SADDR(i)); + qdma_writel(fsl_qdma, temp->bus_addr, + block + FSL_QDMA_BCQEPA_SADDR(i)); + + /* Initialize the queue mode. */ + reg = FSL_QDMA_BCQMR_EN; + reg |= FSL_QDMA_BCQMR_CD_THLD(ilog2(temp->n_cq) - 4); + reg |= FSL_QDMA_BCQMR_CQ_SIZE(ilog2(temp->n_cq) - 6); + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BCQMR(i)); + } + + /* + * Workaround for erratum: ERR010812. + * We must enable XOFF to avoid the enqueue rejection occurs. + * Setting SQCCMR ENTER_WM to 0x20. + */ + + qdma_writel(fsl_qdma, FSL_QDMA_SQCCMR_ENTER_WM, + block + FSL_QDMA_SQCCMR); + + /* + * Initialize status queue registers to point to the first + * command descriptor in memory. + * Dequeue Pointer Address Registers + * Enqueue Pointer Address Registers + */ + + qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr, + block + FSL_QDMA_SQEPAR); + qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr, + block + FSL_QDMA_SQDPAR); + /* Initialize status queue interrupt. */ + qdma_writel(fsl_qdma, FSL_QDMA_BCQIER_CQTIE, + block + FSL_QDMA_BCQIER(0)); + qdma_writel(fsl_qdma, FSL_QDMA_BSQICR_ICEN | + FSL_QDMA_BSQICR_ICST(5) | 0x8000, + block + FSL_QDMA_BSQICR); + qdma_writel(fsl_qdma, FSL_QDMA_CQIER_MEIE | + FSL_QDMA_CQIER_TEIE, + block + FSL_QDMA_CQIER); + + /* Initialize the status queue mode. */ + reg = FSL_QDMA_BSQMR_EN; + reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2 + (fsl_qdma->status[j]->n_cq) - 6); + + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); + } + + /* Initialize controller interrupt register. */ + qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR); + qdma_writel(fsl_qdma, FSL_QDMA_DEIER_CLEAR, status + FSL_QDMA_DEIER); + + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); + reg &= ~FSL_QDMA_DMR_DQD; + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); + + return 0; +} + +static struct dma_async_tx_descriptor * +fsl_qdma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long flags) +{ + struct fsl_qdma_comp *fsl_comp; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + + fsl_comp = fsl_qdma_request_enqueue_desc(fsl_chan); + + if (!fsl_comp) + return NULL; + + fsl_qdma_comp_fill_memcpy(fsl_comp, dst, src, len); + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_comp->vdesc, flags); +} + +static void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan) +{ + u32 reg; + struct virt_dma_desc *vdesc; + struct fsl_qdma_comp *fsl_comp; + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + void __iomem *block = fsl_queue->block_base; + + reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQSR(fsl_queue->id)); + if (reg & (FSL_QDMA_BCQSR_QF | FSL_QDMA_BCQSR_XOFF)) + return; + vdesc = vchan_next_desc(&fsl_chan->vchan); + if (!vdesc) + return; + list_del(&vdesc->node); + fsl_comp = to_fsl_qdma_comp(vdesc); + + memcpy(fsl_queue->virt_head++, + fsl_comp->virt_addr, sizeof(struct fsl_qdma_format)); + if (fsl_queue->virt_head == fsl_queue->cq + fsl_queue->n_cq) + fsl_queue->virt_head = fsl_queue->cq; + + list_add_tail(&fsl_comp->list, &fsl_queue->comp_used); + barrier(); + reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQMR(fsl_queue->id)); + reg |= FSL_QDMA_BCQMR_EI; + qdma_writel(fsl_chan->qdma, reg, block + FSL_QDMA_BCQMR(fsl_queue->id)); + fsl_chan->status = DMA_IN_PROGRESS; +} + +static void fsl_qdma_free_desc(struct virt_dma_desc *vdesc) +{ + unsigned long flags; + struct fsl_qdma_comp *fsl_comp; + struct fsl_qdma_queue *fsl_queue; + + fsl_comp = to_fsl_qdma_comp(vdesc); + fsl_queue = fsl_comp->qchan->queue; + + spin_lock_irqsave(&fsl_queue->queue_lock, flags); + list_add_tail(&fsl_comp->list, &fsl_queue->comp_free); + spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); +} + +static void fsl_qdma_issue_pending(struct dma_chan *chan) +{ + unsigned long flags; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + + spin_lock_irqsave(&fsl_queue->queue_lock, flags); + spin_lock(&fsl_chan->vchan.lock); + if (vchan_issue_pending(&fsl_chan->vchan)) + fsl_qdma_enqueue_desc(fsl_chan); + spin_unlock(&fsl_chan->vchan.lock); + spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); +} + +static void fsl_qdma_synchronize(struct dma_chan *chan) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + + vchan_synchronize(&fsl_chan->vchan); +} + +static int fsl_qdma_terminate_all(struct dma_chan *chan) +{ + LIST_HEAD(head); + unsigned long flags; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + return 0; +} + +static int fsl_qdma_alloc_chan_resources(struct dma_chan *chan) +{ + int ret; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma; + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + + if (fsl_queue->comp_pool && fsl_queue->desc_pool) + return fsl_qdma->desc_allocated; + + INIT_LIST_HEAD(&fsl_queue->comp_free); + + /* + * The dma pool for queue command buffer + */ + fsl_queue->comp_pool = + dma_pool_create("comp_pool", + chan->device->dev, + FSL_QDMA_COMMAND_BUFFER_SIZE, + 64, 0); + if (!fsl_queue->comp_pool) + return -ENOMEM; + + /* + * The dma pool for Descriptor(SD/DD) buffer + */ + fsl_queue->desc_pool = + dma_pool_create("desc_pool", + chan->device->dev, + FSL_QDMA_DESCRIPTOR_BUFFER_SIZE, + 32, 0); + if (!fsl_queue->desc_pool) + goto err_desc_pool; + + ret = fsl_qdma_pre_request_enqueue_desc(fsl_queue); + if (ret) { + dev_err(chan->device->dev, + "failed to alloc dma buffer for S/G descriptor\n"); + goto err_mem; + } + + fsl_qdma->desc_allocated++; + return fsl_qdma->desc_allocated; + +err_mem: + dma_pool_destroy(fsl_queue->desc_pool); +err_desc_pool: + dma_pool_destroy(fsl_queue->comp_pool); + return -ENOMEM; +} + +static int fsl_qdma_probe(struct platform_device *pdev) +{ + int ret, i; + int blk_num, blk_off; + u32 len, chans, queues; + struct resource *res; + struct fsl_qdma_chan *fsl_chan; + struct fsl_qdma_engine *fsl_qdma; + struct device_node *np = pdev->dev.of_node; + + ret = of_property_read_u32(np, "dma-channels", &chans); + if (ret) { + dev_err(&pdev->dev, "Can't get dma-channels.\n"); + return ret; + } + + ret = of_property_read_u32(np, "block-offset", &blk_off); + if (ret) { + dev_err(&pdev->dev, "Can't get block-offset.\n"); + return ret; + } + + ret = of_property_read_u32(np, "block-number", &blk_num); + if (ret) { + dev_err(&pdev->dev, "Can't get block-number.\n"); + return ret; + } + + blk_num = min_t(int, blk_num, num_online_cpus()); + + len = sizeof(*fsl_qdma); + fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma) + return -ENOMEM; + + len = sizeof(*fsl_chan) * chans; + fsl_qdma->chans = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma->chans) + return -ENOMEM; + + len = sizeof(struct fsl_qdma_queue *) * blk_num; + fsl_qdma->status = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma->status) + return -ENOMEM; + + len = sizeof(int) * blk_num; + fsl_qdma->queue_irq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma->queue_irq) + return -ENOMEM; + + ret = of_property_read_u32(np, "fsl,dma-queues", &queues); + if (ret) { + dev_err(&pdev->dev, "Can't get queues.\n"); + return ret; + } + + fsl_qdma->desc_allocated = 0; + fsl_qdma->n_chans = chans; + fsl_qdma->n_queues = queues; + fsl_qdma->block_number = blk_num; + fsl_qdma->block_offset = blk_off; + + mutex_init(&fsl_qdma->fsl_qdma_mutex); + + for (i = 0; i < fsl_qdma->block_number; i++) { + fsl_qdma->status[i] = fsl_qdma_prep_status_queue(pdev); + if (!fsl_qdma->status[i]) + return -ENOMEM; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fsl_qdma->ctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_qdma->ctrl_base)) + return PTR_ERR(fsl_qdma->ctrl_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + fsl_qdma->status_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_qdma->status_base)) + return PTR_ERR(fsl_qdma->status_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + fsl_qdma->block_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_qdma->block_base)) + return PTR_ERR(fsl_qdma->block_base); + fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, fsl_qdma); + if (!fsl_qdma->queue) + return -ENOMEM; + + ret = fsl_qdma_irq_init(pdev, fsl_qdma); + if (ret) + return ret; + + fsl_qdma->irq_base = platform_get_irq_byname(pdev, "qdma-queue0"); + fsl_qdma->feature = of_property_read_bool(np, "big-endian"); + INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels); + + for (i = 0; i < fsl_qdma->n_chans; i++) { + struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i]; + + fsl_chan->qdma = fsl_qdma; + fsl_chan->queue = fsl_qdma->queue + i % (fsl_qdma->n_queues * + fsl_qdma->block_number); + fsl_chan->vchan.desc_free = fsl_qdma_free_desc; + vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev); + } + + dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask); + + fsl_qdma->dma_dev.dev = &pdev->dev; + fsl_qdma->dma_dev.device_free_chan_resources = + fsl_qdma_free_chan_resources; + fsl_qdma->dma_dev.device_alloc_chan_resources = + fsl_qdma_alloc_chan_resources; + fsl_qdma->dma_dev.device_tx_status = dma_cookie_status; + fsl_qdma->dma_dev.device_prep_dma_memcpy = fsl_qdma_prep_memcpy; + fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending; + fsl_qdma->dma_dev.device_synchronize = fsl_qdma_synchronize; + fsl_qdma->dma_dev.device_terminate_all = fsl_qdma_terminate_all; + + dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); + + platform_set_drvdata(pdev, fsl_qdma); + + ret = dma_async_device_register(&fsl_qdma->dma_dev); + if (ret) { + dev_err(&pdev->dev, + "Can't register NXP Layerscape qDMA engine.\n"); + return ret; + } + + ret = fsl_qdma_reg_init(fsl_qdma); + if (ret) { + dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n"); + return ret; + } + + return 0; +} + +static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev) +{ + struct fsl_qdma_chan *chan, *_chan; + + list_for_each_entry_safe(chan, _chan, + &dmadev->channels, vchan.chan.device_node) { + list_del(&chan->vchan.chan.device_node); + tasklet_kill(&chan->vchan.task); + } +} + +static int fsl_qdma_remove(struct platform_device *pdev) +{ + int i; + struct fsl_qdma_queue *status; + struct device_node *np = pdev->dev.of_node; + struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev); + + fsl_qdma_irq_exit(pdev, fsl_qdma); + fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev); + of_dma_controller_free(np); + dma_async_device_unregister(&fsl_qdma->dma_dev); + + for (i = 0; i < fsl_qdma->block_number; i++) { + status = fsl_qdma->status[i]; + dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) * + status->n_cq, status->cq, status->bus_addr); + } + return 0; +} + +static const struct of_device_id fsl_qdma_dt_ids[] = { + { .compatible = "fsl,ls1021a-qdma", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids); + +static struct platform_driver fsl_qdma_driver = { + .driver = { + .name = "fsl-qdma", + .of_match_table = fsl_qdma_dt_ids, + }, + .probe = fsl_qdma_probe, + .remove = fsl_qdma_remove, +}; + +module_platform_driver(fsl_qdma_driver); + +MODULE_ALIAS("platform:fsl-qdma"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver"); diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 9d360a3fbae3..1e38e6b94006 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -53,42 +53,42 @@ static const char msg_ld_oom[] = "No free memory for link descriptor"; static void set_sr(struct fsldma_chan *chan, u32 val) { - DMA_OUT(chan, &chan->regs->sr, val, 32); + FSL_DMA_OUT(chan, &chan->regs->sr, val, 32); } static u32 get_sr(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->sr, 32); + return FSL_DMA_IN(chan, &chan->regs->sr, 32); } static void set_mr(struct fsldma_chan *chan, u32 val) { - DMA_OUT(chan, &chan->regs->mr, val, 32); + FSL_DMA_OUT(chan, &chan->regs->mr, val, 32); } static u32 get_mr(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->mr, 32); + return FSL_DMA_IN(chan, &chan->regs->mr, 32); } static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr) { - DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64); + FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64); } static dma_addr_t get_cdar(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; + return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; } static void set_bcr(struct fsldma_chan *chan, u32 val) { - DMA_OUT(chan, &chan->regs->bcr, val, 32); + FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32); } static u32 get_bcr(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->bcr, 32); + return FSL_DMA_IN(chan, &chan->regs->bcr, 32); } /* diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 4787d485dd76..a9b12f82b5c3 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -196,39 +196,67 @@ struct fsldma_chan { #define to_fsl_desc(lh) container_of(lh, struct fsl_desc_sw, node) #define tx_to_fsl_desc(tx) container_of(tx, struct fsl_desc_sw, async_tx) -#ifndef __powerpc64__ -static u64 in_be64(const u64 __iomem *addr) +#ifdef CONFIG_PPC +#define fsl_ioread32(p) in_le32(p) +#define fsl_ioread32be(p) in_be32(p) +#define fsl_iowrite32(v, p) out_le32(p, v) +#define fsl_iowrite32be(v, p) out_be32(p, v) + +#ifdef __powerpc64__ +#define fsl_ioread64(p) in_le64(p) +#define fsl_ioread64be(p) in_be64(p) +#define fsl_iowrite64(v, p) out_le64(p, v) +#define fsl_iowrite64be(v, p) out_be64(p, v) +#else +static u64 fsl_ioread64(const u64 __iomem *addr) { - return ((u64)in_be32((u32 __iomem *)addr) << 32) | - (in_be32((u32 __iomem *)addr + 1)); + u32 fsl_addr = lower_32_bits(addr); + u64 fsl_addr_hi = (u64)in_le32((u32 *)(fsl_addr + 1)) << 32; + + return fsl_addr_hi | in_le32((u32 *)fsl_addr); } -static void out_be64(u64 __iomem *addr, u64 val) +static void fsl_iowrite64(u64 val, u64 __iomem *addr) { - out_be32((u32 __iomem *)addr, val >> 32); - out_be32((u32 __iomem *)addr + 1, (u32)val); + out_le32((u32 __iomem *)addr + 1, val >> 32); + out_le32((u32 __iomem *)addr, (u32)val); } -/* There is no asm instructions for 64 bits reverse loads and stores */ -static u64 in_le64(const u64 __iomem *addr) +static u64 fsl_ioread64be(const u64 __iomem *addr) { - return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) | - (in_le32((u32 __iomem *)addr)); + u32 fsl_addr = lower_32_bits(addr); + u64 fsl_addr_hi = (u64)in_be32((u32 *)fsl_addr) << 32; + + return fsl_addr_hi | in_be32((u32 *)(fsl_addr + 1)); } -static void out_le64(u64 __iomem *addr, u64 val) +static void fsl_iowrite64be(u64 val, u64 __iomem *addr) { - out_le32((u32 __iomem *)addr + 1, val >> 32); - out_le32((u32 __iomem *)addr, (u32)val); + out_be32((u32 __iomem *)addr, val >> 32); + out_be32((u32 __iomem *)addr + 1, (u32)val); } #endif +#endif -#define DMA_IN(fsl_chan, addr, width) \ - (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ - in_be##width(addr) : in_le##width(addr)) -#define DMA_OUT(fsl_chan, addr, val, width) \ - (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ - out_be##width(addr, val) : out_le##width(addr, val)) +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM) +#define fsl_ioread32(p) ioread32(p) +#define fsl_ioread32be(p) ioread32be(p) +#define fsl_iowrite32(v, p) iowrite32(v, p) +#define fsl_iowrite32be(v, p) iowrite32be(v, p) +#define fsl_ioread64(p) ioread64(p) +#define fsl_ioread64be(p) ioread64be(p) +#define fsl_iowrite64(v, p) iowrite64(v, p) +#define fsl_iowrite64be(v, p) iowrite64be(v, p) +#endif + +#define FSL_DMA_IN(fsl_dma, addr, width) \ + (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \ + fsl_ioread##width##be(addr) : fsl_ioread##width(addr)) + +#define FSL_DMA_OUT(fsl_dma, addr, val, width) \ + (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \ + fsl_iowrite##width##be(val, addr) : fsl_iowrite \ + ##width(val, addr)) #define DMA_TO_CPU(fsl_chan, d, width) \ (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 4a09af3cd546..00a089e24150 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -278,14 +278,14 @@ static int imxdma_hw_chain(struct imxdma_channel *imxdmac) /* * imxdma_sg_next - prepare next chunk for scatter-gather DMA emulation */ -static inline int imxdma_sg_next(struct imxdma_desc *d) +static inline void imxdma_sg_next(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); struct imxdma_engine *imxdma = imxdmac->imxdma; struct scatterlist *sg = d->sg; - unsigned long now; + size_t now; - now = min(d->len, sg_dma_len(sg)); + now = min_t(size_t, d->len, sg_dma_len(sg)); if (d->len != IMX_DMA_LENGTH_LOOP) d->len -= now; @@ -303,8 +303,6 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) imx_dmav1_readl(imxdma, DMA_DAR(imxdmac->channel)), imx_dmav1_readl(imxdma, DMA_SAR(imxdmac->channel)), imx_dmav1_readl(imxdma, DMA_CNTR(imxdmac->channel))); - - return now; } static void imxdma_enable_hw(struct imxdma_desc *d) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 86708fb9bda1..5f3c1378b90e 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -377,6 +377,7 @@ struct sdma_channel { unsigned long watermark_level; u32 shp_addr, per_addr; enum dma_status status; + bool context_loaded; struct imx_dma_data data; struct work_struct terminate_worker; }; @@ -440,6 +441,8 @@ struct sdma_engine { unsigned int irq; dma_addr_t bd0_phys; struct sdma_buffer_descriptor *bd0; + /* clock ratio for AHB:SDMA core. 1:1 is 1, 2:1 is 0*/ + bool clk_ratio; }; static int sdma_config_write(struct dma_chan *chan, @@ -662,8 +665,11 @@ static int sdma_run_channel0(struct sdma_engine *sdma) dev_err(sdma->dev, "Timeout waiting for CH0 ready\n"); /* Set bits of CONFIG register with dynamic context switching */ - if (readl(sdma->regs + SDMA_H_CONFIG) == 0) - writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); + reg = readl(sdma->regs + SDMA_H_CONFIG); + if ((reg & SDMA_H_CONFIG_CSM) == 0) { + reg |= SDMA_H_CONFIG_CSM; + writel_relaxed(reg, sdma->regs + SDMA_H_CONFIG); + } return ret; } @@ -677,7 +683,7 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, int ret; unsigned long flags; - buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); + buf_virt = dma_alloc_coherent(sdma->dev, size, &buf_phys, GFP_KERNEL); if (!buf_virt) { return -ENOMEM; } @@ -696,7 +702,7 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, spin_unlock_irqrestore(&sdma->channel_0_lock, flags); - dma_free_coherent(NULL, size, buf_virt, buf_phys); + dma_free_coherent(sdma->dev, size, buf_virt, buf_phys); return ret; } @@ -970,6 +976,9 @@ static int sdma_load_context(struct sdma_channel *sdmac) int ret; unsigned long flags; + if (sdmac->context_loaded) + return 0; + if (sdmac->direction == DMA_DEV_TO_MEM) load_address = sdmac->pc_from_device; else if (sdmac->direction == DMA_DEV_TO_DEV) @@ -1012,6 +1021,8 @@ static int sdma_load_context(struct sdma_channel *sdmac) spin_unlock_irqrestore(&sdma->channel_0_lock, flags); + sdmac->context_loaded = true; + return ret; } @@ -1051,6 +1062,7 @@ static void sdma_channel_terminate_work(struct work_struct *work) sdmac->desc = NULL; spin_unlock_irqrestore(&sdmac->vc.lock, flags); vchan_dma_desc_free_list(&sdmac->vc, &head); + sdmac->context_loaded = false; } static int sdma_disable_channel_async(struct dma_chan *chan) @@ -1182,8 +1194,8 @@ static int sdma_request_channel0(struct sdma_engine *sdma) { int ret = -EBUSY; - sdma->bd0 = dma_alloc_coherent(NULL, PAGE_SIZE, &sdma->bd0_phys, - GFP_NOWAIT); + sdma->bd0 = dma_alloc_coherent(sdma->dev, PAGE_SIZE, &sdma->bd0_phys, + GFP_NOWAIT); if (!sdma->bd0) { ret = -ENOMEM; goto out; @@ -1205,8 +1217,8 @@ static int sdma_alloc_bd(struct sdma_desc *desc) u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor); int ret = 0; - desc->bd = dma_alloc_coherent(NULL, bd_size, &desc->bd_phys, - GFP_NOWAIT); + desc->bd = dma_alloc_coherent(desc->sdmac->sdma->dev, bd_size, + &desc->bd_phys, GFP_NOWAIT); if (!desc->bd) { ret = -ENOMEM; goto out; @@ -1219,7 +1231,8 @@ static void sdma_free_bd(struct sdma_desc *desc) { u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor); - dma_free_coherent(NULL, bd_size, desc->bd, desc->bd_phys); + dma_free_coherent(desc->sdmac->sdma->dev, bd_size, desc->bd, + desc->bd_phys); } static void sdma_desc_free(struct virt_dma_desc *vd) @@ -1839,10 +1852,13 @@ static int sdma_init(struct sdma_engine *sdma) if (ret) goto disable_clk_ipg; + if (clk_get_rate(sdma->clk_ahb) == clk_get_rate(sdma->clk_ipg)) + sdma->clk_ratio = 1; + /* Be sure SDMA has not started yet */ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); - sdma->channel_control = dma_alloc_coherent(NULL, + sdma->channel_control = dma_alloc_coherent(sdma->dev, MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + sizeof(struct sdma_context_data), &ccb_phys, GFP_KERNEL); @@ -1879,8 +1895,10 @@ static int sdma_init(struct sdma_engine *sdma) writel_relaxed(0x4050, sdma->regs + SDMA_CHN0ADDR); /* Set bits of CONFIG register but with static context switching */ - /* FIXME: Check whether to set ACR bit depending on clock ratios */ - writel_relaxed(0, sdma->regs + SDMA_H_CONFIG); + if (sdma->clk_ratio) + writel_relaxed(SDMA_H_CONFIG_ACR, sdma->regs + SDMA_H_CONFIG); + else + writel_relaxed(0, sdma->regs + SDMA_H_CONFIG); writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR); @@ -1903,11 +1921,16 @@ disable_clk_ipg: static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param) { struct sdma_channel *sdmac = to_sdma_chan(chan); + struct sdma_engine *sdma = sdmac->sdma; struct imx_dma_data *data = fn_param; if (!imx_dma_is_general_purpose(chan)) return false; + /* return false if it's not the right device */ + if (sdma->dev->of_node != data->of_node) + return false; + sdmac->data = *data; chan->private = &sdmac->data; @@ -1935,6 +1958,7 @@ static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec, * be set to sdmac->event_id1. */ data.dma_request2 = 0; + data.of_node = ofdma->of_node; return dma_request_channel(mask, sdma_filter_fn, &data); } @@ -2097,6 +2121,7 @@ static int sdma_probe(struct platform_device *pdev) sdma->dma_device.device_prep_dma_memcpy = sdma_prep_memcpy; sdma->dma_device.device_issue_pending = sdma_issue_pending; sdma->dma_device.dev->dma_parms = &sdma->dma_parms; + sdma->dma_device.copy_align = 2; dma_set_max_seg_size(sdma->dma_device.dev, SDMA_BD_MAX_CNT); platform_set_drvdata(pdev, sdma); diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 23fb2fa04000..f373a139e0c3 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -372,6 +372,7 @@ struct ioat_ring_ent ** ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags) { struct ioatdma_chan *ioat_chan = to_ioat_chan(c); + struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; struct ioat_ring_ent **ring; int total_descs = 1 << order; int i, chunks; @@ -437,6 +438,17 @@ ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags) } ring[i]->hw->next = ring[0]->txd.phys; + /* setup descriptor pre-fetching for v3.4 */ + if (ioat_dma->cap & IOAT_CAP_DPS) { + u16 drsctl = IOAT_CHAN_DRSZ_2MB | IOAT_CHAN_DRS_EN; + + if (chunks == 1) + drsctl |= IOAT_CHAN_DRS_AUTOWRAP; + + writew(drsctl, ioat_chan->reg_base + IOAT_CHAN_DRSCTL_OFFSET); + + } + return ring; } diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 1ab42ec2b7ff..aaafd0e882b5 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -27,7 +27,7 @@ #include "registers.h" #include "hw.h" -#define IOAT_DMA_VERSION "4.00" +#define IOAT_DMA_VERSION "5.00" #define IOAT_DMA_DCA_ANY_CPU ~0 diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index abcc51b343ce..781c94de8e81 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -66,11 +66,14 @@ #define PCI_DEVICE_ID_INTEL_IOAT_SKX 0x2021 +#define PCI_DEVICE_ID_INTEL_IOAT_ICX 0x0b00 + #define IOAT_VER_1_2 0x12 /* Version 1.2 */ #define IOAT_VER_2_0 0x20 /* Version 2.0 */ #define IOAT_VER_3_0 0x30 /* Version 3.0 */ #define IOAT_VER_3_2 0x32 /* Version 3.2 */ #define IOAT_VER_3_3 0x33 /* Version 3.3 */ +#define IOAT_VER_3_4 0x34 /* Version 3.4 */ int system_has_dca_enabled(struct pci_dev *pdev); diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 2d810dfcdc48..d41dc9a9ff68 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -119,6 +119,9 @@ static const struct pci_device_id ioat_pci_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE2) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE3) }, + /* I/OAT v3.4 platforms */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_ICX) }, + { 0, } }; MODULE_DEVICE_TABLE(pci, ioat_pci_tbl); @@ -135,10 +138,10 @@ static int ioat3_dma_self_test(struct ioatdma_device *ioat_dma); static int ioat_dca_enabled = 1; module_param(ioat_dca_enabled, int, 0644); MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)"); -int ioat_pending_level = 4; +int ioat_pending_level = 7; module_param(ioat_pending_level, int, 0644); MODULE_PARM_DESC(ioat_pending_level, - "high-water mark for pushing ioat descriptors (default: 4)"); + "high-water mark for pushing ioat descriptors (default: 7)"); static char ioat_interrupt_style[32] = "msix"; module_param_string(ioat_interrupt_style, ioat_interrupt_style, sizeof(ioat_interrupt_style), 0644); @@ -635,6 +638,11 @@ static void ioat_free_chan_resources(struct dma_chan *c) ioat_stop(ioat_chan); ioat_reset_hw(ioat_chan); + /* Put LTR to idle */ + if (ioat_dma->version >= IOAT_VER_3_4) + writeb(IOAT_CHAN_LTR_SWSEL_IDLE, + ioat_chan->reg_base + IOAT_CHAN_LTR_SWSEL_OFFSET); + spin_lock_bh(&ioat_chan->cleanup_lock); spin_lock_bh(&ioat_chan->prep_lock); descs = ioat_ring_space(ioat_chan); @@ -724,6 +732,28 @@ static int ioat_alloc_chan_resources(struct dma_chan *c) spin_unlock_bh(&ioat_chan->prep_lock); spin_unlock_bh(&ioat_chan->cleanup_lock); + /* Setting up LTR values for 3.4 or later */ + if (ioat_chan->ioat_dma->version >= IOAT_VER_3_4) { + u32 lat_val; + + lat_val = IOAT_CHAN_LTR_ACTIVE_SNVAL | + IOAT_CHAN_LTR_ACTIVE_SNLATSCALE | + IOAT_CHAN_LTR_ACTIVE_SNREQMNT; + writel(lat_val, ioat_chan->reg_base + + IOAT_CHAN_LTR_ACTIVE_OFFSET); + + lat_val = IOAT_CHAN_LTR_IDLE_SNVAL | + IOAT_CHAN_LTR_IDLE_SNLATSCALE | + IOAT_CHAN_LTR_IDLE_SNREQMNT; + writel(lat_val, ioat_chan->reg_base + + IOAT_CHAN_LTR_IDLE_OFFSET); + + /* Select to active */ + writeb(IOAT_CHAN_LTR_SWSEL_ACTIVE, + ioat_chan->reg_base + + IOAT_CHAN_LTR_SWSEL_OFFSET); + } + ioat_start_null_desc(ioat_chan); /* check that we got off the ground */ @@ -1185,6 +1215,10 @@ static int ioat3_dma_probe(struct ioatdma_device *ioat_dma, int dca) if (err) return err; + if (ioat_dma->cap & IOAT_CAP_DPS) + writeb(ioat_pending_level + 1, + ioat_dma->reg_base + IOAT_PREFETCH_LIMIT_OFFSET); + return 0; } @@ -1350,6 +1384,8 @@ static int ioat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, device); device->version = readb(device->reg_base + IOAT_VER_OFFSET); + if (device->version >= IOAT_VER_3_4) + ioat_dca_enabled = 0; if (device->version >= IOAT_VER_3_0) { if (is_skx_ioat(pdev)) device->version = IOAT_VER_3_2; diff --git a/drivers/dma/ioat/registers.h b/drivers/dma/ioat/registers.h index 2f3bbc88ff2a..99c1c24d465d 100644 --- a/drivers/dma/ioat/registers.h +++ b/drivers/dma/ioat/registers.h @@ -84,6 +84,9 @@ #define IOAT_CAP_PQ 0x00000200 #define IOAT_CAP_DWBES 0x00002000 #define IOAT_CAP_RAID16SS 0x00020000 +#define IOAT_CAP_DPS 0x00800000 + +#define IOAT_PREFETCH_LIMIT_OFFSET 0x4C /* CHWPREFLMT */ #define IOAT_CHANNEL_MMIO_SIZE 0x80 /* Each Channel MMIO space is this size */ @@ -243,4 +246,25 @@ #define IOAT_CHANERR_MASK_OFFSET 0x2C /* 32-bit Channel Error Register */ +#define IOAT_CHAN_DRSCTL_OFFSET 0xB6 +#define IOAT_CHAN_DRSZ_4KB 0x0000 +#define IOAT_CHAN_DRSZ_8KB 0x0001 +#define IOAT_CHAN_DRSZ_2MB 0x0009 +#define IOAT_CHAN_DRS_EN 0x0100 +#define IOAT_CHAN_DRS_AUTOWRAP 0x0200 + +#define IOAT_CHAN_LTR_SWSEL_OFFSET 0xBC +#define IOAT_CHAN_LTR_SWSEL_ACTIVE 0x0 +#define IOAT_CHAN_LTR_SWSEL_IDLE 0x1 + +#define IOAT_CHAN_LTR_ACTIVE_OFFSET 0xC0 +#define IOAT_CHAN_LTR_ACTIVE_SNVAL 0x0000 /* 0 us */ +#define IOAT_CHAN_LTR_ACTIVE_SNLATSCALE 0x0800 /* 1us scale */ +#define IOAT_CHAN_LTR_ACTIVE_SNREQMNT 0x8000 /* snoop req enable */ + +#define IOAT_CHAN_LTR_IDLE_OFFSET 0xC4 +#define IOAT_CHAN_LTR_IDLE_SNVAL 0x0258 /* 600 us */ +#define IOAT_CHAN_LTR_IDLE_SNLATSCALE 0x0800 /* 1us scale */ +#define IOAT_CHAN_LTR_IDLE_SNREQMNT 0x8000 /* snoop req enable */ + #endif /* _IOAT_REGISTERS_H_ */ diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index fdec2b6cfbb0..5737d92eaeeb 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -52,8 +52,6 @@ #define CX_SRC 0x814 #define CX_DST 0x818 #define CX_CFG 0x81c -#define AXI_CFG 0x820 -#define AXI_CFG_DEFAULT 0x201201 #define CX_LLI_CHAIN_EN 0x2 #define CX_CFG_EN 0x1 @@ -113,9 +111,18 @@ struct k3_dma_dev { struct dma_pool *pool; u32 dma_channels; u32 dma_requests; + u32 dma_channel_mask; unsigned int irq; }; + +#define K3_FLAG_NOCLK BIT(1) + +struct k3dma_soc_data { + unsigned long flags; +}; + + #define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave) static int k3_dma_config_write(struct dma_chan *chan, @@ -161,7 +168,6 @@ static void k3_dma_set_desc(struct k3_dma_phy *phy, struct k3_desc_hw *hw) writel_relaxed(hw->count, phy->base + CX_CNT0); writel_relaxed(hw->saddr, phy->base + CX_SRC); writel_relaxed(hw->daddr, phy->base + CX_DST); - writel_relaxed(AXI_CFG_DEFAULT, phy->base + AXI_CFG); writel_relaxed(hw->config, phy->base + CX_CFG); } @@ -314,6 +320,9 @@ static void k3_dma_tasklet(unsigned long arg) /* check new channel request in d->chan_pending */ spin_lock_irq(&d->lock); for (pch = 0; pch < d->dma_channels; pch++) { + if (!(d->dma_channel_mask & (1 << pch))) + continue; + p = &d->phy[pch]; if (p->vchan == NULL && !list_empty(&d->chan_pending)) { @@ -331,6 +340,9 @@ static void k3_dma_tasklet(unsigned long arg) spin_unlock_irq(&d->lock); for (pch = 0; pch < d->dma_channels; pch++) { + if (!(d->dma_channel_mask & (1 << pch))) + continue; + if (pch_alloc & (1 << pch)) { p = &d->phy[pch]; c = p->vchan; @@ -790,8 +802,21 @@ static int k3_dma_transfer_resume(struct dma_chan *chan) return 0; } +static const struct k3dma_soc_data k3_v1_dma_data = { + .flags = 0, +}; + +static const struct k3dma_soc_data asp_v1_dma_data = { + .flags = K3_FLAG_NOCLK, +}; + static const struct of_device_id k3_pdma_dt_ids[] = { - { .compatible = "hisilicon,k3-dma-1.0", }, + { .compatible = "hisilicon,k3-dma-1.0", + .data = &k3_v1_dma_data + }, + { .compatible = "hisilicon,hisi-pcm-asp-dma-1.0", + .data = &asp_v1_dma_data + }, {} }; MODULE_DEVICE_TABLE(of, k3_pdma_dt_ids); @@ -810,6 +835,7 @@ static struct dma_chan *k3_of_dma_simple_xlate(struct of_phandle_args *dma_spec, static int k3_dma_probe(struct platform_device *op) { + const struct k3dma_soc_data *soc_data; struct k3_dma_dev *d; const struct of_device_id *of_id; struct resource *iores; @@ -823,6 +849,10 @@ static int k3_dma_probe(struct platform_device *op) if (!d) return -ENOMEM; + soc_data = device_get_match_data(&op->dev); + if (!soc_data) + return -EINVAL; + d->base = devm_ioremap_resource(&op->dev, iores); if (IS_ERR(d->base)) return PTR_ERR(d->base); @@ -833,12 +863,21 @@ static int k3_dma_probe(struct platform_device *op) "dma-channels", &d->dma_channels); of_property_read_u32((&op->dev)->of_node, "dma-requests", &d->dma_requests); + ret = of_property_read_u32((&op->dev)->of_node, + "dma-channel-mask", &d->dma_channel_mask); + if (ret) { + dev_warn(&op->dev, + "dma-channel-mask doesn't exist, considering all as available.\n"); + d->dma_channel_mask = (u32)~0UL; + } } - d->clk = devm_clk_get(&op->dev, NULL); - if (IS_ERR(d->clk)) { - dev_err(&op->dev, "no dma clk\n"); - return PTR_ERR(d->clk); + if (!(soc_data->flags & K3_FLAG_NOCLK)) { + d->clk = devm_clk_get(&op->dev, NULL); + if (IS_ERR(d->clk)) { + dev_err(&op->dev, "no dma clk\n"); + return PTR_ERR(d->clk); + } } irq = platform_get_irq(op, 0); @@ -862,8 +901,12 @@ static int k3_dma_probe(struct platform_device *op) return -ENOMEM; for (i = 0; i < d->dma_channels; i++) { - struct k3_dma_phy *p = &d->phy[i]; + struct k3_dma_phy *p; + + if (!(d->dma_channel_mask & BIT(i))) + continue; + p = &d->phy[i]; p->idx = i; p->base = d->base + i * 0x40; } diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c index 5de1b07eddff..7de54b2fafdb 100644 --- a/drivers/dma/mcf-edma.c +++ b/drivers/dma/mcf-edma.c @@ -214,6 +214,7 @@ static int mcf_edma_probe(struct platform_device *pdev) mcf_chan->edma = mcf_edma; mcf_chan->slave_id = i; mcf_chan->idle = true; + mcf_chan->dma_dir = DMA_NONE; mcf_chan->vchan.desc_free = fsl_edma_free_desc; vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev); iowrite32(0x0, ®s->tcd[i].csr); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 7f595355fb79..65af2e7fcb2c 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1059,6 +1059,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev, mv_chan->op_in_desc = XOR_MODE_IN_DESC; dma_dev = &mv_chan->dmadev; + dma_dev->dev = &pdev->dev; mv_chan->xordev = xordev; /* @@ -1091,7 +1092,6 @@ mv_xor_channel_add(struct mv_xor_device *xordev, dma_dev->device_free_chan_resources = mv_xor_free_chan_resources; dma_dev->device_tx_status = mv_xor_status; dma_dev->device_issue_pending = mv_xor_issue_pending; - dma_dev->dev = &pdev->dev; /* set prep routines based on capability */ if (dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask)) @@ -1153,7 +1153,10 @@ mv_xor_channel_add(struct mv_xor_device *xordev, dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); - dma_async_device_register(dma_dev); + ret = dma_async_device_register(dma_dev); + if (ret) + goto err_free_irq; + return mv_chan; err_free_irq: diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index cff1b143fff5..eec79fdf27a5 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2267,7 +2267,6 @@ static int pl330_terminate_all(struct dma_chan *chan) struct dma_pl330_desc *desc; unsigned long flags; struct pl330_dmac *pl330 = pch->dmac; - LIST_HEAD(list); bool power_down = false; pm_runtime_get_sync(pl330->ddma.dev); diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index 1617715aa6e0..cb860cb53c27 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -636,8 +636,8 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_FIFO_SIZE); /* allocate enough room to accomodate the number of entries */ - async_desc = kzalloc(sizeof(*async_desc) + - (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT); + async_desc = kzalloc(struct_size(async_desc, desc, num_alloc), + GFP_NOWAIT); if (!async_desc) goto err_out; diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 43d4b00b8138..411f91fde734 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -138,24 +138,25 @@ static void hidma_process_completed(struct hidma_chan *mchan) desc = &mdesc->desc; last_cookie = desc->cookie; + llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch); + spin_lock_irqsave(&mchan->lock, irqflags); + if (llstat == DMA_COMPLETE) { + mchan->last_success = last_cookie; + result.result = DMA_TRANS_NOERROR; + } else { + result.result = DMA_TRANS_ABORTED; + } + dma_cookie_complete(desc); spin_unlock_irqrestore(&mchan->lock, irqflags); - llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch); dmaengine_desc_get_callback(desc, &cb); dma_run_dependencies(desc); spin_lock_irqsave(&mchan->lock, irqflags); list_move(&mdesc->node, &mchan->free); - - if (llstat == DMA_COMPLETE) { - mchan->last_success = last_cookie; - result.result = DMA_TRANS_NOERROR; - } else - result.result = DMA_TRANS_ABORTED; - spin_unlock_irqrestore(&mchan->lock, irqflags); dmaengine_desc_callback_invoke(&cb, &result); @@ -415,6 +416,7 @@ hidma_prep_dma_memcpy(struct dma_chan *dmach, dma_addr_t dest, dma_addr_t src, if (!mdesc) return NULL; + mdesc->desc.flags = flags; hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, src, dest, len, flags, HIDMA_TRE_MEMCPY); @@ -447,6 +449,7 @@ hidma_prep_dma_memset(struct dma_chan *dmach, dma_addr_t dest, int value, if (!mdesc) return NULL; + mdesc->desc.flags = flags; hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, value, dest, len, flags, HIDMA_TRE_MEMSET); diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c index d64edeb6771a..681de12f4c67 100644 --- a/drivers/dma/qcom/hidma_mgmt.c +++ b/drivers/dma/qcom/hidma_mgmt.c @@ -423,9 +423,8 @@ static int __init hidma_mgmt_init(void) hidma_mgmt_of_populate_channels(child); } #endif - platform_driver_register(&hidma_mgmt_driver); + return platform_driver_register(&hidma_mgmt_driver); - return 0; } module_init(hidma_mgmt_init); MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index 784d5f1a473b..3fae23768b47 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -705,7 +705,6 @@ static int sa11x0_dma_device_pause(struct dma_chan *chan) struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; - LIST_HEAD(head); unsigned long flags; dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); @@ -732,7 +731,6 @@ static int sa11x0_dma_device_resume(struct dma_chan *chan) struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; - LIST_HEAD(head); unsigned long flags; dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index 7f7184c3cf95..59403f6d008a 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev) #endif /* CONFIG_PM */ static const struct dev_pm_ops usb_dmac_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume, NULL) }; diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index e2f016700fcc..48431e2da987 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -580,15 +580,7 @@ static irqreturn_t dma_irq_handle(int irq, void *dev_id) static int sprd_dma_alloc_chan_resources(struct dma_chan *chan) { - struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); - int ret; - - ret = pm_runtime_get_sync(chan->device->dev); - if (ret < 0) - return ret; - - schan->dev_id = SPRD_DMA_SOFTWARE_UID; - return 0; + return pm_runtime_get_sync(chan->device->dev); } static void sprd_dma_free_chan_resources(struct dma_chan *chan) @@ -1021,13 +1013,10 @@ static void sprd_dma_free_desc(struct virt_dma_desc *vd) static bool sprd_dma_filter_fn(struct dma_chan *chan, void *param) { struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); - struct sprd_dma_dev *sdev = to_sprd_dma_dev(&schan->vc.chan); - u32 req = *(u32 *)param; + u32 slave_id = *(u32 *)param; - if (req < sdev->total_chns) - return req == schan->chn_num + 1; - else - return false; + schan->dev_id = slave_id; + return true; } static int sprd_dma_probe(struct platform_device *pdev) diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c index 07c20aa2e955..bc7a1de3f29b 100644 --- a/drivers/dma/st_fdma.c +++ b/drivers/dma/st_fdma.c @@ -243,8 +243,7 @@ static struct st_fdma_desc *st_fdma_alloc_desc(struct st_fdma_chan *fchan, struct st_fdma_desc *fdesc; int i; - fdesc = kzalloc(sizeof(*fdesc) + - sizeof(struct st_fdma_sw_node) * sg_len, GFP_NOWAIT); + fdesc = kzalloc(struct_size(fdesc, node, sg_len), GFP_NOWAIT); if (!fdesc) return NULL; @@ -294,8 +293,6 @@ static void st_fdma_free_chan_res(struct dma_chan *chan) struct rproc *rproc = fchan->fdev->slim_rproc->rproc; unsigned long flags; - LIST_HEAD(head); - dev_dbg(fchan->fdev->dev, "%s: freeing chan:%d\n", __func__, fchan->vchan.chan.chan_id); @@ -626,7 +623,6 @@ static void st_fdma_issue_pending(struct dma_chan *chan) static int st_fdma_pause(struct dma_chan *chan) { unsigned long flags; - LIST_HEAD(head); struct st_fdma_chan *fchan = to_st_fdma_chan(chan); int ch_id = fchan->vchan.chan.chan_id; unsigned long cmd = FDMA_CMD_PAUSE(ch_id); diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 4903a408fc14..ba239b529fa9 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -23,6 +23,7 @@ #include <linux/of_device.h> #include <linux/of_dma.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/sched.h> #include <linux/slab.h> @@ -641,12 +642,13 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid) { struct stm32_dma_chan *chan = devid; struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); - u32 status, scr; + u32 status, scr, sfcr; spin_lock(&chan->vchan.lock); status = stm32_dma_irq_status(chan); scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id)); + sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id)); if (status & STM32_DMA_TCI) { stm32_dma_irq_clear(chan, STM32_DMA_TCI); @@ -661,10 +663,12 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid) if (status & STM32_DMA_FEI) { stm32_dma_irq_clear(chan, STM32_DMA_FEI); status &= ~STM32_DMA_FEI; - if (!(scr & STM32_DMA_SCR_EN)) - dev_err(chan2dev(chan), "FIFO Error\n"); - else - dev_dbg(chan2dev(chan), "FIFO over/underrun\n"); + if (sfcr & STM32_DMA_SFCR_FEIE) { + if (!(scr & STM32_DMA_SCR_EN)) + dev_err(chan2dev(chan), "FIFO Error\n"); + else + dev_dbg(chan2dev(chan), "FIFO over/underrun\n"); + } } if (status) { stm32_dma_irq_clear(chan, status); @@ -1112,15 +1116,14 @@ static int stm32_dma_alloc_chan_resources(struct dma_chan *c) int ret; chan->config_init = false; - ret = clk_prepare_enable(dmadev->clk); - if (ret < 0) { - dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret); + + ret = pm_runtime_get_sync(dmadev->ddev.dev); + if (ret < 0) return ret; - } ret = stm32_dma_disable_chan(chan); if (ret < 0) - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); return ret; } @@ -1140,7 +1143,7 @@ static void stm32_dma_free_chan_resources(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); vchan_free_chan_resources(to_virt_chan(c)); } @@ -1240,6 +1243,12 @@ static int stm32_dma_probe(struct platform_device *pdev) return PTR_ERR(dmadev->clk); } + ret = clk_prepare_enable(dmadev->clk); + if (ret < 0) { + dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); + return ret; + } + dmadev->mem2mem = of_property_read_bool(pdev->dev.of_node, "st,mem2mem"); @@ -1289,7 +1298,7 @@ static int stm32_dma_probe(struct platform_device *pdev) ret = dma_async_device_register(dd); if (ret) - return ret; + goto clk_free; for (i = 0; i < STM32_DMA_MAX_CHANNELS; i++) { chan = &dmadev->chan[i]; @@ -1321,20 +1330,58 @@ static int stm32_dma_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dmadev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_put(&pdev->dev); + dev_info(&pdev->dev, "STM32 DMA driver registered\n"); return 0; err_unregister: dma_async_device_unregister(dd); +clk_free: + clk_disable_unprepare(dmadev->clk); return ret; } +#ifdef CONFIG_PM +static int stm32_dma_runtime_suspend(struct device *dev) +{ + struct stm32_dma_device *dmadev = dev_get_drvdata(dev); + + clk_disable_unprepare(dmadev->clk); + + return 0; +} + +static int stm32_dma_runtime_resume(struct device *dev) +{ + struct stm32_dma_device *dmadev = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dmadev->clk); + if (ret) { + dev_err(dev, "failed to prepare_enable clock\n"); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops stm32_dma_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_dma_runtime_suspend, + stm32_dma_runtime_resume, NULL) +}; + static struct platform_driver stm32_dma_driver = { .driver = { .name = "stm32-dma", .of_match_table = stm32_dma_of_match, + .pm = &stm32_dma_pm_ops, }, }; diff --git a/drivers/dma/stm32-dmamux.c b/drivers/dma/stm32-dmamux.c index b922db90939a..a67119199c45 100644 --- a/drivers/dma/stm32-dmamux.c +++ b/drivers/dma/stm32-dmamux.c @@ -28,6 +28,7 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/of_dma.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -79,8 +80,7 @@ static void stm32_dmamux_free(struct device *dev, void *route_data) stm32_dmamux_write(dmamux->iomem, STM32_DMAMUX_CCR(mux->chan_id), 0); clear_bit(mux->chan_id, dmamux->dma_inuse); - if (!IS_ERR(dmamux->clk)) - clk_disable(dmamux->clk); + pm_runtime_put_sync(dev); spin_unlock_irqrestore(&dmamux->lock, flags); @@ -146,13 +146,10 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec, /* Set dma request */ spin_lock_irqsave(&dmamux->lock, flags); - if (!IS_ERR(dmamux->clk)) { - ret = clk_enable(dmamux->clk); - if (ret < 0) { - spin_unlock_irqrestore(&dmamux->lock, flags); - dev_err(&pdev->dev, "clk_prep_enable issue: %d\n", ret); - goto error; - } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + spin_unlock_irqrestore(&dmamux->lock, flags); + goto error; } spin_unlock_irqrestore(&dmamux->lock, flags); @@ -254,6 +251,7 @@ static int stm32_dmamux_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "DMAMUX defaulting on %u requests\n", stm32_dmamux->dmamux_requests); } + pm_runtime_get_noresume(&pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iomem = devm_ioremap_resource(&pdev->dev, res); @@ -282,6 +280,8 @@ static int stm32_dmamux_probe(struct platform_device *pdev) stm32_dmamux->dmarouter.route_free = stm32_dmamux_free; platform_set_drvdata(pdev, stm32_dmamux); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); if (!IS_ERR(stm32_dmamux->clk)) { ret = clk_prepare_enable(stm32_dmamux->clk); @@ -291,17 +291,52 @@ static int stm32_dmamux_probe(struct platform_device *pdev) } } + pm_runtime_get_noresume(&pdev->dev); + /* Reset the dmamux */ for (i = 0; i < stm32_dmamux->dma_requests; i++) stm32_dmamux_write(stm32_dmamux->iomem, STM32_DMAMUX_CCR(i), 0); - if (!IS_ERR(stm32_dmamux->clk)) - clk_disable(stm32_dmamux->clk); + pm_runtime_put(&pdev->dev); return of_dma_router_register(node, stm32_dmamux_route_allocate, &stm32_dmamux->dmarouter); } +#ifdef CONFIG_PM +static int stm32_dmamux_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); + + clk_disable_unprepare(stm32_dmamux->clk); + + return 0; +} + +static int stm32_dmamux_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); + int ret; + + ret = clk_prepare_enable(stm32_dmamux->clk); + if (ret) { + dev_err(&pdev->dev, "failed to prepare_enable clock\n"); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops stm32_dmamux_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_dmamux_runtime_suspend, + stm32_dmamux_runtime_resume, NULL) +}; + static const struct of_device_id stm32_dmamux_match[] = { { .compatible = "st,stm32h7-dmamux" }, {}, @@ -312,6 +347,7 @@ static struct platform_driver stm32_dmamux_driver = { .driver = { .name = "stm32-dmamux", .of_match_table = stm32_dmamux_match, + .pm = &stm32_dmamux_pm_ops, }, }; diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index 390e4cae0e1a..4e0eede599a8 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -37,6 +37,7 @@ #include <linux/of_device.h> #include <linux/of_dma.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/slab.h> @@ -1456,15 +1457,13 @@ static int stm32_mdma_alloc_chan_resources(struct dma_chan *c) return -ENOMEM; } - ret = clk_prepare_enable(dmadev->clk); - if (ret < 0) { - dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret); + ret = pm_runtime_get_sync(dmadev->ddev.dev); + if (ret < 0) return ret; - } ret = stm32_mdma_disable_chan(chan); if (ret < 0) - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); return ret; } @@ -1484,7 +1483,7 @@ static void stm32_mdma_free_chan_resources(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); vchan_free_chan_resources(to_virt_chan(c)); dmam_pool_destroy(chan->desc_pool); chan->desc_pool = NULL; @@ -1579,9 +1578,11 @@ static int stm32_mdma_probe(struct platform_device *pdev) dmadev->nr_channels = nr_channels; dmadev->nr_requests = nr_requests; - device_property_read_u32_array(&pdev->dev, "st,ahb-addr-masks", + ret = device_property_read_u32_array(&pdev->dev, "st,ahb-addr-masks", dmadev->ahb_addr_masks, count); + if (ret) + return ret; dmadev->nr_ahb_addr_masks = count; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1597,6 +1598,12 @@ static int stm32_mdma_probe(struct platform_device *pdev) return ret; } + ret = clk_prepare_enable(dmadev->clk); + if (ret < 0) { + dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); + return ret; + } + dmadev->rst = devm_reset_control_get(&pdev->dev, NULL); if (!IS_ERR(dmadev->rst)) { reset_control_assert(dmadev->rst); @@ -1668,6 +1675,10 @@ static int stm32_mdma_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, dmadev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_put(&pdev->dev); dev_info(&pdev->dev, "STM32 MDMA driver registered\n"); @@ -1677,11 +1688,42 @@ err_unregister: return ret; } +#ifdef CONFIG_PM +static int stm32_mdma_runtime_suspend(struct device *dev) +{ + struct stm32_mdma_device *dmadev = dev_get_drvdata(dev); + + clk_disable_unprepare(dmadev->clk); + + return 0; +} + +static int stm32_mdma_runtime_resume(struct device *dev) +{ + struct stm32_mdma_device *dmadev = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dmadev->clk); + if (ret) { + dev_err(dev, "failed to prepare_enable clock\n"); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops stm32_mdma_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_mdma_runtime_suspend, + stm32_mdma_runtime_resume, NULL) +}; + static struct platform_driver stm32_mdma_driver = { .probe = stm32_mdma_probe, .driver = { .name = "stm32-mdma", .of_match_table = stm32_mdma_of_match, + .pm = &stm32_mdma_pm_ops, }, }; diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 9a558e30c461..cf462b1abc0b 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -38,6 +38,9 @@ #include "dmaengine.h" +#define CREATE_TRACE_POINTS +#include <trace/events/tegra_apb_dma.h> + #define TEGRA_APBDMA_GENERAL 0x0 #define TEGRA_APBDMA_GENERAL_ENABLE BIT(31) @@ -146,7 +149,7 @@ struct tegra_dma_channel_regs { }; /* - * tegra_dma_sg_req: Dma request details to configure hardware. This + * tegra_dma_sg_req: DMA request details to configure hardware. This * contains the details for one transfer to configure DMA hw. * The client's request for data transfer can be broken into multiple * sub-transfer as per requester details and hw support. @@ -155,7 +158,7 @@ struct tegra_dma_channel_regs { */ struct tegra_dma_sg_req { struct tegra_dma_channel_regs ch_regs; - int req_len; + unsigned int req_len; bool configured; bool last_sg; struct list_head node; @@ -169,8 +172,8 @@ struct tegra_dma_sg_req { */ struct tegra_dma_desc { struct dma_async_tx_descriptor txd; - int bytes_requested; - int bytes_transferred; + unsigned int bytes_requested; + unsigned int bytes_transferred; enum dma_status dma_status; struct list_head node; struct list_head tx_list; @@ -186,7 +189,7 @@ typedef void (*dma_isr_handler)(struct tegra_dma_channel *tdc, /* tegra_dma_channel: Channel specific information */ struct tegra_dma_channel { struct dma_chan dma_chan; - char name[30]; + char name[12]; bool config_init; int id; int irq; @@ -574,7 +577,7 @@ static bool handle_continuous_head_request(struct tegra_dma_channel *tdc, struct tegra_dma_sg_req *hsgreq = NULL; if (list_empty(&tdc->pending_sg_req)) { - dev_err(tdc2dev(tdc), "Dma is running without req\n"); + dev_err(tdc2dev(tdc), "DMA is running without req\n"); tegra_dma_stop(tdc); return false; } @@ -587,7 +590,7 @@ static bool handle_continuous_head_request(struct tegra_dma_channel *tdc, hsgreq = list_first_entry(&tdc->pending_sg_req, typeof(*hsgreq), node); if (!hsgreq->configured) { tegra_dma_stop(tdc); - dev_err(tdc2dev(tdc), "Error in dma transfer, aborting dma\n"); + dev_err(tdc2dev(tdc), "Error in DMA transfer, aborting DMA\n"); tegra_dma_abort_all(tdc); return false; } @@ -636,7 +639,10 @@ static void handle_cont_sngl_cycle_dma_done(struct tegra_dma_channel *tdc, sgreq = list_first_entry(&tdc->pending_sg_req, typeof(*sgreq), node); dma_desc = sgreq->dma_desc; - dma_desc->bytes_transferred += sgreq->req_len; + /* if we dma for long enough the transfer count will wrap */ + dma_desc->bytes_transferred = + (dma_desc->bytes_transferred + sgreq->req_len) % + dma_desc->bytes_requested; /* Callback need to be call */ if (!dma_desc->cb_count) @@ -669,6 +675,8 @@ static void tegra_dma_tasklet(unsigned long data) dmaengine_desc_get_callback(&dma_desc->txd, &cb); cb_count = dma_desc->cb_count; dma_desc->cb_count = 0; + trace_tegra_dma_complete_cb(&tdc->dma_chan, cb_count, + cb.callback); spin_unlock_irqrestore(&tdc->lock, flags); while (cb_count--) dmaengine_desc_callback_invoke(&cb, NULL); @@ -685,6 +693,7 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id) spin_lock_irqsave(&tdc->lock, flags); + trace_tegra_dma_isr(&tdc->dma_chan, irq); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status); @@ -843,6 +852,7 @@ found: dma_set_residue(txstate, residual); } + trace_tegra_dma_tx_status(&tdc->dma_chan, cookie, txstate); spin_unlock_irqrestore(&tdc->lock, flags); return ret; } @@ -919,7 +929,7 @@ static int get_transfer_param(struct tegra_dma_channel *tdc, return 0; default: - dev_err(tdc2dev(tdc), "Dma direction is not supported\n"); + dev_err(tdc2dev(tdc), "DMA direction is not supported\n"); return -EINVAL; } return -EINVAL; @@ -952,7 +962,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( enum dma_slave_buswidth slave_bw; if (!tdc->config_init) { - dev_err(tdc2dev(tdc), "dma channel is not configured\n"); + dev_err(tdc2dev(tdc), "DMA channel is not configured\n"); return NULL; } if (sg_len < 1) { @@ -985,7 +995,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( dma_desc = tegra_dma_desc_get(tdc); if (!dma_desc) { - dev_err(tdc2dev(tdc), "Dma descriptors not available\n"); + dev_err(tdc2dev(tdc), "DMA descriptors not available\n"); return NULL; } INIT_LIST_HEAD(&dma_desc->tx_list); @@ -1005,14 +1015,14 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( if ((len & 3) || (mem & 3) || (len > tdc->tdma->chip_data->max_dma_count)) { dev_err(tdc2dev(tdc), - "Dma length/memory address is not supported\n"); + "DMA length/memory address is not supported\n"); tegra_dma_desc_put(tdc, dma_desc); return NULL; } sg_req = tegra_dma_sg_req_get(tdc); if (!sg_req) { - dev_err(tdc2dev(tdc), "Dma sg-req not available\n"); + dev_err(tdc2dev(tdc), "DMA sg-req not available\n"); tegra_dma_desc_put(tdc, dma_desc); return NULL; } @@ -1087,7 +1097,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( * terminating the DMA. */ if (tdc->busy) { - dev_err(tdc2dev(tdc), "Request not allowed when dma running\n"); + dev_err(tdc2dev(tdc), "Request not allowed when DMA running\n"); return NULL; } @@ -1144,7 +1154,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( while (remain_len) { sg_req = tegra_dma_sg_req_get(tdc); if (!sg_req) { - dev_err(tdc2dev(tdc), "Dma sg-req not available\n"); + dev_err(tdc2dev(tdc), "DMA sg-req not available\n"); tegra_dma_desc_put(tdc, dma_desc); return NULL; } @@ -1319,8 +1329,9 @@ static int tegra_dma_probe(struct platform_device *pdev) return -ENODEV; } - tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels * - sizeof(struct tegra_dma_channel), GFP_KERNEL); + tdma = devm_kzalloc(&pdev->dev, + struct_size(tdma, channels, cdata->nr_channels), + GFP_KERNEL); if (!tdma) return -ENOMEM; diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index b26256f23d67..5ec0dd97b397 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -678,8 +678,9 @@ static int tegra_adma_probe(struct platform_device *pdev) return -ENODEV; } - tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels * - sizeof(struct tegra_adma_chan), GFP_KERNEL); + tdma = devm_kzalloc(&pdev->dev, + struct_size(tdma, channels, cdata->nr_channels), + GFP_KERNEL); if (!tdma) return -ENOMEM; diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index fc0f9c8766a8..afbb1c95b721 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -643,8 +643,8 @@ static int td_probe(struct platform_device *pdev) DRIVER_NAME)) return -EBUSY; - td = kzalloc(sizeof(struct timb_dma) + - sizeof(struct timb_dma_chan) * pdata->nr_channels, GFP_KERNEL); + td = kzalloc(struct_size(td, channels, pdata->nr_channels), + GFP_KERNEL); if (!td) { err = -ENOMEM; goto err_release_region; diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index cb20b411493e..c43c1a154604 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -86,6 +86,7 @@ #define XILINX_DMA_DMASR_DMA_DEC_ERR BIT(6) #define XILINX_DMA_DMASR_DMA_SLAVE_ERR BIT(5) #define XILINX_DMA_DMASR_DMA_INT_ERR BIT(4) +#define XILINX_DMA_DMASR_SG_MASK BIT(3) #define XILINX_DMA_DMASR_IDLE BIT(1) #define XILINX_DMA_DMASR_HALTED BIT(0) #define XILINX_DMA_DMASR_DELAY_MASK GENMASK(31, 24) @@ -161,7 +162,9 @@ #define XILINX_DMA_REG_BTT 0x28 /* AXI DMA Specific Masks/Bit fields */ -#define XILINX_DMA_MAX_TRANS_LEN GENMASK(22, 0) +#define XILINX_DMA_MAX_TRANS_LEN_MIN 8 +#define XILINX_DMA_MAX_TRANS_LEN_MAX 23 +#define XILINX_DMA_V2_MAX_TRANS_LEN_MAX 26 #define XILINX_DMA_CR_COALESCE_MAX GENMASK(23, 16) #define XILINX_DMA_CR_CYCLIC_BD_EN_MASK BIT(4) #define XILINX_DMA_CR_COALESCE_SHIFT 16 @@ -412,7 +415,6 @@ struct xilinx_dma_config { * @dev: Device Structure * @common: DMA device structure * @chan: Driver specific DMA channel - * @has_sg: Specifies whether Scatter-Gather is present or not * @mcdma: Specifies whether Multi-Channel is present or not * @flush_on_fsync: Flush on frame sync * @ext_addr: Indicates 64 bit addressing is supported by dma device @@ -425,13 +427,13 @@ struct xilinx_dma_config { * @rxs_clk: DMA s2mm stream clock * @nr_channels: Number of channels DMA device supports * @chan_id: DMA channel identifier + * @max_buffer_len: Max buffer length */ struct xilinx_dma_device { void __iomem *regs; struct device *dev; struct dma_device common; struct xilinx_dma_chan *chan[XILINX_DMA_MAX_CHANS_PER_DEVICE]; - bool has_sg; bool mcdma; u32 flush_on_fsync; bool ext_addr; @@ -444,6 +446,7 @@ struct xilinx_dma_device { struct clk *rxs_clk; u32 nr_channels; u32 chan_id; + u32 max_buffer_len; }; /* Macros */ @@ -960,6 +963,34 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan) } /** + * xilinx_dma_calc_copysize - Calculate the amount of data to copy + * @chan: Driver specific DMA channel + * @size: Total data that needs to be copied + * @done: Amount of data that has been already copied + * + * Return: Amount of data that has to be copied + */ +static int xilinx_dma_calc_copysize(struct xilinx_dma_chan *chan, + int size, int done) +{ + size_t copy; + + copy = min_t(size_t, size - done, + chan->xdev->max_buffer_len); + + if ((copy + done < size) && + chan->xdev->common.copy_align) { + /* + * If this is not the last descriptor, make sure + * the next one will be properly aligned + */ + copy = rounddown(copy, + (1 << chan->xdev->common.copy_align)); + } + return copy; +} + +/** * xilinx_dma_tx_status - Get DMA transaction status * @dchan: DMA channel * @cookie: Transaction identifier @@ -992,7 +1023,7 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan, list_for_each_entry(segment, &desc->segments, node) { hw = &segment->hw; residue += (hw->control - hw->status) & - XILINX_DMA_MAX_TRANS_LEN; + chan->xdev->max_buffer_len; } } spin_unlock_irqrestore(&chan->lock, flags); @@ -1070,7 +1101,8 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) struct xilinx_vdma_config *config = &chan->config; struct xilinx_dma_tx_descriptor *desc, *tail_desc; u32 reg, j; - struct xilinx_vdma_tx_segment *tail_segment; + struct xilinx_vdma_tx_segment *segment, *last = NULL; + int i = 0; /* This function was invoked with lock held */ if (chan->err) @@ -1087,17 +1119,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) tail_desc = list_last_entry(&chan->pending_list, struct xilinx_dma_tx_descriptor, node); - tail_segment = list_last_entry(&tail_desc->segments, - struct xilinx_vdma_tx_segment, node); - - /* - * If hardware is idle, then all descriptors on the running lists are - * done, start new transfers - */ - if (chan->has_sg) - dma_ctrl_write(chan, XILINX_DMA_REG_CURDESC, - desc->async_tx.phys); - /* Configure the hardware using info in the config structure */ if (chan->has_vflip) { reg = dma_read(chan, XILINX_VDMA_REG_ENABLE_VERTICAL_FLIP); @@ -1114,15 +1135,11 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) else reg &= ~XILINX_DMA_DMACR_FRAMECNT_EN; - /* - * With SG, start with circular mode, so that BDs can be fetched. - * In direct register mode, if not parking, enable circular mode - */ - if (chan->has_sg || !config->park) - reg |= XILINX_DMA_DMACR_CIRC_EN; - + /* If not parking, enable circular mode */ if (config->park) reg &= ~XILINX_DMA_DMACR_CIRC_EN; + else + reg |= XILINX_DMA_DMACR_CIRC_EN; dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg); @@ -1144,48 +1161,38 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) return; /* Start the transfer */ - if (chan->has_sg) { - dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC, - tail_segment->phys); - list_splice_tail_init(&chan->pending_list, &chan->active_list); - chan->desc_pendingcount = 0; - } else { - struct xilinx_vdma_tx_segment *segment, *last = NULL; - int i = 0; - - if (chan->desc_submitcount < chan->num_frms) - i = chan->desc_submitcount; - - list_for_each_entry(segment, &desc->segments, node) { - if (chan->ext_addr) - vdma_desc_write_64(chan, - XILINX_VDMA_REG_START_ADDRESS_64(i++), - segment->hw.buf_addr, - segment->hw.buf_addr_msb); - else - vdma_desc_write(chan, + if (chan->desc_submitcount < chan->num_frms) + i = chan->desc_submitcount; + + list_for_each_entry(segment, &desc->segments, node) { + if (chan->ext_addr) + vdma_desc_write_64(chan, + XILINX_VDMA_REG_START_ADDRESS_64(i++), + segment->hw.buf_addr, + segment->hw.buf_addr_msb); + else + vdma_desc_write(chan, XILINX_VDMA_REG_START_ADDRESS(i++), segment->hw.buf_addr); - last = segment; - } + last = segment; + } - if (!last) - return; + if (!last) + return; - /* HW expects these parameters to be same for one transaction */ - vdma_desc_write(chan, XILINX_DMA_REG_HSIZE, last->hw.hsize); - vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE, - last->hw.stride); - vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize); + /* HW expects these parameters to be same for one transaction */ + vdma_desc_write(chan, XILINX_DMA_REG_HSIZE, last->hw.hsize); + vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE, + last->hw.stride); + vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize); - chan->desc_submitcount++; - chan->desc_pendingcount--; - list_del(&desc->node); - list_add_tail(&desc->node, &chan->active_list); - if (chan->desc_submitcount == chan->num_frms) - chan->desc_submitcount = 0; - } + chan->desc_submitcount++; + chan->desc_pendingcount--; + list_del(&desc->node); + list_add_tail(&desc->node, &chan->active_list); + if (chan->desc_submitcount == chan->num_frms) + chan->desc_submitcount = 0; chan->idle = false; } @@ -1254,7 +1261,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan) /* Start the transfer */ dma_ctrl_write(chan, XILINX_DMA_REG_BTT, - hw->control & XILINX_DMA_MAX_TRANS_LEN); + hw->control & chan->xdev->max_buffer_len); } list_splice_tail_init(&chan->pending_list, &chan->active_list); @@ -1357,7 +1364,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) /* Start the transfer */ dma_ctrl_write(chan, XILINX_DMA_REG_BTT, - hw->control & XILINX_DMA_MAX_TRANS_LEN); + hw->control & chan->xdev->max_buffer_len); } list_splice_tail_init(&chan->pending_list, &chan->active_list); @@ -1718,7 +1725,7 @@ xilinx_cdma_prep_memcpy(struct dma_chan *dchan, dma_addr_t dma_dst, struct xilinx_cdma_tx_segment *segment; struct xilinx_cdma_desc_hw *hw; - if (!len || len > XILINX_DMA_MAX_TRANS_LEN) + if (!len || len > chan->xdev->max_buffer_len) return NULL; desc = xilinx_dma_alloc_tx_descriptor(chan); @@ -1808,8 +1815,8 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg( * Calculate the maximum number of bytes to transfer, * making sure it is less than the hw limit */ - copy = min_t(size_t, sg_dma_len(sg) - sg_used, - XILINX_DMA_MAX_TRANS_LEN); + copy = xilinx_dma_calc_copysize(chan, sg_dma_len(sg), + sg_used); hw = &segment->hw; /* Fill in the descriptor */ @@ -1913,8 +1920,8 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_dma_cyclic( * Calculate the maximum number of bytes to transfer, * making sure it is less than the hw limit */ - copy = min_t(size_t, period_len - sg_used, - XILINX_DMA_MAX_TRANS_LEN); + copy = xilinx_dma_calc_copysize(chan, period_len, + sg_used); hw = &segment->hw; xilinx_axidma_buf(chan, hw, buf_addr, sg_used, period_len * i); @@ -2389,7 +2396,6 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, chan->dev = xdev->dev; chan->xdev = xdev; - chan->has_sg = xdev->has_sg; chan->desc_pendingcount = 0x0; chan->ext_addr = xdev->ext_addr; /* This variable ensures that descriptors are not @@ -2489,6 +2495,15 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, chan->stop_transfer = xilinx_dma_stop_transfer; } + /* check if SG is enabled (only for AXIDMA and CDMA) */ + if (xdev->dma_config->dmatype != XDMA_TYPE_VDMA) { + if (dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) & + XILINX_DMA_DMASR_SG_MASK) + chan->has_sg = true; + dev_dbg(chan->dev, "ch %d: SG %s\n", chan->id, + chan->has_sg ? "enabled" : "disabled"); + } + /* Initialize the tasklet */ tasklet_init(&chan->tasklet, xilinx_dma_do_tasklet, (unsigned long)chan); @@ -2596,7 +2611,7 @@ static int xilinx_dma_probe(struct platform_device *pdev) struct xilinx_dma_device *xdev; struct device_node *child, *np = pdev->dev.of_node; struct resource *io; - u32 num_frames, addr_width; + u32 num_frames, addr_width, len_width; int i, err; /* Allocate and initialize the DMA engine structure */ @@ -2627,9 +2642,24 @@ static int xilinx_dma_probe(struct platform_device *pdev) return PTR_ERR(xdev->regs); /* Retrieve the DMA engine properties from the device tree */ - xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg"); - if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) + xdev->max_buffer_len = GENMASK(XILINX_DMA_MAX_TRANS_LEN_MAX - 1, 0); + + if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { xdev->mcdma = of_property_read_bool(node, "xlnx,mcdma"); + if (!of_property_read_u32(node, "xlnx,sg-length-width", + &len_width)) { + if (len_width < XILINX_DMA_MAX_TRANS_LEN_MIN || + len_width > XILINX_DMA_V2_MAX_TRANS_LEN_MAX) { + dev_warn(xdev->dev, + "invalid xlnx,sg-length-width property value. Using default width\n"); + } else { + if (len_width > XILINX_DMA_MAX_TRANS_LEN_MAX) + dev_warn(xdev->dev, "Please ensure that IP supports buffer length > 23 bits\n"); + xdev->max_buffer_len = + GENMASK(len_width - 1, 0); + } + } + } if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) { err = of_property_read_u32(node, "xlnx,num-fstores", diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index ec4fd253a4e9..d168c87c7d30 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -333,7 +333,7 @@ int __init firmware_map_add_early(u64 start, u64 end, const char *type) { struct firmware_map_entry *entry; - entry = memblock_alloc_nopanic(sizeof(struct firmware_map_entry), + entry = memblock_alloc(sizeof(struct firmware_map_entry), SMP_CACHE_BYTES); if (WARN_ON(!entry)) return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index ead851413c0a..bfa9062ce6b9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -947,10 +947,6 @@ int amdgpu_vm_alloc_pts(struct amdgpu_device *adev, if (r) return r; - r = amdgpu_vm_clear_bo(adev, vm, pt, cursor.level, ats); - if (r) - goto error_free_pt; - if (vm->use_cpu_for_update) { r = amdgpu_bo_kmap(pt, NULL); if (r) @@ -963,6 +959,10 @@ int amdgpu_vm_alloc_pts(struct amdgpu_device *adev, pt->parent = amdgpu_bo_ref(cursor.parent->base.bo); amdgpu_vm_bo_base_init(&entry->base, vm, pt); + + r = amdgpu_vm_clear_bo(adev, vm, pt, cursor.level, ats); + if (r) + goto error_free_pt; } return 0; @@ -3033,13 +3033,14 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (r) goto error_unreserve; + amdgpu_vm_bo_base_init(&vm->root.base, vm, root); + r = amdgpu_vm_clear_bo(adev, vm, root, adev->vm_manager.root_level, vm->pte_support_ats); if (r) goto error_unreserve; - amdgpu_vm_bo_base_init(&vm->root.base, vm, root); amdgpu_bo_unreserve(vm->root.base.bo); if (pasid) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 5533f6e4f4a4..d0309e8c9d12 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -220,6 +220,7 @@ static const struct soc15_reg_golden golden_settings_gc_9_1_rv2[] = static const struct soc15_reg_golden golden_settings_gc_9_x_common[] = { + SOC15_REG_GOLDEN_VALUE(GC, 0, mmCP_SD_CNTL, 0xffffffff, 0x000001ff), SOC15_REG_GOLDEN_VALUE(GC, 0, mmGRBM_CAM_INDEX, 0xffffffff, 0x00000000), SOC15_REG_GOLDEN_VALUE(GC, 0, mmGRBM_CAM_DATA, 0xffffffff, 0x2544c382) }; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c b/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c index c63de945c021..0487e3a4e9e7 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c @@ -500,9 +500,7 @@ static bool psp_v3_1_smu_reload_quirk(struct psp_context *psp) struct amdgpu_device *adev = psp->adev; uint32_t reg; - reg = smnMP1_FIRMWARE_FLAGS | 0x03b00000; - WREG32_SOC15(NBIO, 0, mmPCIE_INDEX2, reg); - reg = RREG32_SOC15(NBIO, 0, mmPCIE_DATA2); + reg = RREG32_PCIE(smnMP1_FIRMWARE_FLAGS | 0x03b00000); return (reg & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) ? true : false; } diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index 99ebcf29dcb0..ed89a101f73f 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -461,7 +461,6 @@ static int soc15_asic_reset(struct amdgpu_device *adev) switch (adev->asic_type) { case CHIP_VEGA10: - case CHIP_VEGA20: soc15_asic_get_baco_capability(adev, &baco_reset); break; default: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c index 47243165a082..ae90a99909ef 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c @@ -323,57 +323,7 @@ static int init_mqd_hiq(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj **mqd_mem_obj, uint64_t *gart_addr, struct queue_properties *q) { - uint64_t addr; - struct cik_mqd *m; - int retval; - - retval = kfd_gtt_sa_allocate(mm->dev, sizeof(struct cik_mqd), - mqd_mem_obj); - - if (retval != 0) - return -ENOMEM; - - m = (struct cik_mqd *) (*mqd_mem_obj)->cpu_ptr; - addr = (*mqd_mem_obj)->gpu_addr; - - memset(m, 0, ALIGN(sizeof(struct cik_mqd), 256)); - - m->header = 0xC0310800; - m->compute_pipelinestat_enable = 1; - m->compute_static_thread_mgmt_se0 = 0xFFFFFFFF; - m->compute_static_thread_mgmt_se1 = 0xFFFFFFFF; - m->compute_static_thread_mgmt_se2 = 0xFFFFFFFF; - m->compute_static_thread_mgmt_se3 = 0xFFFFFFFF; - - m->cp_hqd_persistent_state = DEFAULT_CP_HQD_PERSISTENT_STATE | - PRELOAD_REQ; - m->cp_hqd_quantum = QUANTUM_EN | QUANTUM_SCALE_1MS | - QUANTUM_DURATION(10); - - m->cp_mqd_control = MQD_CONTROL_PRIV_STATE_EN; - m->cp_mqd_base_addr_lo = lower_32_bits(addr); - m->cp_mqd_base_addr_hi = upper_32_bits(addr); - - m->cp_hqd_ib_control = DEFAULT_MIN_IB_AVAIL_SIZE; - - /* - * Pipe Priority - * Identifies the pipe relative priority when this queue is connected - * to the pipeline. The pipe priority is against the GFX pipe and HP3D. - * In KFD we are using a fixed pipe priority set to CS_MEDIUM. - * 0 = CS_LOW (typically below GFX) - * 1 = CS_MEDIUM (typically between HP3D and GFX - * 2 = CS_HIGH (typically above HP3D) - */ - m->cp_hqd_pipe_priority = 1; - m->cp_hqd_queue_priority = 15; - - *mqd = m; - if (gart_addr) - *gart_addr = addr; - retval = mm->update_mqd(mm, m, q); - - return retval; + return init_mqd(mm, mqd, mqd_mem_obj, gart_addr, q); } static int update_mqd_hiq(struct mqd_manager *mm, void *mqd, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 2f26581b93ff..fb27783d7a54 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -886,6 +886,7 @@ static void emulated_link_detect(struct dc_link *link) return; } + /* dc_sink_create returns a new reference */ link->local_sink = sink; edid_status = dm_helpers_read_local_edid( @@ -952,6 +953,8 @@ static int dm_resume(void *handle) if (aconnector->fake_enable && aconnector->dc_link->local_sink) aconnector->fake_enable = false; + if (aconnector->dc_sink) + dc_sink_release(aconnector->dc_sink); aconnector->dc_sink = NULL; amdgpu_dm_update_connector_after_detect(aconnector); mutex_unlock(&aconnector->hpd_lock); @@ -1061,6 +1064,8 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) sink = aconnector->dc_link->local_sink; + if (sink) + dc_sink_retain(sink); /* * Edid mgmt connector gets first update only in mode_valid hook and then @@ -1085,21 +1090,24 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) * to it anymore after disconnect, so on next crtc to connector * reshuffle by UMD we will get into unwanted dc_sink release */ - if (aconnector->dc_sink != aconnector->dc_em_sink) - dc_sink_release(aconnector->dc_sink); + dc_sink_release(aconnector->dc_sink); } aconnector->dc_sink = sink; + dc_sink_retain(aconnector->dc_sink); amdgpu_dm_update_freesync_caps(connector, aconnector->edid); } else { amdgpu_dm_update_freesync_caps(connector, NULL); - if (!aconnector->dc_sink) + if (!aconnector->dc_sink) { aconnector->dc_sink = aconnector->dc_em_sink; - else if (aconnector->dc_sink != aconnector->dc_em_sink) dc_sink_retain(aconnector->dc_sink); + } } mutex_unlock(&dev->mode_config.mutex); + + if (sink) + dc_sink_release(sink); return; } @@ -1107,8 +1115,10 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) * TODO: temporary guard to look for proper fix * if this sink is MST sink, we should not do anything */ - if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + dc_sink_release(sink); return; + } if (aconnector->dc_sink == sink) { /* @@ -1117,6 +1127,8 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) */ DRM_DEBUG_DRIVER("DCHPD: connector_id=%d: dc_sink didn't change.\n", aconnector->connector_id); + if (sink) + dc_sink_release(sink); return; } @@ -1138,6 +1150,7 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) amdgpu_dm_update_freesync_caps(connector, NULL); aconnector->dc_sink = sink; + dc_sink_retain(aconnector->dc_sink); if (sink->dc_edid.length == 0) { aconnector->edid = NULL; drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); @@ -1158,11 +1171,15 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) amdgpu_dm_update_freesync_caps(connector, NULL); drm_connector_update_edid_property(connector, NULL); aconnector->num_modes = 0; + dc_sink_release(aconnector->dc_sink); aconnector->dc_sink = NULL; aconnector->edid = NULL; } mutex_unlock(&dev->mode_config.mutex); + + if (sink) + dc_sink_release(sink); } static void handle_hpd_irq(void *param) @@ -2977,6 +2994,7 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, return stream; } else { sink = aconnector->dc_sink; + dc_sink_retain(sink); } stream = dc_create_stream_for_sink(sink); @@ -3042,8 +3060,7 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, update_stream_signal(stream, sink); finish: - if (sink && sink->sink_signal == SIGNAL_TYPE_VIRTUAL && aconnector->base.force != DRM_FORCE_ON) - dc_sink_release(sink); + dc_sink_release(sink); return stream; } @@ -3301,6 +3318,14 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector) dm->backlight_dev = NULL; } #endif + + if (aconnector->dc_em_sink) + dc_sink_release(aconnector->dc_em_sink); + aconnector->dc_em_sink = NULL; + if (aconnector->dc_sink) + dc_sink_release(aconnector->dc_sink); + aconnector->dc_sink = NULL; + drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux); drm_connector_unregister(connector); drm_connector_cleanup(connector); @@ -3398,10 +3423,12 @@ static void create_eml_sink(struct amdgpu_dm_connector *aconnector) (edid->extensions + 1) * EDID_LENGTH, &init_params); - if (aconnector->base.force == DRM_FORCE_ON) + if (aconnector->base.force == DRM_FORCE_ON) { aconnector->dc_sink = aconnector->dc_link->local_sink ? aconnector->dc_link->local_sink : aconnector->dc_em_sink; + dc_sink_retain(aconnector->dc_sink); + } } static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index f51d52eb52e6..c4ea3a91f17a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -191,6 +191,7 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) &init_params); dc_sink->priv = aconnector; + /* dc_link_add_remote_sink returns a new reference */ aconnector->dc_sink = dc_sink; if (aconnector->dc_sink) diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c index 12d1842079ae..eb62d10bb65c 100644 --- a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c @@ -1348,12 +1348,12 @@ void dcn_bw_update_from_pplib(struct dc *dc) struct dm_pp_clock_levels_with_voltage fclks = {0}, dcfclks = {0}; bool res; - kernel_fpu_begin(); - /* TODO: This is not the proper way to obtain fabric_and_dram_bandwidth, should be min(fclk, memclk) */ res = dm_pp_get_clock_levels_by_type_with_voltage( ctx, DM_PP_CLOCK_TYPE_FCLK, &fclks); + kernel_fpu_begin(); + if (res) res = verify_clock_values(&fclks); @@ -1372,9 +1372,13 @@ void dcn_bw_update_from_pplib(struct dc *dc) } else BREAK_TO_DEBUGGER(); + kernel_fpu_end(); + res = dm_pp_get_clock_levels_by_type_with_voltage( ctx, DM_PP_CLOCK_TYPE_DCFCLK, &dcfclks); + kernel_fpu_begin(); + if (res) res = verify_clock_values(&dcfclks); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index 7f5a947ad31d..4eba3c4800b6 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -794,6 +794,7 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) sink->link->dongle_max_pix_clk = sink_caps.max_hdmi_pixel_clock; sink->converter_disable_audio = converter_disable_audio; + /* dc_sink_create returns a new reference */ link->local_sink = sink; edid_status = dm_helpers_read_local_edid( @@ -2037,6 +2038,9 @@ static enum dc_status enable_link( break; } + if (status == DC_OK) + pipe_ctx->stream->link->link_status.link_active = true; + return status; } @@ -2060,6 +2064,14 @@ static void disable_link(struct dc_link *link, enum signal_type signal) dp_disable_link_phy_mst(link, signal); } else link->link_enc->funcs->disable_output(link->link_enc, signal); + + if (signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + /* MST disable link only when no stream use the link */ + if (link->mst_stream_alloc_table.stream_count <= 0) + link->link_status.link_active = false; + } else { + link->link_status.link_active = false; + } } static bool dp_active_dongle_validate_timing( @@ -2623,8 +2635,6 @@ void core_link_enable_stream( } } - stream->link->link_status.link_active = true; - core_dc->hwss.enable_audio_stream(pipe_ctx); /* turn off otg test pattern if enable */ @@ -2659,8 +2669,6 @@ void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option) core_dc->hwss.disable_stream(pipe_ctx, option); disable_link(pipe_ctx->stream->link, pipe_ctx->stream->signal); - - pipe_ctx->stream->link->link_status.link_active = false; } void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 94a84bc57c7a..bfd27f10879e 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -724,7 +724,7 @@ static void build_vrr_infopacket_v1(enum signal_type signal, static void build_vrr_infopacket_v2(enum signal_type signal, const struct mod_vrr_params *vrr, - const enum color_transfer_func *app_tf, + enum color_transfer_func app_tf, struct dc_info_packet *infopacket) { unsigned int payload_size = 0; @@ -732,8 +732,7 @@ static void build_vrr_infopacket_v2(enum signal_type signal, build_vrr_infopacket_header_v2(signal, infopacket, &payload_size); build_vrr_infopacket_data(vrr, infopacket); - if (app_tf != NULL) - build_vrr_infopacket_fs2_data(*app_tf, infopacket); + build_vrr_infopacket_fs2_data(app_tf, infopacket); build_vrr_infopacket_checksum(&payload_size, infopacket); @@ -757,7 +756,7 @@ void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, const struct dc_stream_state *stream, const struct mod_vrr_params *vrr, enum vrr_packet_type packet_type, - const enum color_transfer_func *app_tf, + enum color_transfer_func app_tf, struct dc_info_packet *infopacket) { /* SPD info packet for FreeSync diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h index 4222e403b151..dcef85994c45 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h @@ -145,7 +145,7 @@ void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, const struct dc_stream_state *stream, const struct mod_vrr_params *vrr, enum vrr_packet_type packet_type, - const enum color_transfer_func *app_tf, + enum color_transfer_func app_tf, struct dc_info_packet *infopacket); void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c b/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c index ce177d7f04cb..6bf48934fdc4 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c @@ -277,8 +277,7 @@ int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr, bool skip_display_set if (!skip_display_settings) phm_notify_smc_display_config_after_ps_adjustment(hwmgr); - if ((hwmgr->request_dpm_level != hwmgr->dpm_level) && - !phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level)) + if (!phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level)) hwmgr->dpm_level = hwmgr->request_dpm_level; if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) { diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c index 4588bddf8b33..615cf2c09e54 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c @@ -489,15 +489,16 @@ int pp_atomfwctrl_get_gpio_information(struct pp_hwmgr *hwmgr, } int pp_atomfwctrl_get_clk_information_by_clkid(struct pp_hwmgr *hwmgr, - uint8_t id, uint32_t *frequency) + uint8_t clk_id, uint8_t syspll_id, + uint32_t *frequency) { struct amdgpu_device *adev = hwmgr->adev; struct atom_get_smu_clock_info_parameters_v3_1 parameters; struct atom_get_smu_clock_info_output_parameters_v3_1 *output; uint32_t ix; - parameters.clk_id = id; - parameters.syspll_id = 0; + parameters.clk_id = clk_id; + parameters.syspll_id = syspll_id; parameters.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ; parameters.dfsdid = 0; @@ -530,20 +531,23 @@ static void pp_atomfwctrl_copy_vbios_bootup_values_3_2(struct pp_hwmgr *hwmgr, boot_values->ulSocClk = 0; boot_values->ulDCEFClk = 0; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_SOCCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_SOCCLK_ID, SMU11_SYSPLL0_ID, &frequency)) boot_values->ulSocClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCEFCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCEFCLK_ID, SMU11_SYSPLL0_ID, &frequency)) boot_values->ulDCEFClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_ECLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_ECLK_ID, SMU11_SYSPLL0_ID, &frequency)) boot_values->ulEClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_VCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_VCLK_ID, SMU11_SYSPLL0_ID, &frequency)) boot_values->ulVClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCLK_ID, SMU11_SYSPLL0_ID, &frequency)) boot_values->ulDClk = frequency; + + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL1_0_FCLK_ID, SMU11_SYSPLL1_2_ID, &frequency)) + boot_values->ulFClk = frequency; } static void pp_atomfwctrl_copy_vbios_bootup_values_3_1(struct pp_hwmgr *hwmgr, @@ -563,19 +567,19 @@ static void pp_atomfwctrl_copy_vbios_bootup_values_3_1(struct pp_hwmgr *hwmgr, boot_values->ulSocClk = 0; boot_values->ulDCEFClk = 0; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_SOCCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_SOCCLK_ID, 0, &frequency)) boot_values->ulSocClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCEFCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCEFCLK_ID, 0, &frequency)) boot_values->ulDCEFClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_ECLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_ECLK_ID, 0, &frequency)) boot_values->ulEClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_VCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_VCLK_ID, 0, &frequency)) boot_values->ulVClk = frequency; - if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCLK_ID, &frequency)) + if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCLK_ID, 0, &frequency)) boot_values->ulDClk = frequency; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h index fe9e8ceef50e..b7e2651b570b 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h @@ -139,6 +139,7 @@ struct pp_atomfwctrl_bios_boot_up_values { uint32_t ulEClk; uint32_t ulVClk; uint32_t ulDClk; + uint32_t ulFClk; uint16_t usVddc; uint16_t usVddci; uint16_t usMvddc; @@ -236,7 +237,8 @@ int pp_atomfwctrl_get_vbios_bootup_values(struct pp_hwmgr *hwmgr, int pp_atomfwctrl_get_smc_dpm_information(struct pp_hwmgr *hwmgr, struct pp_atomfwctrl_smc_dpm_parameters *param); int pp_atomfwctrl_get_clk_information_by_clkid(struct pp_hwmgr *hwmgr, - uint8_t id, uint32_t *frequency); + uint8_t clk_id, uint8_t syspll_id, + uint32_t *frequency); #endif diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c index 48187acac59e..83d3d935f3ac 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c @@ -3491,14 +3491,14 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogStart); cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, - ixSMU_PM_STATUS_94, 0); + ixSMU_PM_STATUS_95, 0); for (i = 0; i < 10; i++) { - mdelay(1); + mdelay(500); smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogSample); tmp = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, - ixSMU_PM_STATUS_94); + ixSMU_PM_STATUS_95); if (tmp != 0) break; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c index 5479125ff4f6..5c4f701939ea 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c @@ -2575,10 +2575,10 @@ static int vega10_init_smc_table(struct pp_hwmgr *hwmgr) data->vbios_boot_state.gfx_clock = boot_up_values.ulGfxClk; data->vbios_boot_state.mem_clock = boot_up_values.ulUClk; pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, - SMU9_SYSPLL0_SOCCLK_ID, &boot_up_values.ulSocClk); + SMU9_SYSPLL0_SOCCLK_ID, 0, &boot_up_values.ulSocClk); pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, - SMU9_SYSPLL0_DCEFCLK_ID, &boot_up_values.ulDCEFClk); + SMU9_SYSPLL0_DCEFCLK_ID, 0, &boot_up_values.ulDCEFClk); data->vbios_boot_state.soc_clock = boot_up_values.ulSocClk; data->vbios_boot_state.dcef_clock = boot_up_values.ulDCEFClk; @@ -4407,9 +4407,9 @@ static int vega10_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfe return ret; features_to_disable = - (features_enabled ^ new_ppfeature_masks) & features_enabled; + features_enabled & ~new_ppfeature_masks; features_to_enable = - (features_enabled ^ new_ppfeature_masks) ^ features_to_disable; + ~features_enabled & new_ppfeature_masks; pr_debug("features_to_disable 0x%llx\n", features_to_disable); pr_debug("features_to_enable 0x%llx\n", features_to_enable); diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c index 6c8e78611c03..bdb48e94eff6 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c @@ -2009,9 +2009,9 @@ static int vega12_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfe return ret; features_to_disable = - (features_enabled ^ new_ppfeature_masks) & features_enabled; + features_enabled & ~new_ppfeature_masks; features_to_enable = - (features_enabled ^ new_ppfeature_masks) ^ features_to_disable; + ~features_enabled & new_ppfeature_masks; pr_debug("features_to_disable 0x%llx\n", features_to_disable); pr_debug("features_to_enable 0x%llx\n", features_to_enable); diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c index aad79affb081..9aa7bec1b5fe 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c @@ -463,9 +463,9 @@ static int vega20_setup_asic_task(struct pp_hwmgr *hwmgr) static void vega20_init_dpm_state(struct vega20_dpm_state *dpm_state) { dpm_state->soft_min_level = 0x0; - dpm_state->soft_max_level = 0xffff; + dpm_state->soft_max_level = VG20_CLOCK_MAX_DEFAULT; dpm_state->hard_min_level = 0x0; - dpm_state->hard_max_level = 0xffff; + dpm_state->hard_max_level = VG20_CLOCK_MAX_DEFAULT; } static int vega20_get_number_of_dpm_level(struct pp_hwmgr *hwmgr, @@ -711,8 +711,10 @@ static int vega20_setup_default_dpm_tables(struct pp_hwmgr *hwmgr) PP_ASSERT_WITH_CODE(!ret, "[SetupDefaultDpmTable] failed to get fclk dpm levels!", return ret); - } else - dpm_table->count = 0; + } else { + dpm_table->count = 1; + dpm_table->dpm_levels[0].value = data->vbios_boot_state.fclock / 100; + } vega20_init_dpm_state(&(dpm_table->dpm_state)); /* save a copy of the default DPM table */ @@ -754,6 +756,7 @@ static int vega20_init_smc_table(struct pp_hwmgr *hwmgr) data->vbios_boot_state.eclock = boot_up_values.ulEClk; data->vbios_boot_state.vclock = boot_up_values.ulVClk; data->vbios_boot_state.dclock = boot_up_values.ulDClk; + data->vbios_boot_state.fclock = boot_up_values.ulFClk; data->vbios_boot_state.uc_cooling_id = boot_up_values.ucCoolingID; smum_send_msg_to_smc_with_parameter(hwmgr, @@ -780,6 +783,8 @@ static int vega20_init_smc_table(struct pp_hwmgr *hwmgr) static int vega20_override_pcie_parameters(struct pp_hwmgr *hwmgr) { struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev); + struct vega20_hwmgr *data = + (struct vega20_hwmgr *)(hwmgr->backend); uint32_t pcie_gen = 0, pcie_width = 0, smu_pcie_arg; int ret; @@ -816,6 +821,10 @@ static int vega20_override_pcie_parameters(struct pp_hwmgr *hwmgr) "[OverridePcieParameters] Attempt to override pcie params failed!", return ret); + data->pcie_parameters_override = 1; + data->pcie_gen_level1 = pcie_gen; + data->pcie_width_level1 = pcie_width; + return 0; } @@ -979,6 +988,8 @@ static int vega20_od8_set_feature_capabilities( } if (data->smu_features[GNLD_DPM_UCLK].enabled) { + pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] = + data->dpm_table.mem_table.dpm_levels[data->dpm_table.mem_table.count - 2].value; if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_UCLK_MAX] && pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] > 0 && pptable_information->od_settings_max[OD8_SETTING_UCLK_FMAX] > 0 && @@ -2314,32 +2325,8 @@ static int vega20_force_dpm_lowest(struct pp_hwmgr *hwmgr) static int vega20_unforce_dpm_levels(struct pp_hwmgr *hwmgr) { - struct vega20_hwmgr *data = - (struct vega20_hwmgr *)(hwmgr->backend); - uint32_t soft_min_level, soft_max_level; int ret = 0; - soft_min_level = vega20_find_lowest_dpm_level(&(data->dpm_table.gfx_table)); - soft_max_level = vega20_find_highest_dpm_level(&(data->dpm_table.gfx_table)); - data->dpm_table.gfx_table.dpm_state.soft_min_level = - data->dpm_table.gfx_table.dpm_levels[soft_min_level].value; - data->dpm_table.gfx_table.dpm_state.soft_max_level = - data->dpm_table.gfx_table.dpm_levels[soft_max_level].value; - - soft_min_level = vega20_find_lowest_dpm_level(&(data->dpm_table.mem_table)); - soft_max_level = vega20_find_highest_dpm_level(&(data->dpm_table.mem_table)); - data->dpm_table.mem_table.dpm_state.soft_min_level = - data->dpm_table.mem_table.dpm_levels[soft_min_level].value; - data->dpm_table.mem_table.dpm_state.soft_max_level = - data->dpm_table.mem_table.dpm_levels[soft_max_level].value; - - soft_min_level = vega20_find_lowest_dpm_level(&(data->dpm_table.soc_table)); - soft_max_level = vega20_find_highest_dpm_level(&(data->dpm_table.soc_table)); - data->dpm_table.soc_table.dpm_state.soft_min_level = - data->dpm_table.soc_table.dpm_levels[soft_min_level].value; - data->dpm_table.soc_table.dpm_state.soft_max_level = - data->dpm_table.soc_table.dpm_levels[soft_max_level].value; - ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF); PP_ASSERT_WITH_CODE(!ret, "Failed to upload DPM Bootup Levels!", @@ -2641,9 +2628,8 @@ static int vega20_get_sclks(struct pp_hwmgr *hwmgr, struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.gfx_table); int i, count; - PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_GFXCLK].enabled, - "[GetSclks]: gfxclk dpm not enabled!\n", - return -EPERM); + if (!data->smu_features[GNLD_DPM_GFXCLK].enabled) + return -1; count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count; clocks->num_levels = count; @@ -2670,9 +2656,8 @@ static int vega20_get_memclocks(struct pp_hwmgr *hwmgr, struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.mem_table); int i, count; - PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_UCLK].enabled, - "[GetMclks]: uclk dpm not enabled!\n", - return -EPERM); + if (!data->smu_features[GNLD_DPM_UCLK].enabled) + return -1; count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count; clocks->num_levels = data->mclk_latency_table.count = count; @@ -2696,9 +2681,8 @@ static int vega20_get_dcefclocks(struct pp_hwmgr *hwmgr, struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.dcef_table); int i, count; - PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_DCEFCLK].enabled, - "[GetDcfclocks]: dcefclk dpm not enabled!\n", - return -EPERM); + if (!data->smu_features[GNLD_DPM_DCEFCLK].enabled) + return -1; count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count; clocks->num_levels = count; @@ -2719,9 +2703,8 @@ static int vega20_get_socclocks(struct pp_hwmgr *hwmgr, struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.soc_table); int i, count; - PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_SOCCLK].enabled, - "[GetSocclks]: socclk dpm not enabled!\n", - return -EPERM); + if (!data->smu_features[GNLD_DPM_SOCCLK].enabled) + return -1; count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count; clocks->num_levels = count; @@ -2799,7 +2782,6 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, data->od8_settings.od8_settings_array; OverDriveTable_t *od_table = &(data->smc_state_table.overdrive_table); - struct pp_clock_levels_with_latency clocks; int32_t input_index, input_clk, input_vol, i; int od8_id; int ret; @@ -2858,11 +2840,6 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, return -EOPNOTSUPP; } - ret = vega20_get_memclocks(hwmgr, &clocks); - PP_ASSERT_WITH_CODE(!ret, - "Attempt to get memory clk levels failed!", - return ret); - for (i = 0; i < size; i += 2) { if (i + 2 > size) { pr_info("invalid number of input parameters %d\n", @@ -2879,11 +2856,11 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, return -EINVAL; } - if (input_clk < clocks.data[0].clocks_in_khz / 1000 || + if (input_clk < od8_settings[OD8_SETTING_UCLK_FMAX].min_value || input_clk > od8_settings[OD8_SETTING_UCLK_FMAX].max_value) { pr_info("clock freq %d is not within allowed range [%d - %d]\n", input_clk, - clocks.data[0].clocks_in_khz / 1000, + od8_settings[OD8_SETTING_UCLK_FMAX].min_value, od8_settings[OD8_SETTING_UCLK_FMAX].max_value); return -EINVAL; } @@ -3088,9 +3065,9 @@ static int vega20_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfe return ret; features_to_disable = - (features_enabled ^ new_ppfeature_masks) & features_enabled; + features_enabled & ~new_ppfeature_masks; features_to_enable = - (features_enabled ^ new_ppfeature_masks) ^ features_to_disable; + ~features_enabled & new_ppfeature_masks; pr_debug("features_to_disable 0x%llx\n", features_to_disable); pr_debug("features_to_enable 0x%llx\n", features_to_enable); @@ -3128,7 +3105,7 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, &(data->dpm_table.fclk_table); int i, now, size = 0; int ret = 0; - uint32_t gen_speed, lane_width; + uint32_t gen_speed, lane_width, current_gen_speed, current_lane_width; switch (type) { case PP_SCLK: @@ -3137,10 +3114,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, "Attempt to get current gfx clk Failed!", return ret); - ret = vega20_get_sclks(hwmgr, &clocks); - PP_ASSERT_WITH_CODE(!ret, - "Attempt to get gfx clk levels Failed!", - return ret); + if (vega20_get_sclks(hwmgr, &clocks)) { + size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n", + now / 100); + break; + } for (i = 0; i < clocks.num_levels; i++) size += sprintf(buf + size, "%d: %uMhz %s\n", @@ -3154,10 +3132,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, "Attempt to get current mclk freq Failed!", return ret); - ret = vega20_get_memclocks(hwmgr, &clocks); - PP_ASSERT_WITH_CODE(!ret, - "Attempt to get memory clk levels Failed!", - return ret); + if (vega20_get_memclocks(hwmgr, &clocks)) { + size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n", + now / 100); + break; + } for (i = 0; i < clocks.num_levels; i++) size += sprintf(buf + size, "%d: %uMhz %s\n", @@ -3171,10 +3150,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, "Attempt to get current socclk freq Failed!", return ret); - ret = vega20_get_socclocks(hwmgr, &clocks); - PP_ASSERT_WITH_CODE(!ret, - "Attempt to get soc clk levels Failed!", - return ret); + if (vega20_get_socclocks(hwmgr, &clocks)) { + size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n", + now / 100); + break; + } for (i = 0; i < clocks.num_levels; i++) size += sprintf(buf + size, "%d: %uMhz %s\n", @@ -3200,10 +3180,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, "Attempt to get current dcefclk freq Failed!", return ret); - ret = vega20_get_dcefclocks(hwmgr, &clocks); - PP_ASSERT_WITH_CODE(!ret, - "Attempt to get dcefclk levels Failed!", - return ret); + if (vega20_get_dcefclocks(hwmgr, &clocks)) { + size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n", + now / 100); + break; + } for (i = 0; i < clocks.num_levels; i++) size += sprintf(buf + size, "%d: %uMhz %s\n", @@ -3212,28 +3193,36 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, break; case PP_PCIE: - gen_speed = (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) & + current_gen_speed = (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) & PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK) >> PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT; - lane_width = (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) & + current_lane_width = (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) & PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK) >> PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT; - for (i = 0; i < NUM_LINK_LEVELS; i++) + for (i = 0; i < NUM_LINK_LEVELS; i++) { + if (i == 1 && data->pcie_parameters_override) { + gen_speed = data->pcie_gen_level1; + lane_width = data->pcie_width_level1; + } else { + gen_speed = pptable->PcieGenSpeed[i]; + lane_width = pptable->PcieLaneCount[i]; + } size += sprintf(buf + size, "%d: %s %s %dMhz %s\n", i, - (pptable->PcieGenSpeed[i] == 0) ? "2.5GT/s," : - (pptable->PcieGenSpeed[i] == 1) ? "5.0GT/s," : - (pptable->PcieGenSpeed[i] == 2) ? "8.0GT/s," : - (pptable->PcieGenSpeed[i] == 3) ? "16.0GT/s," : "", - (pptable->PcieLaneCount[i] == 1) ? "x1" : - (pptable->PcieLaneCount[i] == 2) ? "x2" : - (pptable->PcieLaneCount[i] == 3) ? "x4" : - (pptable->PcieLaneCount[i] == 4) ? "x8" : - (pptable->PcieLaneCount[i] == 5) ? "x12" : - (pptable->PcieLaneCount[i] == 6) ? "x16" : "", + (gen_speed == 0) ? "2.5GT/s," : + (gen_speed == 1) ? "5.0GT/s," : + (gen_speed == 2) ? "8.0GT/s," : + (gen_speed == 3) ? "16.0GT/s," : "", + (lane_width == 1) ? "x1" : + (lane_width == 2) ? "x2" : + (lane_width == 3) ? "x4" : + (lane_width == 4) ? "x8" : + (lane_width == 5) ? "x12" : + (lane_width == 6) ? "x16" : "", pptable->LclkFreq[i], - (gen_speed == pptable->PcieGenSpeed[i]) && - (lane_width == pptable->PcieLaneCount[i]) ? + (current_gen_speed == gen_speed) && + (current_lane_width == lane_width) ? "*" : ""); + } break; case OD_SCLK: @@ -3288,13 +3277,8 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, } if (od8_settings[OD8_SETTING_UCLK_FMAX].feature_id) { - ret = vega20_get_memclocks(hwmgr, &clocks); - PP_ASSERT_WITH_CODE(!ret, - "Fail to get memory clk levels!", - return ret); - size += sprintf(buf + size, "MCLK: %7uMhz %10uMhz\n", - clocks.data[0].clocks_in_khz / 1000, + od8_settings[OD8_SETTING_UCLK_FMAX].min_value, od8_settings[OD8_SETTING_UCLK_FMAX].max_value); } @@ -3356,6 +3340,31 @@ static int vega20_set_uclk_to_highest_dpm_level(struct pp_hwmgr *hwmgr, return ret; } +static int vega20_set_fclk_to_highest_dpm_level(struct pp_hwmgr *hwmgr) +{ + struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend); + struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.fclk_table); + int ret = 0; + + if (data->smu_features[GNLD_DPM_FCLK].enabled) { + PP_ASSERT_WITH_CODE(dpm_table->count > 0, + "[SetFclkToHightestDpmLevel] Dpm table has no entry!", + return -EINVAL); + PP_ASSERT_WITH_CODE(dpm_table->count <= NUM_FCLK_DPM_LEVELS, + "[SetFclkToHightestDpmLevel] Dpm table has too many entries!", + return -EINVAL); + + dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetSoftMinByFreq, + (PPCLK_FCLK << 16 ) | dpm_table->dpm_state.soft_min_level)), + "[SetFclkToHightestDpmLevel] Set soft min fclk failed!", + return ret); + } + + return ret; +} + static int vega20_pre_display_configuration_changed_task(struct pp_hwmgr *hwmgr) { struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend); @@ -3366,8 +3375,10 @@ static int vega20_pre_display_configuration_changed_task(struct pp_hwmgr *hwmgr) ret = vega20_set_uclk_to_highest_dpm_level(hwmgr, &data->dpm_table.mem_table); + if (ret) + return ret; - return ret; + return vega20_set_fclk_to_highest_dpm_level(hwmgr); } static int vega20_display_configuration_changed_task(struct pp_hwmgr *hwmgr) @@ -3461,9 +3472,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr) /* gfxclk */ dpm_table = &(data->dpm_table.gfx_table); dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT; dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT; if (PP_CAP(PHM_PlatformCaps_UMDPState)) { if (VEGA20_UMD_PSTATE_GFXCLK_LEVEL < dpm_table->count) { @@ -3485,9 +3496,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr) /* memclk */ dpm_table = &(data->dpm_table.mem_table); dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT; dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT; if (PP_CAP(PHM_PlatformCaps_UMDPState)) { if (VEGA20_UMD_PSTATE_MCLK_LEVEL < dpm_table->count) { @@ -3526,12 +3537,21 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr) if (hwmgr->display_config->nb_pstate_switch_disable) dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + /* fclk */ + dpm_table = &(data->dpm_table.fclk_table); + dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value; + dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT; + dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value; + dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT; + if (hwmgr->display_config->nb_pstate_switch_disable) + dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + /* vclk */ dpm_table = &(data->dpm_table.vclk_table); dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT; dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT; if (PP_CAP(PHM_PlatformCaps_UMDPState)) { if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) { @@ -3548,9 +3568,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr) /* dclk */ dpm_table = &(data->dpm_table.dclk_table); dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT; dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT; if (PP_CAP(PHM_PlatformCaps_UMDPState)) { if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) { @@ -3567,9 +3587,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr) /* socclk */ dpm_table = &(data->dpm_table.soc_table); dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT; dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT; if (PP_CAP(PHM_PlatformCaps_UMDPState)) { if (VEGA20_UMD_PSTATE_SOCCLK_LEVEL < dpm_table->count) { @@ -3586,9 +3606,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr) /* eclk */ dpm_table = &(data->dpm_table.eclk_table); dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT; dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value; - dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value; + dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT; if (PP_CAP(PHM_PlatformCaps_UMDPState)) { if (VEGA20_UMD_PSTATE_VCEMCLK_LEVEL < dpm_table->count) { diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h index 37f5f5e657da..a5bc758ae097 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h @@ -42,6 +42,8 @@ #define AVFS_CURVE 0 #define OD8_HOTCURVE_TEMPERATURE 85 +#define VG20_CLOCK_MAX_DEFAULT 0xFFFF + typedef uint32_t PP_Clock; enum { @@ -219,6 +221,7 @@ struct vega20_vbios_boot_state { uint32_t eclock; uint32_t dclock; uint32_t vclock; + uint32_t fclock; }; #define DPMTABLE_OD_UPDATE_SCLK 0x00000001 @@ -523,6 +526,10 @@ struct vega20_hwmgr { unsigned long metrics_time; SmuMetrics_t metrics_table; + + bool pcie_parameters_override; + uint32_t pcie_gen_level1; + uint32_t pcie_width_level1; }; #define VEGA20_DPM2_NEAR_TDP_DEC 10 diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c index 97f8a1a970c3..7a7f15d0c53a 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c @@ -32,6 +32,8 @@ #include "cgs_common.h" #include "vega20_pptable.h" +#define VEGA20_FAN_TARGET_TEMPERATURE_OVERRIDE 105 + static void set_hw_cap(struct pp_hwmgr *hwmgr, bool enable, enum phm_platform_caps cap) { @@ -798,6 +800,17 @@ static int append_vbios_pptable(struct pp_hwmgr *hwmgr, PPTable_t *ppsmc_pptable return 0; } +static int override_powerplay_table_fantargettemperature(struct pp_hwmgr *hwmgr) +{ + struct phm_ppt_v3_information *pptable_information = + (struct phm_ppt_v3_information *)hwmgr->pptable; + PPTable_t *ppsmc_pptable = (PPTable_t *)(pptable_information->smc_pptable); + + ppsmc_pptable->FanTargetTemperature = VEGA20_FAN_TARGET_TEMPERATURE_OVERRIDE; + + return 0; +} + #define VEGA20_ENGINECLOCK_HARDMAX 198000 static int init_powerplay_table_information( struct pp_hwmgr *hwmgr, @@ -887,6 +900,10 @@ static int init_powerplay_table_information( result = append_vbios_pptable(hwmgr, (pptable_information->smc_pptable)); + if (result) + return result; + + result = override_powerplay_table_fantargettemperature(hwmgr); return result; } diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c index 52abca065764..2d4cfe14f72e 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c @@ -2330,6 +2330,7 @@ static uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member) case DRAM_LOG_BUFF_SIZE: return offsetof(SMU74_SoftRegisters, DRAM_LOG_BUFF_SIZE); } + break; case SMU_Discrete_DpmTable: switch (member) { case UvdBootLevel: @@ -2339,6 +2340,7 @@ static uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member) case LowSclkInterruptThreshold: return offsetof(SMU74_Discrete_DpmTable, LowSclkInterruptThreshold); } + break; } pr_warn("can't get the offset of type %x member %x\n", type, member); return 0; diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c index 079fc8e8f709..742b3dc1f6cb 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c @@ -40,10 +40,8 @@ bool smu9_is_smc_ram_running(struct pp_hwmgr *hwmgr) struct amdgpu_device *adev = hwmgr->adev; uint32_t mp1_fw_flags; - WREG32_SOC15(NBIF, 0, mmPCIE_INDEX2, - (MP1_Public | (smnMP1_FIRMWARE_FLAGS & 0xffffffff))); - - mp1_fw_flags = RREG32_SOC15(NBIF, 0, mmPCIE_DATA2); + mp1_fw_flags = RREG32_PCIE(MP1_Public | + (smnMP1_FIRMWARE_FLAGS & 0xffffffff)); if (mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) return true; diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c index b7ff7d4d6f44..ba00744c3413 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c @@ -49,10 +49,8 @@ static bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr) struct amdgpu_device *adev = hwmgr->adev; uint32_t mp1_fw_flags; - WREG32_SOC15(NBIF, 0, mmPCIE_INDEX2, - (MP1_Public | (smnMP1_FIRMWARE_FLAGS & 0xffffffff))); - - mp1_fw_flags = RREG32_SOC15(NBIF, 0, mmPCIE_DATA2); + mp1_fw_flags = RREG32_PCIE(MP1_Public | + (smnMP1_FIRMWARE_FLAGS & 0xffffffff)); if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >> MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 540a77a2ade9..40ac19848034 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3039,9 +3039,31 @@ commit: return 0; } -static int __drm_atomic_helper_disable_all(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx, - bool clean_old_fbs) +/** + * drm_atomic_helper_disable_all - disable all currently active outputs + * @dev: DRM device + * @ctx: lock acquisition context + * + * Loops through all connectors, finding those that aren't turned off and then + * turns them off by setting their DPMS mode to OFF and deactivating the CRTC + * that they are connected to. + * + * This is used for example in suspend/resume to disable all currently active + * functions when suspending. If you just want to shut down everything at e.g. + * driver unload, look at drm_atomic_helper_shutdown(). + * + * Note that if callers haven't already acquired all modeset locks this might + * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). + * + * Returns: + * 0 on success or a negative error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and + * drm_atomic_helper_shutdown(). + */ +int drm_atomic_helper_disable_all(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) { struct drm_atomic_state *state; struct drm_connector_state *conn_state; @@ -3099,35 +3121,6 @@ free: drm_atomic_state_put(state); return ret; } - -/** - * drm_atomic_helper_disable_all - disable all currently active outputs - * @dev: DRM device - * @ctx: lock acquisition context - * - * Loops through all connectors, finding those that aren't turned off and then - * turns them off by setting their DPMS mode to OFF and deactivating the CRTC - * that they are connected to. - * - * This is used for example in suspend/resume to disable all currently active - * functions when suspending. If you just want to shut down everything at e.g. - * driver unload, look at drm_atomic_helper_shutdown(). - * - * Note that if callers haven't already acquired all modeset locks this might - * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). - * - * Returns: - * 0 on success or a negative error code on failure. - * - * See also: - * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and - * drm_atomic_helper_shutdown(). - */ -int drm_atomic_helper_disable_all(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) -{ - return __drm_atomic_helper_disable_all(dev, ctx, false); -} EXPORT_SYMBOL(drm_atomic_helper_disable_all); /** @@ -3148,7 +3141,7 @@ void drm_atomic_helper_shutdown(struct drm_device *dev) DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); - ret = __drm_atomic_helper_disable_all(dev, &ctx, true); + ret = drm_atomic_helper_disable_all(dev, &ctx); if (ret) DRM_ERROR("Disabling all crtc's during unload failed with %i\n", ret); diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index 67b1fca39aa6..0e3043e08c69 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -185,7 +185,7 @@ static int compat_drm_getmap(struct file *file, unsigned int cmd, m32.size = map.size; m32.type = map.type; m32.flags = map.flags; - m32.handle = ptr_to_compat(map.handle); + m32.handle = ptr_to_compat((void __user *)map.handle); m32.mtrr = map.mtrr; if (copy_to_user(argp, &m32, sizeof(m32))) return -EFAULT; @@ -216,7 +216,7 @@ static int compat_drm_addmap(struct file *file, unsigned int cmd, m32.offset = map.offset; m32.mtrr = map.mtrr; - m32.handle = ptr_to_compat(map.handle); + m32.handle = ptr_to_compat((void __user *)map.handle); if (map.handle != compat_ptr(m32.handle)) pr_err_ratelimited("compat_drm_addmap truncated handle %p for type %d offset %x\n", map.handle, m32.type, m32.offset); @@ -526,7 +526,7 @@ static int compat_drm_getsareactx(struct file *file, unsigned int cmd, if (err) return err; - req32.handle = ptr_to_compat(req.handle); + req32.handle = ptr_to_compat((void __user *)req.handle); if (copy_to_user(argp, &req32, sizeof(req32))) return -EFAULT; diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index 041a77e400d4..21df44b78df3 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -2,7 +2,6 @@ config DRM_ETNAVIV tristate "ETNAVIV (DRM support for Vivante GPU IP cores)" depends on DRM - depends on ARCH_MXC || ARCH_DOVE || (ARM && COMPILE_TEST) depends on MMU select SHMEM select SYNC_FILE diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h index acb68c698363..4d5d1a77eb2a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h @@ -15,8 +15,6 @@ struct etnaviv_perfmon_request; struct etnaviv_cmdbuf { /* suballocator this cmdbuf is allocated from */ struct etnaviv_cmdbuf_suballoc *suballoc; - /* user context key, must be unique between all active users */ - struct etnaviv_file_private *ctx; /* cmdbuf properties */ int suballoc_offset; void *vaddr; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c index 3fbb4855396c..33854c94cb85 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c @@ -215,7 +215,7 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) mutex_lock(&obj->lock); pages = etnaviv_gem_get_pages(obj); mutex_unlock(&obj->lock); - if (pages) { + if (!IS_ERR(pages)) { int j; iter.hdr->data[0] = bomap - bomap_start; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index 76079c2291f8..f0abb744ef95 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -95,6 +95,7 @@ struct etnaviv_gem_submit_bo { struct etnaviv_gem_submit { struct drm_sched_job sched_job; struct kref refcount; + struct etnaviv_file_private *ctx; struct etnaviv_gpu *gpu; struct dma_fence *out_fence, *in_fence; int out_fence_id; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 0566171f8df2..f21529e635e3 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -15,7 +15,7 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) int npages = obj->size >> PAGE_SHIFT; if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */ - return NULL; + return ERR_PTR(-EINVAL); return drm_prime_pages_to_sg(etnaviv_obj->pages, npages); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 30875f8f2933..b2fe3446bfbc 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -506,7 +506,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, if (ret) goto err_submit_objects; - submit->cmdbuf.ctx = file->driver_priv; + submit->ctx = file->driver_priv; submit->exec_state = args->exec_state; submit->flags = args->flags; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c index f1c88d8ad5ba..f794e04be9e6 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c @@ -320,8 +320,8 @@ etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu) domain = &etnaviv_domain->base; domain->dev = gpu->dev; - domain->base = 0; - domain->size = (u64)SZ_1G * 4; + domain->base = SZ_4K; + domain->size = (u64)SZ_1G * 4 - SZ_4K; domain->ops = &etnaviv_iommuv2_ops; ret = etnaviv_iommuv2_init(etnaviv_domain); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c index 9980d81a26e3..4227a4006c34 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c @@ -113,7 +113,7 @@ static const struct etnaviv_pm_domain doms_3d[] = { .name = "PE", .profile_read = VIVS_MC_PROFILE_PE_READ, .profile_config = VIVS_MC_PROFILE_CONFIG0, - .nr_signals = 5, + .nr_signals = 4, .signal = (const struct etnaviv_pm_signal[]) { { "PIXEL_COUNT_KILLED_BY_COLOR_PIPE", @@ -435,7 +435,7 @@ int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu, dom = meta->domains + signal->domain; - if (signal->iter > dom->nr_signals) + if (signal->iter >= dom->nr_signals) return -EINVAL; sig = &dom->signal[signal->iter]; @@ -461,7 +461,7 @@ int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r, dom = meta->domains + r->domain; - if (r->signal > dom->nr_signals) + if (r->signal >= dom->nr_signals) return -EINVAL; return 0; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index 67ae26602024..6d24fea1766b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -153,7 +153,7 @@ int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity, mutex_lock(&submit->gpu->fence_lock); ret = drm_sched_job_init(&submit->sched_job, sched_entity, - submit->cmdbuf.ctx); + submit->ctx); if (ret) goto out_unlock; diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index 215b6ff8aa73..db7bb5bd5add 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -163,17 +163,25 @@ int i915_active_ref(struct i915_active *ref, struct i915_request *rq) { struct i915_active_request *active; + int err = 0; + + /* Prevent reaping in case we malloc/wait while building the tree */ + i915_active_acquire(ref); active = active_instance(ref, timeline); - if (IS_ERR(active)) - return PTR_ERR(active); + if (IS_ERR(active)) { + err = PTR_ERR(active); + goto out; + } if (!i915_active_request_isset(active)) ref->count++; __i915_active_request_set(active, rq); GEM_BUG_ON(!ref->count); - return 0; +out: + i915_active_release(ref); + return err; } bool i915_active_acquire(struct i915_active *ref) @@ -223,19 +231,25 @@ int i915_request_await_active_request(struct i915_request *rq, int i915_request_await_active(struct i915_request *rq, struct i915_active *ref) { struct active_node *it, *n; - int ret; + int err = 0; - ret = i915_request_await_active_request(rq, &ref->last); - if (ret) - return ret; + /* await allocates and so we need to avoid hitting the shrinker */ + if (i915_active_acquire(ref)) + goto out; /* was idle */ + + err = i915_request_await_active_request(rq, &ref->last); + if (err) + goto out; rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) { - ret = i915_request_await_active_request(rq, &it->base); - if (ret) - return ret; + err = i915_request_await_active_request(rq, &it->base); + if (err) + goto out; } - return 0; +out: + i915_active_release(ref); + return err; } #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 6630212f2faf..9df65d386d11 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -757,39 +757,6 @@ static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) return ret; } -#if !defined(CONFIG_VGA_CONSOLE) -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{ - return 0; -} -#elif !defined(CONFIG_DUMMY_CONSOLE) -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{ - return -ENODEV; -} -#else -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{ - int ret = 0; - - DRM_INFO("Replacing VGA console driver\n"); - - console_lock(); - if (con_is_bound(&vga_con)) - ret = do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES - 1, 1); - if (ret == 0) { - ret = do_unregister_con_driver(&vga_con); - - /* Ignore "already unregistered". */ - if (ret == -ENODEV) - ret = 0; - } - console_unlock(); - - return ret; -} -#endif - static void intel_init_dpio(struct drm_i915_private *dev_priv) { /* @@ -1420,7 +1387,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) goto err_ggtt; } - ret = i915_kick_out_vgacon(dev_priv); + ret = vga_remove_vgacon(pdev); if (ret) { DRM_ERROR("failed to remove conflicting VGA console\n"); goto err_ggtt; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6728ea5c71d4..30d516e975c6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1688,7 +1688,8 @@ __vma_matches(struct vm_area_struct *vma, struct file *filp, if (vma->vm_file != filp) return false; - return vma->vm_start == addr && (vma->vm_end - vma->vm_start) == size; + return vma->vm_start == addr && + (vma->vm_end - vma->vm_start) == PAGE_ALIGN(size); } /** diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c index d01683167c77..8bc042551692 100644 --- a/drivers/gpu/drm/i915/i915_scheduler.c +++ b/drivers/gpu/drm/i915/i915_scheduler.c @@ -223,8 +223,14 @@ out: return &p->requests[idx]; } +struct sched_cache { + struct list_head *priolist; +}; + static struct intel_engine_cs * -sched_lock_engine(struct i915_sched_node *node, struct intel_engine_cs *locked) +sched_lock_engine(const struct i915_sched_node *node, + struct intel_engine_cs *locked, + struct sched_cache *cache) { struct intel_engine_cs *engine = node_to_request(node)->engine; @@ -232,6 +238,7 @@ sched_lock_engine(struct i915_sched_node *node, struct intel_engine_cs *locked) if (engine != locked) { spin_unlock(&locked->timeline.lock); + memset(cache, 0, sizeof(*cache)); spin_lock(&engine->timeline.lock); } @@ -253,11 +260,11 @@ static bool inflight(const struct i915_request *rq, static void __i915_schedule(struct i915_request *rq, const struct i915_sched_attr *attr) { - struct list_head *uninitialized_var(pl); - struct intel_engine_cs *engine, *last; + struct intel_engine_cs *engine; struct i915_dependency *dep, *p; struct i915_dependency stack; const int prio = attr->priority; + struct sched_cache cache; LIST_HEAD(dfs); /* Needed in order to use the temporary link inside i915_dependency */ @@ -328,7 +335,7 @@ static void __i915_schedule(struct i915_request *rq, __list_del_entry(&stack.dfs_link); } - last = NULL; + memset(&cache, 0, sizeof(cache)); engine = rq->engine; spin_lock_irq(&engine->timeline.lock); @@ -338,7 +345,7 @@ static void __i915_schedule(struct i915_request *rq, INIT_LIST_HEAD(&dep->dfs_link); - engine = sched_lock_engine(node, engine); + engine = sched_lock_engine(node, engine, &cache); lockdep_assert_held(&engine->timeline.lock); /* Recheck after acquiring the engine->timeline.lock */ @@ -347,11 +354,11 @@ static void __i915_schedule(struct i915_request *rq, node->attr.priority = prio; if (!list_empty(&node->link)) { - if (last != engine) { - pl = i915_sched_lookup_priolist(engine, prio); - last = engine; - } - list_move_tail(&node->link, pl); + if (!cache.priolist) + cache.priolist = + i915_sched_lookup_priolist(engine, + prio); + list_move_tail(&node->link, cache.priolist); } else { /* * If the request is not in the priolist queue because diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c index cacaa1d04d17..09ed90c0ba00 100644 --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c @@ -106,16 +106,6 @@ bool intel_engine_breadcrumbs_irq(struct intel_engine_cs *engine) GEM_BUG_ON(!test_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags)); - clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags); - - /* - * We may race with direct invocation of - * dma_fence_signal(), e.g. i915_request_retire(), - * in which case we can skip processing it ourselves. - */ - if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, - &rq->fence.flags)) - continue; /* * Queue for execution after dropping the signaling @@ -123,6 +113,14 @@ bool intel_engine_breadcrumbs_irq(struct intel_engine_cs *engine) * more signalers to the same context or engine. */ i915_request_get(rq); + + /* + * We may race with direct invocation of + * dma_fence_signal(), e.g. i915_request_retire(), + * so we need to acquire our reference to the request + * before we cancel the breadcrumb. + */ + clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags); list_add_tail(&rq->signal_link, &signal); } diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index ca705546a0ab..14d580cdefd3 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -3568,6 +3568,13 @@ static void intel_ddi_update_pipe(struct intel_encoder *encoder, { if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) intel_ddi_update_pipe_dp(encoder, crtc_state, conn_state); + + if (conn_state->content_protection == + DRM_MODE_CONTENT_PROTECTION_DESIRED) + intel_hdcp_enable(to_intel_connector(conn_state->connector)); + else if (conn_state->content_protection == + DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + intel_hdcp_disable(to_intel_connector(conn_state->connector)); } static void intel_ddi_set_fia_lane_count(struct intel_encoder *encoder, @@ -3962,12 +3969,7 @@ static int modeset_pipe(struct drm_crtc *crtc, goto out; ret = drm_atomic_commit(state); - if (ret) - goto out; - - return 0; - - out: +out: drm_atomic_state_put(state); return ret; diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/selftests/i915_gem_context.c index d00d0bb07784..7eb58a9d1319 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_context.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_context.c @@ -710,47 +710,45 @@ __sseu_prepare(struct drm_i915_private *i915, unsigned int flags, struct i915_gem_context *ctx, struct intel_engine_cs *engine, - struct igt_spinner **spin_out) + struct igt_spinner **spin) { - int ret = 0; - - if (flags & (TEST_BUSY | TEST_RESET)) { - struct igt_spinner *spin; - struct i915_request *rq; + struct i915_request *rq; + int ret; - spin = kzalloc(sizeof(*spin), GFP_KERNEL); - if (!spin) { - ret = -ENOMEM; - goto out; - } + *spin = NULL; + if (!(flags & (TEST_BUSY | TEST_RESET))) + return 0; - ret = igt_spinner_init(spin, i915); - if (ret) - return ret; + *spin = kzalloc(sizeof(**spin), GFP_KERNEL); + if (!*spin) + return -ENOMEM; - rq = igt_spinner_create_request(spin, ctx, engine, MI_NOOP); - if (IS_ERR(rq)) { - ret = PTR_ERR(rq); - igt_spinner_fini(spin); - kfree(spin); - goto out; - } + ret = igt_spinner_init(*spin, i915); + if (ret) + goto err_free; - i915_request_add(rq); + rq = igt_spinner_create_request(*spin, ctx, engine, MI_NOOP); + if (IS_ERR(rq)) { + ret = PTR_ERR(rq); + goto err_fini; + } - if (!igt_wait_for_spinner(spin, rq)) { - pr_err("%s: Spinner failed to start!\n", name); - igt_spinner_end(spin); - igt_spinner_fini(spin); - kfree(spin); - ret = -ETIMEDOUT; - goto out; - } + i915_request_add(rq); - *spin_out = spin; + if (!igt_wait_for_spinner(*spin, rq)) { + pr_err("%s: Spinner failed to start!\n", name); + ret = -ETIMEDOUT; + goto err_end; } -out: + return 0; + +err_end: + igt_spinner_end(*spin); +err_fini: + igt_spinner_fini(*spin); +err_free: + kfree(fetch_and_zero(spin)); return ret; } @@ -897,22 +895,23 @@ __sseu_test(struct drm_i915_private *i915, ret = __sseu_prepare(i915, name, flags, ctx, engine, &spin); if (ret) - goto out; + goto out_context; ret = __i915_gem_context_reconfigure_sseu(ctx, engine, sseu); if (ret) - goto out; + goto out_spin; ret = __sseu_finish(i915, name, flags, ctx, kctx, engine, obj, hweight32(sseu.slice_mask), spin); -out: +out_spin: if (spin) { igt_spinner_end(spin); igt_spinner_fini(spin); kfree(spin); } +out_context: kernel_context_close(kctx); return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c index 8be7a83ced9b..aa9fec80492d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c @@ -261,7 +261,7 @@ static const struct migrate_vma_ops nouveau_dmem_fault_migrate_ops = { .finalize_and_map = nouveau_dmem_fault_finalize_and_map, }; -static int +static vm_fault_t nouveau_dmem_fault(struct hmm_devmem *devmem, struct vm_area_struct *vma, unsigned long addr, diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index bb81e310eb6d..578d867a81d5 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -79,6 +79,10 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto free_dev; + ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "qxl"); + if (ret) + goto disable_pci; + ret = qxl_device_init(qdev, &qxl_driver, pdev); if (ret) goto disable_pci; @@ -94,7 +98,6 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto modeset_cleanup; - drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "qxl"); drm_fbdev_generic_setup(&qdev->ddev, 32); return 0; diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index dc8e039bfab5..f2f3ef8af271 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -48,6 +48,8 @@ #include <linux/miscdevice.h> #include <linux/slab.h> #include <linux/screen_info.h> +#include <linux/vt.h> +#include <linux/console.h> #include <linux/uaccess.h> @@ -168,6 +170,53 @@ void vga_set_default_device(struct pci_dev *pdev) vga_default = pci_dev_get(pdev); } +/** + * vga_remove_vgacon - deactivete vga console + * + * Unbind and unregister vgacon in case pdev is the default vga + * device. Can be called by gpu drivers on initialization to make + * sure vga register access done by vgacon will not disturb the + * device. + * + * @pdev: pci device. + */ +#if !defined(CONFIG_VGA_CONSOLE) +int vga_remove_vgacon(struct pci_dev *pdev) +{ + return 0; +} +#elif !defined(CONFIG_DUMMY_CONSOLE) +int vga_remove_vgacon(struct pci_dev *pdev) +{ + return -ENODEV; +} +#else +int vga_remove_vgacon(struct pci_dev *pdev) +{ + int ret = 0; + + if (pdev != vga_default) + return 0; + vgaarb_info(&pdev->dev, "deactivate vga console\n"); + + console_lock(); + if (con_is_bound(&vga_con)) + ret = do_take_over_console(&dummy_con, 0, + MAX_NR_CONSOLES - 1, 1); + if (ret == 0) { + ret = do_unregister_con_driver(&vga_con); + + /* Ignore "already unregistered". */ + if (ret == -ENODEV) + ret = 0; + } + console_unlock(); + + return ret; +} +#endif +EXPORT_SYMBOL(vga_remove_vgacon); + static inline void vga_irq_set_state(struct vga_device *vgadev, bool state) { if (vgadev->irq_set_state) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 9a63e87ea5f3..be302ec5f66b 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -871,7 +871,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) } pm_runtime_put(&adev->dev); - dev_info(dev, "%s initialized\n", (char *)id->data); + dev_info(dev, "%s initialized\n", (char *)coresight_get_uci_data(id)); if (boot_enable) { coresight_enable(drvdata->csdev); drvdata->boot_enable = true; @@ -915,36 +915,18 @@ static const struct dev_pm_ops etm_dev_pm_ops = { }; static const struct amba_id etm_ids[] = { - { /* ETM 3.3 */ - .id = 0x000bb921, - .mask = 0x000fffff, - .data = "ETM 3.3", - }, - { /* ETM 3.5 - Cortex-A5 */ - .id = 0x000bb955, - .mask = 0x000fffff, - .data = "ETM 3.5", - }, - { /* ETM 3.5 */ - .id = 0x000bb956, - .mask = 0x000fffff, - .data = "ETM 3.5", - }, - { /* PTM 1.0 */ - .id = 0x000bb950, - .mask = 0x000fffff, - .data = "PTM 1.0", - }, - { /* PTM 1.1 */ - .id = 0x000bb95f, - .mask = 0x000fffff, - .data = "PTM 1.1", - }, - { /* PTM 1.1 Qualcomm */ - .id = 0x000b006f, - .mask = 0x000fffff, - .data = "PTM 1.1", - }, + /* ETM 3.3 */ + CS_AMBA_ID_DATA(0x000bb921, "ETM 3.3"), + /* ETM 3.5 - Cortex-A5 */ + CS_AMBA_ID_DATA(0x000bb955, "ETM 3.5"), + /* ETM 3.5 */ + CS_AMBA_ID_DATA(0x000bb956, "ETM 3.5"), + /* PTM 1.0 */ + CS_AMBA_ID_DATA(0x000bb950, "PTM 1.0"), + /* PTM 1.1 */ + CS_AMBA_ID_DATA(0x000bb95f, "PTM 1.1"), + /* PTM 1.1 Qualcomm */ + CS_AMBA_ID_DATA(0x000b006f, "PTM 1.1"), { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index fe76b176974a..08ce37c9475d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1068,18 +1068,21 @@ err_arch_supported: return ret; } -#define ETM4x_AMBA_ID(pid) \ - { \ - .id = pid, \ - .mask = 0x000fffff, \ +static struct amba_cs_uci_id uci_id_etm4[] = { + { + /* ETMv4 UCI data */ + .devarch = 0x47704a13, + .devarch_mask = 0xfff0ffff, + .devtype = 0x00000013, } +}; static const struct amba_id etm4_ids[] = { - ETM4x_AMBA_ID(0x000bb95d), /* Cortex-A53 */ - ETM4x_AMBA_ID(0x000bb95e), /* Cortex-A57 */ - ETM4x_AMBA_ID(0x000bb95a), /* Cortex-A72 */ - ETM4x_AMBA_ID(0x000bb959), /* Cortex-A73 */ - ETM4x_AMBA_ID(0x000bb9da), /* Cortex-A35 */ + CS_AMBA_ID(0x000bb95d), /* Cortex-A53 */ + CS_AMBA_ID(0x000bb95e), /* Cortex-A57 */ + CS_AMBA_ID(0x000bb95a), /* Cortex-A72 */ + CS_AMBA_ID(0x000bb959), /* Cortex-A73 */ + CS_AMBA_UCI_ID(0x000bb9da, uci_id_etm4), /* Cortex-A35 */ {}, }; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index b936c6d7e13f..e0684d06e9ee 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -6,6 +6,7 @@ #ifndef _CORESIGHT_PRIV_H #define _CORESIGHT_PRIV_H +#include <linux/amba/bus.h> #include <linux/bitops.h> #include <linux/io.h> #include <linux/coresight.h> @@ -160,4 +161,43 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif +/* + * Macros and inline functions to handle CoreSight UCI data and driver + * private data in AMBA ID table entries, and extract data values. + */ + +/* coresight AMBA ID, no UCI, no driver data: id table entry */ +#define CS_AMBA_ID(pid) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + } + +/* coresight AMBA ID, UCI with driver data only: id table entry. */ +#define CS_AMBA_ID_DATA(pid, dval) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + .data = (void *)&(struct amba_cs_uci_id) \ + { \ + .data = (void *)dval, \ + } \ + } + +/* coresight AMBA ID, full UCI structure: id table entry. */ +#define CS_AMBA_UCI_ID(pid, uci_ptr) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + .data = uci_ptr \ + } + +/* extract the data value from a UCI structure given amba_id pointer. */ +static inline void *coresight_get_uci_data(const struct amba_id *id) +{ + if (id->data) + return ((struct amba_cs_uci_id *)(id->data))->data; + return 0; +} + #endif diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index f07825df5c7a..9f8a844ed7aa 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -870,7 +870,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); - dev_info(dev, "%s initialized\n", (char *)id->data); + dev_info(dev, "%s initialized\n", (char *)coresight_get_uci_data(id)); return 0; stm_unregister: @@ -905,16 +905,8 @@ static const struct dev_pm_ops stm_dev_pm_ops = { }; static const struct amba_id stm_ids[] = { - { - .id = 0x000bb962, - .mask = 0x000fffff, - .data = "STM32", - }, - { - .id = 0x000bb963, - .mask = 0x000fffff, - .data = "STM500", - }, + CS_AMBA_ID_DATA(0x000bb962, "STM32"), + CS_AMBA_ID_DATA(0x000bb963, "STM500"), { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index ea249f0bcd73..2a02da3d630f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -443,7 +443,8 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etr_cs_ops; - ret = tmc_etr_setup_caps(drvdata, devid, id->data); + ret = tmc_etr_setup_caps(drvdata, devid, + coresight_get_uci_data(id)); if (ret) goto out; break; @@ -475,26 +476,13 @@ out: } static const struct amba_id tmc_ids[] = { - { - .id = 0x000bb961, - .mask = 0x000fffff, - }, - { - /* Coresight SoC 600 TMC-ETR/ETS */ - .id = 0x000bb9e8, - .mask = 0x000fffff, - .data = (void *)(unsigned long)CORESIGHT_SOC_600_ETR_CAPS, - }, - { - /* Coresight SoC 600 TMC-ETB */ - .id = 0x000bb9e9, - .mask = 0x000fffff, - }, - { - /* Coresight SoC 600 TMC-ETF */ - .id = 0x000bb9ea, - .mask = 0x000fffff, - }, + CS_AMBA_ID(0x000bb961), + /* Coresight SoC 600 TMC-ETR/ETS */ + CS_AMBA_ID_DATA(0x000bb9e8, (unsigned long)CORESIGHT_SOC_600_ETR_CAPS), + /* Coresight SoC 600 TMC-ETB */ + CS_AMBA_ID(0x000bb9e9), + /* Coresight SoC 600 TMC-ETF */ + CS_AMBA_ID(0x000bb9ea), { 0, 0}, }; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index ead5e7de3e4d..416f89b8f881 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -86,7 +86,6 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev) struct i2c_timings *t = &dev->timings; u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0; - dev->adapter.nr = -1; dev->tx_fifo_depth = 32; dev->rx_fifo_depth = 32; @@ -219,7 +218,7 @@ static void i2c_dw_configure_slave(struct dw_i2c_dev *dev) dev->mode = DW_IC_SLAVE; } -static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id) +static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev) { u32 param, tx_fifo_depth, rx_fifo_depth; @@ -233,7 +232,6 @@ static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id) if (!dev->tx_fifo_depth) { dev->tx_fifo_depth = tx_fifo_depth; dev->rx_fifo_depth = rx_fifo_depth; - dev->adapter.nr = id; } else if (tx_fifo_depth >= 2) { dev->tx_fifo_depth = min_t(u32, dev->tx_fifo_depth, tx_fifo_depth); @@ -358,13 +356,14 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000); } - dw_i2c_set_fifo_size(dev, pdev->id); + dw_i2c_set_fifo_size(dev); adap = &dev->adapter; adap->owner = THIS_MODULE; adap->class = I2C_CLASS_DEPRECATED; ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); adap->dev.of_node = pdev->dev.of_node; + adap->nr = -1; dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE | diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 660de1ee68ed..684d651612b3 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -503,7 +503,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG); writel(I2C_DMA_CON_RX, i2c->pdmabase + OFFSET_CON); - dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 0); + dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 1); if (!dma_rd_buf) return -ENOMEM; @@ -526,7 +526,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG); writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON); - dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 0); + dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1); if (!dma_wr_buf) return -ENOMEM; @@ -549,7 +549,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG); writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON); - dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 0); + dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1); if (!dma_wr_buf) return -ENOMEM; @@ -561,7 +561,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, return -ENOMEM; } - dma_rd_buf = i2c_get_dma_safe_msg_buf((msgs + 1), 0); + dma_rd_buf = i2c_get_dma_safe_msg_buf((msgs + 1), 1); if (!dma_rd_buf) { dma_unmap_single(i2c->dev, wpaddr, msgs->len, DMA_TO_DEVICE); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index dd52a068b140..a7578f6da979 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -363,9 +363,6 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE ? priv->dma_rx : priv->dma_tx; - /* Disable DMA Master Received/Transmitted */ - rcar_i2c_write(priv, ICDMAER, 0); - dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg), sg_dma_len(&priv->sg), priv->dma_direction); @@ -375,6 +372,9 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) priv->flags |= ID_P_NO_RXDMA; priv->dma_direction = DMA_NONE; + + /* Disable DMA Master Received/Transmitted, must be last! */ + rcar_i2c_write(priv, ICDMAER, 0); } static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv) @@ -611,6 +611,15 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) return true; } +/* + * This driver has a lock-free design because there are IP cores (at least + * R-Car Gen2) which have an inherent race condition in their hardware design. + * There, we need to clear RCAR_BUS_MASK_DATA bits as soon as possible after + * the interrupt was generated, otherwise an unwanted repeated message gets + * generated. It turned out that taking a spinlock at the beginning of the ISR + * was already causing repeated messages. Thus, this driver was converted to + * the now lockless behaviour. Please keep this in mind when hacking the driver. + */ static irqreturn_t rcar_i2c_irq(int irq, void *ptr) { struct rcar_i2c_priv *priv = ptr; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 1e6805b5cef2..a57aa4fe51a4 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -478,7 +478,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name)) { dev_err(&sis630_dev->dev, - "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n", + "I/O Region 0x%04x-0x%04x for SMBus already in use.\n", smbus_base + SMB_STS, smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); retval = -EBUSY; @@ -528,7 +528,7 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) sis630_adapter.dev.parent = &dev->dev; snprintf(sis630_adapter.name, sizeof(sis630_adapter.name), - "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS); + "SMBus SIS630 adapter at %04x", smbus_base + SMB_STS); return i2c_add_adapter(&sis630_adapter); } diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 13e1213561d4..4284fc991cfd 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -432,7 +432,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0); dnf_delay = setup->dnf * i2cclk; - sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min - + sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time - af_delay_min - (setup->dnf + 3) * i2cclk; sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time - diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index cb6c5cb0df0b..38af18645133 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -2258,7 +2258,8 @@ EXPORT_SYMBOL(i2c_put_adapter); /** * i2c_get_dma_safe_msg_buf() - get a DMA safe buffer for the given i2c_msg * @msg: the message to be checked - * @threshold: the minimum number of bytes for which using DMA makes sense + * @threshold: the minimum number of bytes for which using DMA makes sense. + * Should at least be 1. * * Return: NULL if a DMA safe buffer was not obtained. Use msg->buf with PIO. * Or a valid pointer to be used with DMA. After use, release it by @@ -2268,7 +2269,11 @@ EXPORT_SYMBOL(i2c_put_adapter); */ u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold) { - if (msg->len < threshold) + /* also skip 0-length msgs for bogus thresholds of 0 */ + if (!threshold) + pr_debug("DMA buffer for addr=0x%02x with length 0 is bogus\n", + msg->addr); + if (msg->len < threshold || msg->len == 0) return NULL; if (msg->flags & I2C_M_DMA_SAFE) diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 901b8833847f..19fcd0756f46 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -320,9 +320,9 @@ config BLK_DEV_OFFBOARD config BLK_DEV_GENERIC tristate "Generic PCI IDE Chipset Support" select BLK_DEV_IDEPCI - help - This option provides generic support for various PCI IDE Chipsets - which otherwise might not be supported. + help + This option provides generic support for various PCI IDE Chipsets + which otherwise might not be supported. config BLK_DEV_OPTI621 tristate "OPTi 82C621 chipset enhanced support" @@ -516,7 +516,7 @@ config BLK_DEV_IT8213 tristate "IT8213 IDE support" select BLK_DEV_IDEDMA_PCI help - This driver adds support for the ITE 8213 IDE controller. + This driver adds support for the ITE 8213 IDE controller. config BLK_DEV_IT821X tristate "IT821X IDE support" @@ -671,20 +671,20 @@ config BLK_DEV_IDE_PMAC_ATA100FIRST hard disk and hdc for CD-ROM. config BLK_DEV_IDE_AU1XXX - bool "IDE for AMD Alchemy Au1200" - depends on MIPS_ALCHEMY - select IDE_XFER_MODE + bool "IDE for AMD Alchemy Au1200" + depends on MIPS_ALCHEMY + select IDE_XFER_MODE choice - prompt "IDE Mode for AMD Alchemy Au1200" - default BLK_DEV_IDE_AU1XXX_PIO_DBDMA - depends on BLK_DEV_IDE_AU1XXX + prompt "IDE Mode for AMD Alchemy Au1200" + default BLK_DEV_IDE_AU1XXX_PIO_DBDMA + depends on BLK_DEV_IDE_AU1XXX config BLK_DEV_IDE_AU1XXX_PIO_DBDMA - bool "PIO+DbDMA IDE for AMD Alchemy Au1200" + bool "PIO+DbDMA IDE for AMD Alchemy Au1200" config BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - bool "MDMA2+DbDMA IDE for AMD Alchemy Au1200" - depends on BLK_DEV_IDE_AU1XXX + bool "MDMA2+DbDMA IDE for AMD Alchemy Au1200" + depends on BLK_DEV_IDE_AU1XXX endchoice config BLK_DEV_IDE_TX4938 diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c index 4d565b0c5a6e..0a3f9bcc8b04 100644 --- a/drivers/ide/hpt366.c +++ b/drivers/ide/hpt366.c @@ -574,7 +574,7 @@ static u8 hpt3xx_udma_filter(ide_drive_t *drive) if (!HPT370_ALLOW_ATA100_5 || check_in_drive_list(drive, bad_ata100_5)) return ATA_UDMA4; - /* else: fall through */ + /* fall through */ case HPT372 : case HPT372A: case HPT372N: @@ -601,7 +601,7 @@ static u8 hpt3xx_mdma_filter(ide_drive_t *drive) case HPT374 : if (ata_id_is_sata(drive->id)) return 0x00; - /* else: fall through */ + /* fall through */ default: return 0x07; } diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 780d33ccc5d8..1ea2f9e82bf8 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -427,7 +427,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) * (maintains previous driver behaviour) */ break; - /* else: fall through */ + /* fall through */ case CAPACITY_CURRENT: /* Normal Zip/LS-120 disks */ if (memcmp(cap_desc, &floppy->cap_desc, 8)) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index a9f29156e486..7421ec4883fb 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -668,19 +668,10 @@ static int assign_name(struct ib_device *device, const char *name) } strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX); - /* Cyclically allocate a user visible ID for the device */ - device->index = last_id; - ret = xa_alloc(&devices, &device->index, INT_MAX, device, GFP_KERNEL); - if (ret == -ENOSPC) { - device->index = 0; - ret = xa_alloc(&devices, &device->index, INT_MAX, device, - GFP_KERNEL); - } - if (ret) - goto out; - last_id = device->index + 1; - - ret = 0; + ret = xa_alloc_cyclic(&devices, &device->index, device, xa_limit_31b, + &last_id, GFP_KERNEL); + if (ret > 0) + ret = 0; out: up_write(&devices_rwsem); @@ -1059,14 +1050,15 @@ static int assign_client_id(struct ib_client *client) * to get the LIFO order. The extra linked list can go away if xarray * learns to reverse iterate. */ - if (list_empty(&client_list)) + if (list_empty(&client_list)) { client->client_id = 0; - else - client->client_id = - list_last_entry(&client_list, struct ib_client, list) - ->client_id; - ret = xa_alloc(&clients, &client->client_id, INT_MAX, client, - GFP_KERNEL); + } else { + struct ib_client *last; + + last = list_last_entry(&client_list, struct ib_client, list); + client->client_id = last->client_id + 1; + } + ret = xa_insert(&clients, client->client_id, client, GFP_KERNEL); if (ret) goto out; diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index fa804093fafb..3b5ff2f7b5f8 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -13,28 +13,6 @@ #include "cma_priv.h" #include "restrack.h" -static int rt_xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, - u32 *next) -{ - int err; - - *id = *next; - if (*next == U32_MAX) - *id = 0; - - xa_lock(xa); - err = __xa_alloc(xa, id, U32_MAX, entry, GFP_KERNEL); - if (err && *next != U32_MAX) { - *id = 0; - err = __xa_alloc(xa, id, *next, entry, GFP_KERNEL); - } - - if (!err) - *next = *id + 1; - xa_unlock(xa); - return err; -} - /** * rdma_restrack_init() - initialize and allocate resource tracking * @dev: IB device @@ -226,7 +204,8 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res) kref_init(&res->kref); init_completion(&res->comp); if (res->type != RDMA_RESTRACK_QP) - ret = rt_xa_alloc_cyclic(&rt->xa, &res->id, res, &rt->next_id); + ret = xa_alloc_cyclic(&rt->xa, &res->id, res, xa_limit_32b, + &rt->next_id, GFP_KERNEL); else { /* Special case to ensure that LQPN points to right QP */ struct ib_qp *qp = container_of(res, struct ib_qp, res); diff --git a/drivers/infiniband/hw/i40iw/i40iw_utils.c b/drivers/infiniband/hw/i40iw/i40iw_utils.c index c5a881172524..337410f40860 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_utils.c +++ b/drivers/infiniband/hw/i40iw/i40iw_utils.c @@ -173,7 +173,12 @@ int i40iw_inetaddr_event(struct notifier_block *notifier, rcu_read_lock(); in = __in_dev_get_rcu(upper_dev); - local_ipaddr = ntohl(in->ifa_list->ifa_address); + + if (!in->ifa_list) + local_ipaddr = 0; + else + local_ipaddr = ntohl(in->ifa_list->ifa_address); + rcu_read_unlock(); } else { local_ipaddr = ntohl(ifa->ifa_address); @@ -185,6 +190,11 @@ int i40iw_inetaddr_event(struct notifier_block *notifier, case NETDEV_UP: /* Fall through */ case NETDEV_CHANGEADDR: + + /* Just skip if no need to handle ARP cache */ + if (!local_ipaddr) + break; + i40iw_manage_arp_cache(iwdev, netdev->dev_addr, &local_ipaddr, diff --git a/drivers/infiniband/hw/mlx4/alias_GUID.c b/drivers/infiniband/hw/mlx4/alias_GUID.c index 782499abcd98..2a0b59a4b6eb 100644 --- a/drivers/infiniband/hw/mlx4/alias_GUID.c +++ b/drivers/infiniband/hw/mlx4/alias_GUID.c @@ -804,8 +804,8 @@ void mlx4_ib_destroy_alias_guid_service(struct mlx4_ib_dev *dev) unsigned long flags; for (i = 0 ; i < dev->num_ports; i++) { - cancel_delayed_work(&dev->sriov.alias_guid.ports_guid[i].alias_guid_work); det = &sriov->alias_guid.ports_guid[i]; + cancel_delayed_work_sync(&det->alias_guid_work); spin_lock_irqsave(&sriov->alias_guid.ag_work_lock, flags); while (!list_empty(&det->cb_list)) { cb_ctx = list_entry(det->cb_list.next, diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index eaa055007f28..9e08df7914aa 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -20,6 +20,7 @@ enum devx_obj_flags { DEVX_OBJ_FLAGS_INDIRECT_MKEY = 1 << 0, + DEVX_OBJ_FLAGS_DCT = 1 << 1, }; struct devx_async_data { @@ -39,7 +40,10 @@ struct devx_obj { u32 dinlen; /* destroy inbox length */ u32 dinbox[MLX5_MAX_DESTROY_INBOX_SIZE_DW]; u32 flags; - struct mlx5_ib_devx_mr devx_mr; + union { + struct mlx5_ib_devx_mr devx_mr; + struct mlx5_core_dct core_dct; + }; }; struct devx_umem { @@ -347,7 +351,6 @@ static u64 devx_get_obj_id(const void *in) obj_id = get_enc_obj_id(MLX5_CMD_OP_CREATE_RQ, MLX5_GET(arm_rq_in, in, srq_number)); break; - case MLX5_CMD_OP_DRAIN_DCT: case MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION: obj_id = get_enc_obj_id(MLX5_CMD_OP_CREATE_DCT, MLX5_GET(drain_dct_in, in, dctn)); @@ -618,7 +621,6 @@ static bool devx_is_obj_modify_cmd(const void *in) case MLX5_CMD_OP_2RST_QP: case MLX5_CMD_OP_ARM_XRC_SRQ: case MLX5_CMD_OP_ARM_RQ: - case MLX5_CMD_OP_DRAIN_DCT: case MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION: case MLX5_CMD_OP_ARM_XRQ: case MLX5_CMD_OP_SET_XRQ_DC_PARAMS_ENTRY: @@ -1124,7 +1126,11 @@ static int devx_obj_cleanup(struct ib_uobject *uobject, if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) devx_cleanup_mkey(obj); - ret = mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, sizeof(out)); + if (obj->flags & DEVX_OBJ_FLAGS_DCT) + ret = mlx5_core_destroy_dct(obj->mdev, &obj->core_dct); + else + ret = mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, + sizeof(out)); if (ib_is_destroy_retryable(ret, why, uobject)) return ret; @@ -1185,9 +1191,17 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( devx_set_umem_valid(cmd_in); } - err = mlx5_cmd_exec(dev->mdev, cmd_in, - cmd_in_len, - cmd_out, cmd_out_len); + if (opcode == MLX5_CMD_OP_CREATE_DCT) { + obj->flags |= DEVX_OBJ_FLAGS_DCT; + err = mlx5_core_create_dct(dev->mdev, &obj->core_dct, + cmd_in, cmd_in_len, + cmd_out, cmd_out_len); + } else { + err = mlx5_cmd_exec(dev->mdev, cmd_in, + cmd_in_len, + cmd_out, cmd_out_len); + } + if (err) goto obj_free; @@ -1214,7 +1228,11 @@ err_copy: if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) devx_cleanup_mkey(obj); obj_destroy: - mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, sizeof(out)); + if (obj->flags & DEVX_OBJ_FLAGS_DCT) + mlx5_core_destroy_dct(obj->mdev, &obj->core_dct); + else + mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, + sizeof(out)); obj_free: kfree(obj); return err; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 994c19d01211..531ff20b32ad 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -415,10 +415,17 @@ static int translate_eth_ext_proto_oper(u32 eth_proto_oper, u8 *active_speed, *active_speed = IB_SPEED_EDR; break; case MLX5E_PROT_MASK(MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2): + *active_width = IB_WIDTH_2X; + *active_speed = IB_SPEED_EDR; + break; case MLX5E_PROT_MASK(MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR): *active_width = IB_WIDTH_1X; *active_speed = IB_SPEED_HDR; break; + case MLX5E_PROT_MASK(MLX5E_CAUI_4_100GBASE_CR4_KR4): + *active_width = IB_WIDTH_4X; + *active_speed = IB_SPEED_EDR; + break; case MLX5E_PROT_MASK(MLX5E_100GAUI_2_100GBASE_CR2_KR2): *active_width = IB_WIDTH_2X; *active_speed = IB_SPEED_HDR; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 6b1f0e76900b..7cd006da1dae 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -3729,6 +3729,7 @@ static int mlx5_ib_modify_dct(struct ib_qp *ibqp, struct ib_qp_attr *attr, } else if (cur_state == IB_QPS_INIT && new_state == IB_QPS_RTR) { struct mlx5_ib_modify_qp_resp resp = {}; + u32 out[MLX5_ST_SZ_DW(create_dct_out)] = {0}; u32 min_resp_len = offsetof(typeof(resp), dctn) + sizeof(resp.dctn); @@ -3747,7 +3748,8 @@ static int mlx5_ib_modify_dct(struct ib_qp *ibqp, struct ib_qp_attr *attr, MLX5_SET(dctc, dctc, hop_limit, attr->ah_attr.grh.hop_limit); err = mlx5_core_create_dct(dev->mdev, &qp->dct.mdct, qp->dct.in, - MLX5_ST_SZ_BYTES(create_dct_in)); + MLX5_ST_SZ_BYTES(create_dct_in), out, + sizeof(out)); if (err) return err; resp.dctn = qp->dct.mdct.mqp.qpn; diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index 804b1b80a8be..5a52b65bef9a 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -259,7 +259,7 @@ static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char db9_saturn_write_sub(port, type, 3, powered, 0); return data[0] = 0xe3; } - /* else: fall through */ + /* fall through */ default: return data[0]; } diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 492a971b95b5..6cd199e8a370 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -1015,8 +1015,18 @@ static int __maybe_unused gpio_keys_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); +static void gpio_keys_shutdown(struct platform_device *pdev) +{ + int ret; + + ret = gpio_keys_suspend(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "failed to shutdown\n"); +} + static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, + .shutdown = gpio_keys_shutdown, .driver = { .name = "gpio-keys", .pm = &gpio_keys_pm_ops, diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index be56d4f262a7..b132662201a4 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -113,9 +113,8 @@ static int mcs_touchkey_probe(struct i2c_client *client, return -EINVAL; } - data = kzalloc(sizeof(struct mcs_touchkey_data) + - sizeof(data->keycodes[0]) * (pdata->key_maxval + 1), - GFP_KERNEL); + data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1), + GFP_KERNEL); input_dev = input_allocate_device(); if (!data || !input_dev) { dev_err(&client->dev, "Failed to allocate memory\n"); diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index 02c67a1749fc..8e6ebab05ab4 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -14,18 +14,17 @@ * */ -#include <linux/module.h> -#include <linux/kernel.h> #include <linux/input.h> #include <linux/interrupt.h> -#include <linux/platform_device.h> #include <linux/kernel.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/regmap.h> #include <linux/mfd/mt6323/registers.h> -#include <linux/mfd/mt6397/registers.h> #include <linux/mfd/mt6397/core.h> +#include <linux/mfd/mt6397/registers.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> #define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1 #define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6 diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index d466bc07aebb..6a43895b28e7 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -68,7 +68,6 @@ struct qt2160_data { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; - spinlock_t lock; /* Protects canceling/rescheduling of dwork */ unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; u16 key_matrix; #ifdef CONFIG_LEDS_CLASS @@ -212,22 +211,15 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160) static irqreturn_t qt2160_irq(int irq, void *_qt2160) { struct qt2160_data *qt2160 = _qt2160; - unsigned long flags; - - spin_lock_irqsave(&qt2160->lock, flags); mod_delayed_work(system_wq, &qt2160->dwork, 0); - spin_unlock_irqrestore(&qt2160->lock, flags); - return IRQ_HANDLED; } static void qt2160_schedule_read(struct qt2160_data *qt2160) { - spin_lock_irq(&qt2160->lock); schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); - spin_unlock_irq(&qt2160->lock); } static void qt2160_worker(struct work_struct *work) @@ -391,7 +383,6 @@ static int qt2160_probe(struct i2c_client *client, qt2160->client = client; qt2160->input = input; INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); - spin_lock_init(&qt2160->lock); input->name = "AT42QT2160 Touch Sense Keyboard"; input->id.bustype = BUS_I2C; diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index dc983ab6c0ad..cdeef180aead 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -219,9 +219,7 @@ static int tca6416_keypad_probe(struct i2c_client *client, return -EINVAL; } - chip = kzalloc(sizeof(struct tca6416_keypad_chip) + - pdata->nbuttons * sizeof(struct tca6416_button), - GFP_KERNEL); + chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL); input = input_allocate_device(); if (!chip || !input) { error = -ENOMEM; diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index abc266e40e17..d4455f3a5cf1 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -22,12 +22,14 @@ #include <linux/leds.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/pm.h> #include <linux/regulator/consumer.h> #define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey" -#define TM2_TOUCHKEY_KEYCODE_REG 0x03 -#define TM2_TOUCHKEY_BASE_REG 0x00 + +#define ARIES_TOUCHKEY_CMD_LED_ON 0x1 +#define ARIES_TOUCHKEY_CMD_LED_OFF 0x2 #define TM2_TOUCHKEY_CMD_LED_ON 0x10 #define TM2_TOUCHKEY_CMD_LED_OFF 0x20 #define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3) @@ -35,9 +37,13 @@ #define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000 #define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000 -enum { - TM2_TOUCHKEY_KEY_MENU = 0x1, - TM2_TOUCHKEY_KEY_BACK, +struct touchkey_variant { + u8 keycode_reg; + u8 base_reg; + u8 cmd_led_on; + u8 cmd_led_off; + bool no_reg; + bool fixed_regulator; }; struct tm2_touchkey_data { @@ -46,9 +52,33 @@ struct tm2_touchkey_data { struct led_classdev led_dev; struct regulator *vdd; struct regulator_bulk_data regulators[2]; + const struct touchkey_variant *variant; + u32 keycodes[4]; + int num_keycodes; +}; + +static const struct touchkey_variant tm2_touchkey_variant = { + .keycode_reg = 0x03, + .base_reg = 0x00, + .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON, + .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF, +}; + +static const struct touchkey_variant midas_touchkey_variant = { + .keycode_reg = 0x00, + .base_reg = 0x00, + .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON, + .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF, }; -static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, +static struct touchkey_variant aries_touchkey_variant = { + .no_reg = true, + .fixed_regulator = true, + .cmd_led_on = ARIES_TOUCHKEY_CMD_LED_ON, + .cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF, +}; + +static int tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct tm2_touchkey_data *touchkey = @@ -58,15 +88,19 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, if (brightness == LED_OFF) { volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN; - data = TM2_TOUCHKEY_CMD_LED_OFF; + data = touchkey->variant->cmd_led_off; } else { volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX; - data = TM2_TOUCHKEY_CMD_LED_ON; + data = touchkey->variant->cmd_led_on; } - regulator_set_voltage(touchkey->vdd, volt, volt); - i2c_smbus_write_byte_data(touchkey->client, - TM2_TOUCHKEY_BASE_REG, data); + if (!touchkey->variant->fixed_regulator) + regulator_set_voltage(touchkey->vdd, volt, volt); + + return touchkey->variant->no_reg ? + i2c_smbus_write_byte(touchkey->client, data) : + i2c_smbus_write_byte_data(touchkey->client, + touchkey->variant->base_reg, data); } static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey) @@ -96,49 +130,57 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) { struct tm2_touchkey_data *touchkey = devid; int data; - int key; - - data = i2c_smbus_read_byte_data(touchkey->client, - TM2_TOUCHKEY_KEYCODE_REG); + int index; + int i; + + if (touchkey->variant->no_reg) + data = i2c_smbus_read_byte(touchkey->client); + else + data = i2c_smbus_read_byte_data(touchkey->client, + touchkey->variant->keycode_reg); if (data < 0) { dev_err(&touchkey->client->dev, "failed to read i2c data: %d\n", data); goto out; } - switch (data & TM2_TOUCHKEY_BIT_KEYCODE) { - case TM2_TOUCHKEY_KEY_MENU: - key = KEY_PHONE; - break; - - case TM2_TOUCHKEY_KEY_BACK: - key = KEY_BACK; - break; - - default: + index = (data & TM2_TOUCHKEY_BIT_KEYCODE) - 1; + if (index < 0 || index >= touchkey->num_keycodes) { dev_warn(&touchkey->client->dev, - "unhandled keycode, data %#02x\n", data); + "invalid keycode index %d\n", index); goto out; } if (data & TM2_TOUCHKEY_BIT_PRESS_EV) { - input_report_key(touchkey->input_dev, KEY_PHONE, 0); - input_report_key(touchkey->input_dev, KEY_BACK, 0); + for (i = 0; i < touchkey->num_keycodes; i++) + input_report_key(touchkey->input_dev, + touchkey->keycodes[i], 0); } else { - input_report_key(touchkey->input_dev, key, 1); + input_report_key(touchkey->input_dev, + touchkey->keycodes[index], 1); } input_sync(touchkey->input_dev); out: + if (touchkey->variant->fixed_regulator && + data & TM2_TOUCHKEY_BIT_PRESS_EV) { + /* touch turns backlight on, so make sure we're in sync */ + if (touchkey->led_dev.brightness == LED_OFF) + tm2_touchkey_led_brightness_set(&touchkey->led_dev, + LED_OFF); + } + return IRQ_HANDLED; } static int tm2_touchkey_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device_node *np = client->dev.of_node; struct tm2_touchkey_data *touchkey; int error; + int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -153,6 +195,8 @@ static int tm2_touchkey_probe(struct i2c_client *client, touchkey->client = client; i2c_set_clientdata(client, touchkey); + touchkey->variant = of_device_get_match_data(&client->dev); + touchkey->regulators[0].supply = "vcc"; touchkey->regulators[1].supply = "vdd"; error = devm_regulator_bulk_get(&client->dev, @@ -166,6 +210,16 @@ static int tm2_touchkey_probe(struct i2c_client *client, /* Save VDD for easy access */ touchkey->vdd = touchkey->regulators[1].consumer; + touchkey->num_keycodes = of_property_read_variable_u32_array(np, + "linux,keycodes", touchkey->keycodes, 0, + ARRAY_SIZE(touchkey->keycodes)); + if (touchkey->num_keycodes <= 0) { + /* default keycodes */ + touchkey->keycodes[0] = KEY_PHONE; + touchkey->keycodes[1] = KEY_BACK; + touchkey->num_keycodes = 2; + } + error = tm2_touchkey_power_enable(touchkey); if (error) { dev_err(&client->dev, "failed to power up device: %d\n", error); @@ -190,8 +244,9 @@ static int tm2_touchkey_probe(struct i2c_client *client, touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME; touchkey->input_dev->id.bustype = BUS_I2C; - input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE); - input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK); + for (i = 0; i < touchkey->num_keycodes; i++) + input_set_capability(touchkey->input_dev, EV_KEY, + touchkey->keycodes[i]); error = input_register_device(touchkey->input_dev); if (error) { @@ -212,9 +267,10 @@ static int tm2_touchkey_probe(struct i2c_client *client, /* led device */ touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME; - touchkey->led_dev.brightness = LED_FULL; + touchkey->led_dev.brightness = LED_ON; touchkey->led_dev.max_brightness = LED_ON; - touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set; + touchkey->led_dev.brightness_set_blocking = + tm2_touchkey_led_brightness_set; error = devm_led_classdev_register(&client->dev, &touchkey->led_dev); if (error) { @@ -223,6 +279,9 @@ static int tm2_touchkey_probe(struct i2c_client *client, return error; } + if (touchkey->variant->fixed_regulator) + tm2_touchkey_led_brightness_set(&touchkey->led_dev, LED_ON); + return 0; } @@ -262,7 +321,16 @@ static const struct i2c_device_id tm2_touchkey_id_table[] = { MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table); static const struct of_device_id tm2_touchkey_of_match[] = { - { .compatible = "cypress,tm2-touchkey", }, + { + .compatible = "cypress,tm2-touchkey", + .data = &tm2_touchkey_variant, + }, { + .compatible = "cypress,midas-touchkey", + .data = &midas_touchkey_variant, + }, { + .compatible = "cypress,aries-touchkey", + .data = &aries_touchkey_variant, + }, { }, }; MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 279fb02a0f14..e15ed1bb8558 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -117,6 +117,16 @@ config INPUT_E3X0_BUTTON To compile this driver as a module, choose M here: the module will be called e3x0_button. +config INPUT_MSM_VIBRATOR + tristate "Qualcomm MSM vibrator driver" + select INPUT_FF_MEMLESS + help + Support for the vibrator that is found on various Qualcomm MSM + SOCs. + + To compile this driver as a module, choose M here: the module + will be called msm_vibrator. + config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1b44202ad8f7..b936c5b1d4ac 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o +obj-$(CONFIG_INPUT_MSM_VIBRATOR) += msm-vibrator.o obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 3d51175c4d72..74cf3b612f05 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -39,8 +39,6 @@ struct ims_pcu_gamepad { struct ims_pcu_backlight { struct led_classdev cdev; - struct work_struct work; - enum led_brightness desired_brightness; char name[32]; }; @@ -949,14 +947,14 @@ out: #define IMS_PCU_MAX_BRIGHTNESS 31998 -static void ims_pcu_backlight_work(struct work_struct *work) +static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev, + enum led_brightness value) { struct ims_pcu_backlight *backlight = - container_of(work, struct ims_pcu_backlight, work); + container_of(cdev, struct ims_pcu_backlight, cdev); struct ims_pcu *pcu = container_of(backlight, struct ims_pcu, backlight); - int desired_brightness = backlight->desired_brightness; - __le16 br_val = cpu_to_le16(desired_brightness); + __le16 br_val = cpu_to_le16(value); int error; mutex_lock(&pcu->cmd_mutex); @@ -966,19 +964,11 @@ static void ims_pcu_backlight_work(struct work_struct *work) if (error && error != -ENODEV) dev_warn(pcu->dev, "Failed to set desired brightness %u, error: %d\n", - desired_brightness, error); + value, error); mutex_unlock(&pcu->cmd_mutex); -} -static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev, - enum led_brightness value) -{ - struct ims_pcu_backlight *backlight = - container_of(cdev, struct ims_pcu_backlight, cdev); - - backlight->desired_brightness = value; - schedule_work(&backlight->work); + return error; } static enum led_brightness @@ -1015,14 +1005,14 @@ static int ims_pcu_setup_backlight(struct ims_pcu *pcu) struct ims_pcu_backlight *backlight = &pcu->backlight; int error; - INIT_WORK(&backlight->work, ims_pcu_backlight_work); snprintf(backlight->name, sizeof(backlight->name), "pcu%d::kbd_backlight", pcu->device_no); backlight->cdev.name = backlight->name; backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS; backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness; - backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness; + backlight->cdev.brightness_set_blocking = + ims_pcu_backlight_set_brightness; error = led_classdev_register(pcu->dev, &backlight->cdev); if (error) { @@ -1040,7 +1030,6 @@ static void ims_pcu_destroy_backlight(struct ims_pcu *pcu) struct ims_pcu_backlight *backlight = &pcu->backlight; led_classdev_unregister(&backlight->cdev); - cancel_work_sync(&backlight->work); } diff --git a/drivers/input/misc/msm-vibrator.c b/drivers/input/misc/msm-vibrator.c new file mode 100644 index 000000000000..b60f1aaee705 --- /dev/null +++ b/drivers/input/misc/msm-vibrator.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm MSM vibrator driver + * + * Copyright (c) 2018 Brian Masney <masneyb@onstation.org> + * + * Based on qcom,pwm-vibrator.c from: + * Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca> + * + * Based on msm_pwm_vibrator.c from downstream Android sources: + * Copyright (C) 2009-2014 LGE, Inc. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#define REG_CMD_RCGR 0x00 +#define REG_CFG_RCGR 0x04 +#define REG_M 0x08 +#define REG_N 0x0C +#define REG_D 0x10 +#define REG_CBCR 0x24 +#define MMSS_CC_M_DEFAULT 1 + +struct msm_vibrator { + struct input_dev *input; + struct mutex mutex; + struct work_struct worker; + void __iomem *base; + struct regulator *vcc; + struct clk *clk; + struct gpio_desc *enable_gpio; + u16 magnitude; + bool enabled; +}; + +static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset, + u32 value) +{ + writel(value, vibrator->base + offset); +} + +static int msm_vibrator_start(struct msm_vibrator *vibrator) +{ + int d_reg_val, ret = 0; + + mutex_lock(&vibrator->mutex); + + if (!vibrator->enabled) { + ret = clk_set_rate(vibrator->clk, 24000); + if (ret) { + dev_err(&vibrator->input->dev, + "Failed to set clock rate: %d\n", ret); + goto unlock; + } + + ret = clk_prepare_enable(vibrator->clk); + if (ret) { + dev_err(&vibrator->input->dev, + "Failed to enable clock: %d\n", ret); + goto unlock; + } + + ret = regulator_enable(vibrator->vcc); + if (ret) { + dev_err(&vibrator->input->dev, + "Failed to enable regulator: %d\n", ret); + clk_disable(vibrator->clk); + goto unlock; + } + + gpiod_set_value_cansleep(vibrator->enable_gpio, 1); + + vibrator->enabled = true; + } + + d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff); + msm_vibrator_write(vibrator, REG_CFG_RCGR, + (2 << 12) | /* dual edge mode */ + (0 << 8) | /* cxo */ + (7 << 0)); + msm_vibrator_write(vibrator, REG_M, 1); + msm_vibrator_write(vibrator, REG_N, 128); + msm_vibrator_write(vibrator, REG_D, d_reg_val); + msm_vibrator_write(vibrator, REG_CMD_RCGR, 1); + msm_vibrator_write(vibrator, REG_CBCR, 1); + +unlock: + mutex_unlock(&vibrator->mutex); + + return ret; +} + +static void msm_vibrator_stop(struct msm_vibrator *vibrator) +{ + mutex_lock(&vibrator->mutex); + + if (vibrator->enabled) { + gpiod_set_value_cansleep(vibrator->enable_gpio, 0); + regulator_disable(vibrator->vcc); + clk_disable(vibrator->clk); + vibrator->enabled = false; + } + + mutex_unlock(&vibrator->mutex); +} + +static void msm_vibrator_worker(struct work_struct *work) +{ + struct msm_vibrator *vibrator = container_of(work, + struct msm_vibrator, + worker); + + if (vibrator->magnitude) + msm_vibrator_start(vibrator); + else + msm_vibrator_stop(vibrator); +} + +static int msm_vibrator_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct msm_vibrator *vibrator = input_get_drvdata(dev); + + mutex_lock(&vibrator->mutex); + + if (effect->u.rumble.strong_magnitude > 0) + vibrator->magnitude = effect->u.rumble.strong_magnitude; + else + vibrator->magnitude = effect->u.rumble.weak_magnitude; + + mutex_unlock(&vibrator->mutex); + + schedule_work(&vibrator->worker); + + return 0; +} + +static void msm_vibrator_close(struct input_dev *input) +{ + struct msm_vibrator *vibrator = input_get_drvdata(input); + + cancel_work_sync(&vibrator->worker); + msm_vibrator_stop(vibrator); +} + +static int msm_vibrator_probe(struct platform_device *pdev) +{ + struct msm_vibrator *vibrator; + struct resource *res; + int ret; + + vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL); + if (!vibrator) + return -ENOMEM; + + vibrator->input = devm_input_allocate_device(&pdev->dev); + if (!vibrator->input) + return -ENOMEM; + + vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(vibrator->vcc)) { + if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get regulator: %ld\n", + PTR_ERR(vibrator->vcc)); + return PTR_ERR(vibrator->vcc); + } + + vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(vibrator->enable_gpio)) { + if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n", + PTR_ERR(vibrator->enable_gpio)); + return PTR_ERR(vibrator->enable_gpio); + } + + vibrator->clk = devm_clk_get(&pdev->dev, "pwm"); + if (IS_ERR(vibrator->clk)) { + if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n", + PTR_ERR(vibrator->clk)); + return PTR_ERR(vibrator->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + + vibrator->base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!vibrator->base) { + dev_err(&pdev->dev, "Failed to iomap resource.\n"); + return -ENOMEM; + } + + vibrator->enabled = false; + mutex_init(&vibrator->mutex); + INIT_WORK(&vibrator->worker, msm_vibrator_worker); + + vibrator->input->name = "msm-vibrator"; + vibrator->input->id.bustype = BUS_HOST; + vibrator->input->close = msm_vibrator_close; + + input_set_drvdata(vibrator->input, vibrator); + input_set_capability(vibrator->input, EV_FF, FF_RUMBLE); + + ret = input_ff_create_memless(vibrator->input, NULL, + msm_vibrator_play_effect); + if (ret) { + dev_err(&pdev->dev, "Failed to create ff memless: %d", ret); + return ret; + } + + ret = input_register_device(vibrator->input); + if (ret) { + dev_err(&pdev->dev, "Failed to register input device: %d", ret); + return ret; + } + + platform_set_drvdata(pdev, vibrator); + + return 0; +} + +static int __maybe_unused msm_vibrator_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_vibrator *vibrator = platform_get_drvdata(pdev); + + cancel_work_sync(&vibrator->worker); + + if (vibrator->enabled) + msm_vibrator_stop(vibrator); + + return 0; +} + +static int __maybe_unused msm_vibrator_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_vibrator *vibrator = platform_get_drvdata(pdev); + + if (vibrator->enabled) + msm_vibrator_start(vibrator); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend, + msm_vibrator_resume); + +static const struct of_device_id msm_vibrator_of_match[] = { + { .compatible = "qcom,msm8226-vibrator" }, + { .compatible = "qcom,msm8974-vibrator" }, + {}, +}; +MODULE_DEVICE_TABLE(of, msm_vibrator_of_match); + +static struct platform_driver msm_vibrator_driver = { + .probe = msm_vibrator_probe, + .driver = { + .name = "msm-vibrator", + .pm = &msm_vibrator_pm_ops, + .of_match_table = of_match_ptr(msm_vibrator_of_match), + }, +}; +module_platform_driver(msm_vibrator_driver); + +MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>"); +MODULE_DESCRIPTION("Qualcomm MSM vibrator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 23520df7650f..bb458beecb43 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -185,6 +185,10 @@ static int soc_button_parse_btn_desc(struct device *dev, info->name = "power"; info->event_code = KEY_POWER; info->wakeup = true; + } else if (upage == 0x01 && usage == 0xca) { + info->name = "rotation lock switch"; + info->event_type = EV_SW; + info->event_code = SW_ROTATE_LOCK; } else if (upage == 0x07 && usage == 0xe3) { info->name = "home"; info->event_code = KEY_LEFTMETA; @@ -373,7 +377,7 @@ static struct soc_button_info soc_button_PNP0C40[] = { { "home", 1, EV_KEY, KEY_LEFTMETA, false, true }, { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false }, - { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false }, + { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false }, { } }; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 225ae6980182..628ef617bb2f 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1337,6 +1337,7 @@ static const struct acpi_device_id elan_acpi_id[] = { { "ELAN0000", 0 }, { "ELAN0100", 0 }, { "ELAN0600", 0 }, + { "ELAN0601", 0 }, { "ELAN0602", 0 }, { "ELAN0605", 0 }, { "ELAN0608", 0 }, diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 8538318d332c..fa304648d611 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -219,7 +219,6 @@ struct synaptics_i2c { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; - spinlock_t lock; int no_data_count; int no_decel_param; int reduce_report_param; @@ -369,23 +368,11 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) return xy_delta || gesture; } -static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch, - unsigned long delay) -{ - unsigned long flags; - - spin_lock_irqsave(&touch->lock, flags); - - mod_delayed_work(system_wq, &touch->dwork, delay); - - spin_unlock_irqrestore(&touch->lock, flags); -} - static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) { struct synaptics_i2c *touch = dev_id; - synaptics_i2c_reschedule_work(touch, 0); + mod_delayed_work(system_wq, &touch->dwork, 0); return IRQ_HANDLED; } @@ -461,7 +448,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work) * We poll the device once in THREAD_IRQ_SLEEP_SECS and * if error is detected, we try to reset and reconfigure the touchpad. */ - synaptics_i2c_reschedule_work(touch, delay); + mod_delayed_work(system_wq, &touch->dwork, delay); } static int synaptics_i2c_open(struct input_dev *input) @@ -474,7 +461,7 @@ static int synaptics_i2c_open(struct input_dev *input) return ret; if (polling_req) - synaptics_i2c_reschedule_work(touch, + mod_delayed_work(system_wq, &touch->dwork, msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; @@ -530,7 +517,6 @@ static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *clien touch->scan_rate_param = scan_rate; set_scan_rate(touch, scan_rate); INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); - spin_lock_init(&touch->lock); return touch; } @@ -637,7 +623,7 @@ static int __maybe_unused synaptics_i2c_resume(struct device *dev) if (ret) return ret; - synaptics_i2c_reschedule_work(touch, + mod_delayed_work(system_wq, &touch->dwork, msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index 796289846204..fce76812843b 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -53,12 +53,11 @@ static struct resource *kbd_res; static int sparc_i8042_probe(struct platform_device *op) { - struct device_node *dp = op->dev.of_node; + struct device_node *dp; - dp = dp->child; - while (dp) { - if (!strcmp(dp->name, OBP_PS2KBD_NAME1) || - !strcmp(dp->name, OBP_PS2KBD_NAME2)) { + for_each_child_of_node(op->dev.of_node, dp) { + if (of_node_name_eq(dp, OBP_PS2KBD_NAME1) || + of_node_name_eq(dp, OBP_PS2KBD_NAME2)) { struct platform_device *kbd = of_find_device_by_node(dp); unsigned int irq = kbd->archdata.irqs[0]; if (irq == 0xffffffff) @@ -67,16 +66,14 @@ static int sparc_i8042_probe(struct platform_device *op) kbd_iobase = of_ioremap(&kbd->resource[0], 0, 8, "kbd"); kbd_res = &kbd->resource[0]; - } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) || - !strcmp(dp->name, OBP_PS2MS_NAME2)) { + } else if (of_node_name_eq(dp, OBP_PS2MS_NAME1) || + of_node_name_eq(dp, OBP_PS2MS_NAME2)) { struct platform_device *ms = of_find_device_by_node(dp); unsigned int irq = ms->archdata.irqs[0]; if (irq == 0xffffffff) irq = op->archdata.irqs[0]; i8042_aux_irq = irq; } - - dp = dp->sibling; } return 0; @@ -109,8 +106,9 @@ static struct platform_driver sparc_i8042_driver = { static int __init i8042_platform_init(void) { struct device_node *root = of_find_node_by_path("/"); + const char *name = of_get_property(root, "name", NULL); - if (!strcmp(root->name, "SUNW,JavaStation-1")) { + if (name && !strcmp(name, "SUNW,JavaStation-1")) { /* Hardcoded values for MrCoffee. */ i8042_kbd_irq = i8042_aux_irq = 13 | 0x20; kbd_iobase = ioremap(0x71300060, 8); @@ -139,8 +137,9 @@ static int __init i8042_platform_init(void) static inline void i8042_platform_exit(void) { struct device_node *root = of_find_node_by_path("/"); + const char *name = of_get_property(root, "name", NULL); - if (strcmp(root->name, "SUNW,JavaStation-1")) + if (!name || strcmp(name, "SUNW,JavaStation-1")) platform_driver_unregister(&sparc_i8042_driver); } diff --git a/drivers/input/tablet/wacom_serial4.c b/drivers/input/tablet/wacom_serial4.c index 38bfaca48eab..150f9eecaca7 100644 --- a/drivers/input/tablet/wacom_serial4.c +++ b/drivers/input/tablet/wacom_serial4.c @@ -187,6 +187,7 @@ enum { MODEL_DIGITIZER_II = 0x5544, /* UD */ MODEL_GRAPHIRE = 0x4554, /* ET */ MODEL_PENPARTNER = 0x4354, /* CT */ + MODEL_ARTPAD_II = 0x4B54, /* KT */ }; static void wacom_handle_model_response(struct wacom *wacom) @@ -245,6 +246,7 @@ static void wacom_handle_model_response(struct wacom *wacom) wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL; break; + case MODEL_ARTPAD_II: case MODEL_DIGITIZER_II: wacom->dev->name = "Wacom Digitizer II"; wacom->dev->id.version = MODEL_DIGITIZER_II; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 068dbbc610fc..7a4884ad198b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -699,6 +699,7 @@ config TOUCHSCREEN_EDT_FT5X06 config TOUCHSCREEN_RASPBERRYPI_FW tristate "Raspberry Pi's firmware base touch screen support" depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST) + select INPUT_POLLDEV help Say Y here if you have the official Raspberry Pi 7 inch screen on your system. @@ -1168,11 +1169,11 @@ config TOUCHSCREEN_SIS_I2C module will be called sis_i2c. config TOUCHSCREEN_ST1232 - tristate "Sitronix ST1232 touchscreen controllers" + tristate "Sitronix ST1232 or ST1633 touchscreen controllers" depends on I2C help - Say Y here if you want to support Sitronix ST1232 - touchscreen controller. + Say Y here if you want to support the Sitronix ST1232 + or ST1633 touchscreen controller. If unsure, say N. diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 6fa714c587b4..3a016f43fb85 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -246,11 +246,14 @@ static void ad7879_timer(struct timer_list *t) static irqreturn_t ad7879_irq(int irq, void *handle) { struct ad7879 *ts = handle; + int error; - regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS, - ts->conversion_data, AD7879_NR_SENSE); - - if (!ad7879_report(ts)) + error = regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS, + ts->conversion_data, AD7879_NR_SENSE); + if (error) + dev_err_ratelimited(ts->dev, "failed to read %#02x: %d\n", + AD7879_REG_XPLUS, error); + else if (!ad7879_report(ts)) mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); return IRQ_HANDLED; diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 1e18ca0d1b4e..702bfda7ee77 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -31,6 +31,7 @@ #include <linux/interrupt.h> #include <linux/input.h> #include <linux/i2c.h> +#include <linux/kernel.h> #include <linux/uaccess.h> #include <linux/delay.h> #include <linux/debugfs.h> @@ -53,6 +54,11 @@ #define M09_REGISTER_NUM_X 0x94 #define M09_REGISTER_NUM_Y 0x95 +#define EV_REGISTER_THRESHOLD 0x40 +#define EV_REGISTER_GAIN 0x41 +#define EV_REGISTER_OFFSET_Y 0x45 +#define EV_REGISTER_OFFSET_X 0x46 + #define NO_REGISTER 0xff #define WORK_REGISTER_OPMODE 0x3c @@ -73,6 +79,7 @@ enum edt_ver { EDT_M06, EDT_M09, EDT_M12, + EV_FT, GENERIC_FT, }; @@ -81,6 +88,8 @@ struct edt_reg_addr { int reg_report_rate; int reg_gain; int reg_offset; + int reg_offset_x; + int reg_offset_y; int reg_num_x; int reg_num_y; }; @@ -106,6 +115,8 @@ struct edt_ft5x06_ts_data { int threshold; int gain; int offset; + int offset_x; + int offset_y; int report_rate; int max_support_points; @@ -190,6 +201,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) case EDT_M09: case EDT_M12: + case EV_FT: case GENERIC_FT: cmd = 0x0; offset = 3; @@ -242,6 +254,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) x = ((buf[0] << 8) | buf[1]) & 0x0fff; y = ((buf[2] << 8) | buf[3]) & 0x0fff; + /* The FT5x26 send the y coordinate first */ + if (tsdata->version == EV_FT) + swap(x, y); + id = (buf[2] >> 4) & 0x0f; down = type != TOUCH_EVENT_UP; @@ -275,8 +291,10 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); + /* fallthrough */ case EDT_M09: case EDT_M12: + case EV_FT: case GENERIC_FT: wrbuf[0] = addr; wrbuf[1] = value; @@ -315,8 +333,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, } break; + /* fallthrough */ case EDT_M09: case EDT_M12: + case EV_FT: case GENERIC_FT: wrbuf[0] = addr; error = edt_ft5x06_ts_readwrite(tsdata->client, 1, @@ -339,9 +359,10 @@ struct edt_ft5x06_attribute { u8 limit_high; u8 addr_m06; u8 addr_m09; + u8 addr_ev; }; -#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \ +#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev, \ _limit_low, _limit_high) \ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ .dattr = __ATTR(_field, _mode, \ @@ -350,6 +371,7 @@ struct edt_ft5x06_attribute { .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ .addr_m06 = _addr_m06, \ .addr_m09 = _addr_m09, \ + .addr_ev = _addr_ev, \ .limit_low = _limit_low, \ .limit_high = _limit_high, \ } @@ -386,6 +408,10 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, addr = attr->addr_m09; break; + case EV_FT: + addr = attr->addr_ev; + break; + default: error = -ENODEV; goto out; @@ -457,6 +483,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, addr = attr->addr_m09; break; + case EV_FT: + addr = attr->addr_ev; + break; + default: error = -ENODEV; goto out; @@ -480,20 +510,28 @@ out: /* m06, m09: range 0-31, m12: range 0-5 */ static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, - M09_REGISTER_GAIN, 0, 31); + M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31); /* m06, m09: range 0-31, m12: range 0-16 */ static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, - M09_REGISTER_OFFSET, 0, 31); + M09_REGISTER_OFFSET, NO_REGISTER, 0, 31); +/* m06, m09, m12: no supported, ev_ft: range 0-80 */ +static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, + EV_REGISTER_OFFSET_X, 0, 80); +/* m06, m09, m12: no supported, ev_ft: range 0-80 */ +static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, + EV_REGISTER_OFFSET_Y, 0, 80); /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, - M09_REGISTER_THRESHOLD, 0, 255); + M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); /* m06: range 3 to 14, m12: (0x64: 100Hz) */ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, - NO_REGISTER, 0, 255); + NO_REGISTER, NO_REGISTER, 0, 255); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, &edt_ft5x06_attr_offset.dattr.attr, + &edt_ft5x06_attr_offset_x.dattr.attr, + &edt_ft5x06_attr_offset_y.dattr.attr, &edt_ft5x06_attr_threshold.dattr.attr, &edt_ft5x06_attr_report_rate.dattr.attr, NULL @@ -605,8 +643,15 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) tsdata->threshold); edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, tsdata->gain); - edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, - tsdata->offset); + if (reg_addr->reg_offset != NO_REGISTER) + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, + tsdata->offset); + if (reg_addr->reg_offset_x != NO_REGISTER) + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, + tsdata->offset_x); + if (reg_addr->reg_offset_y != NO_REGISTER) + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, + tsdata->offset_y); if (reg_addr->reg_report_rate != NO_REGISTER) edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, tsdata->report_rate); @@ -867,6 +912,16 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, case 0x5a: /* Solomon Goldentek Display */ snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0"); break; + case 0x59: /* Evervision Display with FT5xx6 TS */ + tsdata->version = EV_FT; + error = edt_ft5x06_ts_readwrite(client, 1, "\x53", + 1, rdbuf); + if (error) + return error; + strlcpy(fw_version, rdbuf, 1); + snprintf(model_name, EDT_NAME_LEN, + "EVERVISION-FT5726NEi"); + break; default: snprintf(model_name, EDT_NAME_LEN, "generic ft5x06 (%02x)", @@ -902,6 +957,18 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev, edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val); tsdata->offset = val; } + + error = device_property_read_u32(dev, "offset-x", &val); + if (!error) { + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, val); + tsdata->offset_x = val; + } + + error = device_property_read_u32(dev, "offset-y", &val); + if (!error) { + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, val); + tsdata->offset_y = val; + } } static void @@ -912,7 +979,15 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) tsdata->threshold = edt_ft5x06_register_read(tsdata, reg_addr->reg_threshold); tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); - tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); + if (reg_addr->reg_offset != NO_REGISTER) + tsdata->offset = + edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); + if (reg_addr->reg_offset_x != NO_REGISTER) + tsdata->offset_x = edt_ft5x06_register_read(tsdata, + reg_addr->reg_offset_x); + if (reg_addr->reg_offset_y != NO_REGISTER) + tsdata->offset_y = edt_ft5x06_register_read(tsdata, + reg_addr->reg_offset_y); if (reg_addr->reg_report_rate != NO_REGISTER) tsdata->report_rate = edt_ft5x06_register_read(tsdata, reg_addr->reg_report_rate); @@ -940,6 +1015,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; reg_addr->reg_gain = WORK_REGISTER_GAIN; reg_addr->reg_offset = WORK_REGISTER_OFFSET; + reg_addr->reg_offset_x = NO_REGISTER; + reg_addr->reg_offset_y = NO_REGISTER; reg_addr->reg_num_x = WORK_REGISTER_NUM_X; reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; break; @@ -950,15 +1027,30 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) reg_addr->reg_report_rate = NO_REGISTER; reg_addr->reg_gain = M09_REGISTER_GAIN; reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_offset_x = NO_REGISTER; + reg_addr->reg_offset_y = NO_REGISTER; reg_addr->reg_num_x = M09_REGISTER_NUM_X; reg_addr->reg_num_y = M09_REGISTER_NUM_Y; break; + case EV_FT: + reg_addr->reg_threshold = EV_REGISTER_THRESHOLD; + reg_addr->reg_gain = EV_REGISTER_GAIN; + reg_addr->reg_offset = NO_REGISTER; + reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X; + reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y; + reg_addr->reg_num_x = NO_REGISTER; + reg_addr->reg_num_y = NO_REGISTER; + reg_addr->reg_report_rate = NO_REGISTER; + break; + case GENERIC_FT: /* this is a guesswork */ reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; reg_addr->reg_gain = M09_REGISTER_GAIN; reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_offset_x = NO_REGISTER; + reg_addr->reg_offset_y = NO_REGISTER; break; } } @@ -1155,6 +1247,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = { static const struct i2c_device_id edt_ft5x06_ts_id[] = { { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, + { .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data }, /* Note no edt- prefix for compatibility with the ft6236.c driver */ { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, { /* sentinel */ } @@ -1167,6 +1260,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, + { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data }, /* Note focaltech vendor prefix for compatibility with ft6236.c */ { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, { /* sentinel */ } diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index f2d9c2c41885..f57d82220a88 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -216,6 +216,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id) { switch (id) { case 1151: + case 5688: return >1x_chip_data; case 911: @@ -692,7 +693,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) touchscreen_parse_properties(ts->input_dev, true, &ts->prop); if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) { - dev_err(&ts->client->dev, "Invalid config, using defaults\n"); + dev_err(&ts->client->dev, + "Invalid config (%d, %d, %d), using defaults\n", + ts->prop.max_x, ts->prop.max_y, ts->max_touch_num); ts->prop.max_x = GOODIX_MAX_WIDTH - 1; ts->prop.max_y = GOODIX_MAX_HEIGHT - 1; ts->max_touch_num = GOODIX_MAX_CONTACTS; @@ -942,6 +945,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); #ifdef CONFIG_OF static const struct of_device_id goodix_of_match[] = { { .compatible = "goodix,gt1151" }, + { .compatible = "goodix,gt5688" }, { .compatible = "goodix,gt911" }, { .compatible = "goodix,gt9110" }, { .compatible = "goodix,gt912" }, diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 6f76eeedf465..9169aa03958a 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -4,11 +4,15 @@ #include <linux/slab.h> #include <linux/input.h> #include <linux/input/mt.h> +#include <linux/input/touchscreen.h> #include <linux/delay.h> #include <linux/workqueue.h> -#include <linux/input/ili210x.h> +#include <linux/gpio/consumer.h> +#include <linux/of_device.h> +#include <asm/unaligned.h> -#define MAX_TOUCHES 2 +#define ILI210X_TOUCHES 2 +#define ILI251X_TOUCHES 10 #define DEFAULT_POLL_PERIOD 20 /* Touchscreen commands */ @@ -17,41 +21,32 @@ #define REG_FIRMWARE_VERSION 0x40 #define REG_CALIBRATE 0xcc -struct finger { - u8 x_low; - u8 x_high; - u8 y_low; - u8 y_high; -} __packed; - -struct touchdata { - u8 status; - struct finger finger[MAX_TOUCHES]; -} __packed; - -struct panel_info { - struct finger finger_max; - u8 xchannel_num; - u8 ychannel_num; -} __packed; - struct firmware_version { u8 id; u8 major; u8 minor; } __packed; +enum ili2xxx_model { + MODEL_ILI210X, + MODEL_ILI251X, +}; + struct ili210x { struct i2c_client *client; struct input_dev *input; - bool (*get_pendown_state)(void); unsigned int poll_period; struct delayed_work dwork; + struct gpio_desc *reset_gpio; + struct touchscreen_properties prop; + enum ili2xxx_model model; + unsigned int max_touches; }; static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, size_t len) { + struct ili210x *priv = i2c_get_clientdata(client); struct i2c_msg msg[2] = { { .addr = client->addr, @@ -67,7 +62,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, } }; - if (i2c_transfer(client->adapter, msg, 2) != 2) { + if (priv->model == MODEL_ILI251X) { + if (i2c_transfer(client->adapter, msg, 1) != 1) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + usleep_range(5000, 5500); + + if (i2c_transfer(client->adapter, msg + 1, 1) != 1) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + } else { + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + } + + return 0; +} + +static int ili210x_read(struct i2c_client *client, void *buf, size_t len) +{ + struct i2c_msg msg = { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }; + + if (i2c_transfer(client->adapter, &msg, 1) != 1) { dev_err(&client->dev, "i2c transfer failed\n"); return -EIO; } @@ -75,42 +101,72 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, return 0; } -static void ili210x_report_events(struct input_dev *input, - const struct touchdata *touchdata) +static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, + unsigned int finger, + unsigned int *x, unsigned int *y) { - int i; - bool touch; - unsigned int x, y; - const struct finger *finger; + if (finger >= ILI210X_TOUCHES) + return false; - for (i = 0; i < MAX_TOUCHES; i++) { - input_mt_slot(input, i); + if (touchdata[0] & BIT(finger)) + return false; - finger = &touchdata->finger[i]; + *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); + *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); - touch = touchdata->status & (1 << i); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - if (touch) { - x = finger->x_low | (finger->x_high << 8); - y = finger->y_low | (finger->y_high << 8); + return true; +} + +static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, + unsigned int finger, + unsigned int *x, unsigned int *y) +{ + if (finger >= ILI251X_TOUCHES) + return false; + + *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); + if (!(*x & BIT(15))) /* Touch indication */ + return false; - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); + *x &= 0x3fff; + *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); + + return true; +} + +static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) +{ + struct input_dev *input = priv->input; + int i; + bool contact = false, touch = false; + unsigned int x = 0, y = 0; + + for (i = 0; i < priv->max_touches; i++) { + if (priv->model == MODEL_ILI210X) { + touch = ili210x_touchdata_to_coords(priv, touchdata, + i, &x, &y); + } else if (priv->model == MODEL_ILI251X) { + touch = ili251x_touchdata_to_coords(priv, touchdata, + i, &x, &y); + if (touch) + contact = true; } + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (!touch) + continue; + touchscreen_report_pos(input, &priv->prop, x, y, + true); } input_mt_report_pointer_emulation(input, false); input_sync(input); -} -static bool get_pendown_state(const struct ili210x *priv) -{ - bool state = false; - - if (priv->get_pendown_state) - state = priv->get_pendown_state(); + if (priv->model == MODEL_ILI210X) + contact = touchdata[0] & 0xf3; - return state; + return contact; } static void ili210x_work(struct work_struct *work) @@ -118,20 +174,29 @@ static void ili210x_work(struct work_struct *work) struct ili210x *priv = container_of(work, struct ili210x, dwork.work); struct i2c_client *client = priv->client; - struct touchdata touchdata; - int error; + u8 touchdata[64] = { 0 }; + bool touch; + int error = -EINVAL; + + if (priv->model == MODEL_ILI210X) { + error = ili210x_read_reg(client, REG_TOUCHDATA, + touchdata, sizeof(touchdata)); + } else if (priv->model == MODEL_ILI251X) { + error = ili210x_read_reg(client, REG_TOUCHDATA, + touchdata, 31); + if (!error && touchdata[0] == 2) + error = ili210x_read(client, &touchdata[31], 20); + } - error = ili210x_read_reg(client, REG_TOUCHDATA, - &touchdata, sizeof(touchdata)); if (error) { dev_err(&client->dev, "Unable to get touchdata, err = %d\n", error); return; } - ili210x_report_events(priv->input, &touchdata); + touch = ili210x_report_events(priv, touchdata); - if ((touchdata.status & 0xf3) || get_pendown_state(priv)) + if (touch) schedule_delayed_work(&priv->dwork, msecs_to_jiffies(priv->poll_period)); } @@ -180,103 +245,119 @@ static const struct attribute_group ili210x_attr_group = { .attrs = ili210x_attributes, }; +static void ili210x_power_down(void *data) +{ + struct gpio_desc *reset_gpio = data; + + gpiod_set_value_cansleep(reset_gpio, 1); +} + +static void ili210x_cancel_work(void *data) +{ + struct ili210x *priv = data; + + cancel_delayed_work_sync(&priv->dwork); +} + static int ili210x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - const struct ili210x_platform_data *pdata = dev_get_platdata(dev); struct ili210x *priv; + struct gpio_desc *reset_gpio; struct input_dev *input; - struct panel_info panel; struct firmware_version firmware; - int xmax, ymax; + enum ili2xxx_model model; int error; - dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + model = (enum ili2xxx_model)id->driver_data; - if (!pdata) { - dev_err(dev, "No platform data!\n"); - return -EINVAL; - } + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); if (client->irq <= 0) { dev_err(dev, "No IRQ!\n"); return -EINVAL; } - /* Get firmware version */ - error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, - &firmware, sizeof(firmware)); - if (error) { - dev_err(dev, "Failed to get firmware version, err: %d\n", - error); - return error; - } + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); - /* get panel info */ - error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); - if (error) { - dev_err(dev, "Failed to get panel information, err: %d\n", - error); - return error; + if (reset_gpio) { + error = devm_add_action_or_reset(dev, ili210x_power_down, + reset_gpio); + if (error) + return error; + + usleep_range(50, 100); + gpiod_set_value_cansleep(reset_gpio, 0); + msleep(100); } - xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); - ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - input = input_allocate_device(); - if (!priv || !input) { - error = -ENOMEM; - goto err_free_mem; - } + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; priv->client = client; priv->input = input; - priv->get_pendown_state = pdata->get_pendown_state; - priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; + priv->poll_period = DEFAULT_POLL_PERIOD; INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + priv->reset_gpio = reset_gpio; + priv->model = model; + if (model == MODEL_ILI210X) + priv->max_touches = ILI210X_TOUCHES; + if (model == MODEL_ILI251X) + priv->max_touches = ILI251X_TOUCHES; + + i2c_set_clientdata(client, priv); + + /* Get firmware version */ + error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); + if (error) { + dev_err(dev, "Failed to get firmware version, err: %d\n", + error); + return error; + } /* Setup input device */ input->name = "ILI210x Touchscreen"; input->id.bustype = BUS_I2C; input->dev.parent = dev; - __set_bit(EV_SYN, input->evbit); - __set_bit(EV_KEY, input->evbit); - __set_bit(EV_ABS, input->evbit); - __set_bit(BTN_TOUCH, input->keybit); - - /* Single touch */ - input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); - input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); - /* Multi touch */ - input_mt_init_slots(input, MAX_TOUCHES, 0); - input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); + touchscreen_parse_properties(input, true, &priv->prop); + input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); - i2c_set_clientdata(client, priv); + error = devm_add_action(dev, ili210x_cancel_work, priv); + if (error) + return error; - error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, - client->name, priv); + error = devm_request_irq(dev, client->irq, ili210x_irq, 0, + client->name, priv); if (error) { dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error); - goto err_free_mem; + return error; } - error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); + error = devm_device_add_group(dev, &ili210x_attr_group); if (error) { dev_err(dev, "Unable to create sysfs attributes, err: %d\n", error); - goto err_free_irq; + return error; } error = input_register_device(priv->input); if (error) { dev_err(dev, "Cannot register input device, err: %d\n", error); - goto err_remove_sysfs; + return error; } device_init_wakeup(dev, 1); @@ -286,28 +367,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, client->irq, firmware.id, firmware.major, firmware.minor); return 0; - -err_remove_sysfs: - sysfs_remove_group(&dev->kobj, &ili210x_attr_group); -err_free_irq: - free_irq(client->irq, priv); -err_free_mem: - input_free_device(input); - kfree(priv); - return error; -} - -static int ili210x_i2c_remove(struct i2c_client *client) -{ - struct ili210x *priv = i2c_get_clientdata(client); - - sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); - free_irq(priv->client->irq, priv); - cancel_delayed_work_sync(&priv->dwork); - input_unregister_device(priv->input); - kfree(priv); - - return 0; } static int __maybe_unused ili210x_i2c_suspend(struct device *dev) @@ -334,19 +393,27 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, ili210x_i2c_suspend, ili210x_i2c_resume); static const struct i2c_device_id ili210x_i2c_id[] = { - { "ili210x", 0 }, + { "ili210x", MODEL_ILI210X }, + { "ili251x", MODEL_ILI251X }, { } }; MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); +static const struct of_device_id ili210x_dt_ids[] = { + { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, + { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, + { }, +}; +MODULE_DEVICE_TABLE(of, ili210x_dt_ids); + static struct i2c_driver ili210x_ts_driver = { .driver = { .name = "ili210x_i2c", .pm = &ili210x_i2c_pm, + .of_match_table = ili210x_dt_ids, }, .id_table = ili210x_i2c_id, .probe = ili210x_i2c_probe, - .remove = ili210x_i2c_remove, }; module_i2c_driver(ili210x_ts_driver); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 11ff32c68025..34923399ece4 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -11,25 +11,19 @@ */ #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/pm_qos.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/input/touchscreen.h> #define ST1232_TS_NAME "st1232-ts" - -#define MIN_X 0x00 -#define MIN_Y 0x00 -#define MAX_X 0x31f /* (800 - 1) */ -#define MAX_Y 0x1df /* (480 - 1) */ -#define MAX_AREA 0xff -#define MAX_FINGERS 2 +#define ST1633_TS_NAME "st1633-ts" struct st1232_ts_finger { u16 x; @@ -38,12 +32,25 @@ struct st1232_ts_finger { bool is_valid; }; +struct st_chip_info { + bool have_z; + u16 max_x; + u16 max_y; + u16 max_area; + u16 max_fingers; + u8 start_reg; +}; + struct st1232_ts_data { struct i2c_client *client; struct input_dev *input_dev; - struct st1232_ts_finger finger[MAX_FINGERS]; + struct touchscreen_properties prop; struct dev_pm_qos_request low_latency_req; - int reset_gpio; + struct gpio_desc *reset_gpio; + const struct st_chip_info *chip_info; + int read_buf_len; + u8 *read_buf; + struct st1232_ts_finger *finger; }; static int st1232_ts_read_data(struct st1232_ts_data *ts) @@ -52,40 +59,35 @@ static int st1232_ts_read_data(struct st1232_ts_data *ts) struct i2c_client *client = ts->client; struct i2c_msg msg[2]; int error; - u8 start_reg; - u8 buf[10]; + int i, y; + u8 start_reg = ts->chip_info->start_reg; + u8 *buf = ts->read_buf; - /* read touchscreen data from ST1232 */ + /* read touchscreen data */ msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 1; msg[0].buf = &start_reg; - start_reg = 0x10; msg[1].addr = ts->client->addr; msg[1].flags = I2C_M_RD; - msg[1].len = sizeof(buf); + msg[1].len = ts->read_buf_len; msg[1].buf = buf; error = i2c_transfer(client->adapter, msg, 2); if (error < 0) return error; - /* get "valid" bits */ - finger[0].is_valid = buf[2] >> 7; - finger[1].is_valid = buf[5] >> 7; + for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) { + finger[i].is_valid = buf[i + y] >> 7; + if (finger[i].is_valid) { + finger[i].x = ((buf[i + y] & 0x0070) << 4) | buf[i + 1]; + finger[i].y = ((buf[i + y] & 0x0007) << 8) | buf[i + 2]; - /* get xy coordinate */ - if (finger[0].is_valid) { - finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3]; - finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4]; - finger[0].t = buf[8]; - } - - if (finger[1].is_valid) { - finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6]; - finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7]; - finger[1].t = buf[9]; + /* st1232 includes a z-axis / touch strength */ + if (ts->chip_info->have_z) + finger[i].t = buf[i + 6]; + } } return 0; @@ -104,13 +106,16 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) goto end; /* multi touch protocol */ - for (i = 0; i < MAX_FINGERS; i++) { + for (i = 0; i < ts->chip_info->max_fingers; i++) { if (!finger[i].is_valid) continue; - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); - input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); + if (ts->chip_info->have_z) + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[i].t); + + touchscreen_report_pos(input_dev, &ts->prop, + finger[i].x, finger[i].y, true); input_mt_sync(input_dev); count++; } @@ -138,17 +143,45 @@ end: static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) { - if (gpio_is_valid(ts->reset_gpio)) - gpio_direction_output(ts->reset_gpio, poweron); + if (ts->reset_gpio) + gpiod_set_value_cansleep(ts->reset_gpio, !poweron); } +static const struct st_chip_info st1232_chip_info = { + .have_z = true, + .max_x = 0x31f, /* 800 - 1 */ + .max_y = 0x1df, /* 480 -1 */ + .max_area = 0xff, + .max_fingers = 2, + .start_reg = 0x12, +}; + +static const struct st_chip_info st1633_chip_info = { + .have_z = false, + .max_x = 0x13f, /* 320 - 1 */ + .max_y = 0x1df, /* 480 -1 */ + .max_area = 0x00, + .max_fingers = 5, + .start_reg = 0x12, +}; + static int st1232_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const struct st_chip_info *match; struct st1232_ts_data *ts; + struct st1232_ts_finger *finger; struct input_dev *input_dev; int error; + match = device_get_match_data(&client->dev); + if (!match && id) + match = (const void *)id->driver_data; + if (!match) { + dev_err(&client->dev, "unknown device model\n"); + return -ENODEV; + } + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "need I2C_FUNC_I2C\n"); return -EIO; @@ -163,6 +196,19 @@ static int st1232_ts_probe(struct i2c_client *client, if (!ts) return -ENOMEM; + ts->chip_info = match; + ts->finger = devm_kcalloc(&client->dev, + ts->chip_info->max_fingers, sizeof(*finger), + GFP_KERNEL); + if (!ts->finger) + return -ENOMEM; + + /* allocate a buffer according to the number of registers to read */ + ts->read_buf_len = ts->chip_info->max_fingers * 4; + ts->read_buf = devm_kzalloc(&client->dev, ts->read_buf_len, GFP_KERNEL); + if (!ts->read_buf) + return -ENOMEM; + input_dev = devm_input_allocate_device(&client->dev); if (!input_dev) return -ENOMEM; @@ -170,15 +216,13 @@ static int st1232_ts_probe(struct i2c_client *client, ts->client = client; ts->input_dev = input_dev; - ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); - if (gpio_is_valid(ts->reset_gpio)) { - error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL); - if (error) { - dev_err(&client->dev, - "Unable to request GPIO pin %d.\n", - ts->reset_gpio); - return error; - } + ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL, + GPIOD_OUT_HIGH); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + dev_err(&client->dev, "Unable to request GPIO pin: %d.\n", + error); + return error; } st1232_ts_power(ts, true); @@ -192,9 +236,16 @@ static int st1232_ts_probe(struct i2c_client *client, __set_bit(EV_KEY, input_dev->evbit); __set_bit(EV_ABS, input_dev->evbit); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); + if (ts->chip_info->have_z) + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, + ts->chip_info->max_area, 0, 0); + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, ts->chip_info->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, ts->chip_info->max_y, 0, 0); + + touchscreen_parse_properties(input_dev, true, &ts->prop); error = devm_request_threaded_irq(&client->dev, client->irq, NULL, st1232_ts_irq_handler, @@ -261,13 +312,15 @@ static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops, st1232_ts_suspend, st1232_ts_resume); static const struct i2c_device_id st1232_ts_id[] = { - { ST1232_TS_NAME, 0 }, + { ST1232_TS_NAME, (unsigned long)&st1232_chip_info }, + { ST1633_TS_NAME, (unsigned long)&st1633_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, st1232_ts_id); static const struct of_device_id st1232_ts_dt_ids[] = { - { .compatible = "sitronix,st1232", }, + { .compatible = "sitronix,st1232", .data = &st1232_chip_info }, + { .compatible = "sitronix,st1633", .data = &st1633_chip_info }, { } }; MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids); @@ -286,5 +339,6 @@ static struct i2c_driver st1232_ts_driver = { module_i2c_driver(st1232_ts_driver); MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>"); +MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@ginzinger.com>"); MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 704e99046916..b6f95f20f924 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -106,27 +106,29 @@ struct stmfts_data { bool running; }; -static void stmfts_brightness_set(struct led_classdev *led_cdev, +static int stmfts_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); int err; - if (value == sdata->led_status || !sdata->ledvdd) - return; - - if (!value) { - regulator_disable(sdata->ledvdd); - } else { - err = regulator_enable(sdata->ledvdd); - if (err) - dev_warn(&sdata->client->dev, - "failed to disable ledvdd regulator: %d\n", - err); + if (value != sdata->led_status && sdata->ledvdd) { + if (!value) { + regulator_disable(sdata->ledvdd); + } else { + err = regulator_enable(sdata->ledvdd); + if (err) { + dev_warn(&sdata->client->dev, + "failed to disable ledvdd regulator: %d\n", + err); + return err; + } + } + sdata->led_status = value; } - sdata->led_status = value; + return 0; } static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev) @@ -608,7 +610,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata) sdata->led_cdev.name = STMFTS_DEV_NAME; sdata->led_cdev.max_brightness = LED_ON; sdata->led_cdev.brightness = LED_OFF; - sdata->led_cdev.brightness_set = stmfts_brightness_set; + sdata->led_cdev.brightness_set_blocking = stmfts_brightness_set; sdata->led_cdev.brightness_get = stmfts_brightness_get; err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev); diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index ed29db3ec731..dbdf4898aa17 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -27,12 +27,16 @@ * published by the Free Software Foundation. */ -#include <linux/input.h> -#include <linux/module.h> -#include <linux/of.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/touchscreen.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> /* register addresses */ #define I2C_REG_TOUCH0 0x00 @@ -42,25 +46,28 @@ #define I2C_REG_IRQSRC 0x23 #define I2C_REG_SOFTRESET 0x3f +#define I2C_REG_SX8650_STAT 0x05 +#define SX8650_STAT_CONVIRQ BIT(7) + /* commands */ #define CMD_READ_REGISTER 0x40 -#define CMD_MANUAL 0xc0 #define CMD_PENTRG 0xe0 /* value for I2C_REG_SOFTRESET */ #define SOFTRESET_VALUE 0xde /* bits for I2C_REG_IRQSRC */ -#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08 -#define IRQ_PENRELEASE 0x04 +#define IRQ_PENTOUCH_TOUCHCONVDONE BIT(3) +#define IRQ_PENRELEASE BIT(2) /* bits for RegTouch1 */ #define CONDIRQ 0x20 +#define RPDNT_100K 0x00 #define FILT_7SA 0x03 /* bits for I2C_REG_CHANMASK */ -#define CONV_X 0x80 -#define CONV_Y 0x40 +#define CONV_X BIT(7) +#define CONV_Y BIT(6) /* coordinates rate: higher nibble of CTRL0 register */ #define RATE_MANUAL 0x00 @@ -69,13 +76,122 @@ /* power delay: lower nibble of CTRL0 register */ #define POWDLY_1_1MS 0x0b +/* for sx8650, as we have no pen release IRQ there: timeout in ns following the + * last PENIRQ after which we assume the pen is lifted. + */ +#define SX8650_PENIRQ_TIMEOUT msecs_to_jiffies(10) + #define MAX_12BIT ((1 << 12) - 1) +#define MAX_I2C_READ_LEN 10 /* see datasheet section 5.1.5 */ + +/* channel definition */ +#define CH_X 0x00 +#define CH_Y 0x01 + +struct sx865x_data { + u8 cmd_manual; + u8 chan_mask; + bool has_irq_penrelease; + bool has_reg_irqmask; + irq_handler_t irqh; +}; struct sx8654 { struct input_dev *input; struct i2c_client *client; + struct gpio_desc *gpio_reset; + + spinlock_t lock; /* for input reporting from irq/timer */ + struct timer_list timer; + + struct touchscreen_properties props; + + const struct sx865x_data *data; }; +static inline void sx865x_penrelease(struct sx8654 *ts) +{ + struct input_dev *input_dev = ts->input; + + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); +} + +static void sx865x_penrelease_timer_handler(struct timer_list *t) +{ + struct sx8654 *ts = from_timer(ts, t, timer); + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + sx865x_penrelease(ts); + spin_unlock_irqrestore(&ts->lock, flags); + dev_dbg(&ts->client->dev, "penrelease by timer\n"); +} + +static irqreturn_t sx8650_irq(int irq, void *handle) +{ + struct sx8654 *ts = handle; + struct device *dev = &ts->client->dev; + int len, i; + unsigned long flags; + u8 stat; + u16 x, y; + u16 ch; + u16 chdata; + __be16 data[MAX_I2C_READ_LEN / sizeof(__be16)]; + u8 nchan = hweight32(ts->data->chan_mask); + u8 readlen = nchan * sizeof(*data); + + stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER + | I2C_REG_SX8650_STAT); + + if (!(stat & SX8650_STAT_CONVIRQ)) { + dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat); + return IRQ_HANDLED; + } + + len = i2c_master_recv(ts->client, (u8 *)data, readlen); + if (len != readlen) { + dev_dbg(dev, "ignore short recv (%d)\n", len); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&ts->lock, flags); + + x = 0; + y = 0; + for (i = 0; i < nchan; i++) { + chdata = be16_to_cpu(data[i]); + + if (unlikely(chdata == 0xFFFF)) { + dev_dbg(dev, "invalid qualified data @ %d\n", i); + continue; + } else if (unlikely(chdata & 0x8000)) { + dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata); + continue; + } + + ch = chdata >> 12; + if (ch == CH_X) + x = chdata & MAX_12BIT; + else if (ch == CH_Y) + y = chdata & MAX_12BIT; + else + dev_warn(dev, "unknown channel %d [0x%04x]\n", ch, + chdata); + } + + touchscreen_report_pos(ts->input, &ts->props, x, y, false); + input_report_key(ts->input, BTN_TOUCH, 1); + input_sync(ts->input); + dev_dbg(dev, "point(%4d,%4d)\n", x, y); + + mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT); + spin_unlock_irqrestore(&ts->lock, flags); + + return IRQ_HANDLED; +} + static irqreturn_t sx8654_irq(int irq, void *handle) { struct sx8654 *sx8654 = handle; @@ -112,8 +228,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle) x = ((data[0] & 0xf) << 8) | (data[1]); y = ((data[2] & 0xf) << 8) | (data[3]); - input_report_abs(sx8654->input, ABS_X, x); - input_report_abs(sx8654->input, ABS_Y, y); + touchscreen_report_pos(sx8654->input, &sx8654->props, x, y, + false); input_report_key(sx8654->input, BTN_TOUCH, 1); input_sync(sx8654->input); @@ -124,6 +240,25 @@ out: return IRQ_HANDLED; } +static int sx8654_reset(struct sx8654 *ts) +{ + int err; + + if (ts->gpio_reset) { + gpiod_set_value_cansleep(ts->gpio_reset, 1); + udelay(2); /* Tpulse > 1µs */ + gpiod_set_value_cansleep(ts->gpio_reset, 0); + } else { + dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n"); + err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET, + SOFTRESET_VALUE); + if (err) + return err; + } + + return 0; +} + static int sx8654_open(struct input_dev *dev) { struct sx8654 *sx8654 = input_get_drvdata(dev); @@ -157,14 +292,17 @@ static void sx8654_close(struct input_dev *dev) disable_irq(client->irq); + if (!sx8654->data->has_irq_penrelease) + del_timer_sync(&sx8654->timer); + /* enable manual mode mode */ - error = i2c_smbus_write_byte(client, CMD_MANUAL); + error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual); if (error) { dev_err(&client->dev, "writing command CMD_MANUAL failed"); return; } - error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL); if (error) { dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); return; @@ -186,6 +324,31 @@ static int sx8654_probe(struct i2c_client *client, if (!sx8654) return -ENOMEM; + sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sx8654->gpio_reset)) { + error = PTR_ERR(sx8654->gpio_reset); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, "unable to get reset-gpio: %d\n", + error); + return error; + } + dev_dbg(&client->dev, "got GPIO reset pin\n"); + + sx8654->data = device_get_match_data(&client->dev); + if (!sx8654->data) + sx8654->data = (const struct sx865x_data *)id->driver_data; + if (!sx8654->data) { + dev_err(&client->dev, "invalid or missing device data\n"); + return -EINVAL; + } + + if (!sx8654->data->has_irq_penrelease) { + dev_dbg(&client->dev, "use timer for penrelease\n"); + timer_setup(&sx8654->timer, sx865x_penrelease_timer_handler, 0); + spin_lock_init(&sx8654->lock); + } + input = devm_input_allocate_device(&client->dev); if (!input) return -ENOMEM; @@ -201,43 +364,46 @@ static int sx8654_probe(struct i2c_client *client, input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0); input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0); + touchscreen_parse_properties(input, false, &sx8654->props); + sx8654->client = client; sx8654->input = input; input_set_drvdata(sx8654->input, sx8654); - error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET, - SOFTRESET_VALUE); + error = sx8654_reset(sx8654); if (error) { - dev_err(&client->dev, "writing softreset value failed"); + dev_err(&client->dev, "reset failed"); return error; } error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, - CONV_X | CONV_Y); + sx8654->data->chan_mask); if (error) { dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); return error; } - error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, - IRQ_PENTOUCH_TOUCHCONVDONE | - IRQ_PENRELEASE); - if (error) { - dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); - return error; + if (sx8654->data->has_reg_irqmask) { + error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, + IRQ_PENTOUCH_TOUCHCONVDONE | + IRQ_PENRELEASE); + if (error) { + dev_err(&client->dev, "writing I2C_REG_IRQMASK failed"); + return error; + } } error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, - CONDIRQ | FILT_7SA); + CONDIRQ | RPDNT_100K | FILT_7SA); if (error) { dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); return error; } error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, sx8654_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + NULL, sx8654->data->irqh, + IRQF_ONESHOT, client->name, sx8654); if (error) { dev_err(&client->dev, @@ -256,17 +422,48 @@ static int sx8654_probe(struct i2c_client *client, return 0; } +static const struct sx865x_data sx8650_data = { + .cmd_manual = 0xb0, + .has_irq_penrelease = false, + .has_reg_irqmask = false, + .chan_mask = (CONV_X | CONV_Y), + .irqh = sx8650_irq, +}; + +static const struct sx865x_data sx8654_data = { + .cmd_manual = 0xc0, + .has_irq_penrelease = true, + .has_reg_irqmask = true, + .chan_mask = (CONV_X | CONV_Y), + .irqh = sx8654_irq, +}; + #ifdef CONFIG_OF static const struct of_device_id sx8654_of_match[] = { - { .compatible = "semtech,sx8654", }, - { }, + { + .compatible = "semtech,sx8650", + .data = &sx8650_data, + }, { + .compatible = "semtech,sx8654", + .data = &sx8654_data, + }, { + .compatible = "semtech,sx8655", + .data = &sx8654_data, + }, { + .compatible = "semtech,sx8656", + .data = &sx8654_data, + }, + { } }; MODULE_DEVICE_TABLE(of, sx8654_of_match); #endif static const struct i2c_device_id sx8654_id_table[] = { - { "semtech_sx8654", 0 }, - { }, + { .name = "semtech_sx8650", .driver_data = (long)&sx8650_data }, + { .name = "semtech_sx8654", .driver_data = (long)&sx8654_data }, + { .name = "semtech_sx8655", .driver_data = (long)&sx8654_data }, + { .name = "semtech_sx8656", .driver_data = (long)&sx8654_data }, + { } }; MODULE_DEVICE_TABLE(i2c, sx8654_id_table); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 9e8684ab48f4..83e685557a19 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -507,10 +507,8 @@ static int titsc_remove(struct platform_device *pdev) static int __maybe_unused titsc_suspend(struct device *dev) { struct titsc *ts_dev = dev_get_drvdata(dev); - struct ti_tscadc_dev *tscadc_dev; unsigned int idle; - tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (device_may_wakeup(dev)) { titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK); idle = titsc_readl(ts_dev, REG_IRQENABLE); @@ -524,9 +522,7 @@ static int __maybe_unused titsc_suspend(struct device *dev) static int __maybe_unused titsc_resume(struct device *dev) { struct titsc *ts_dev = dev_get_drvdata(dev); - struct ti_tscadc_dev *tscadc_dev; - tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (device_may_wakeup(dev)) { titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 6b0760dafb3e..b319e51c379b 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -140,10 +140,14 @@ static struct lock_class_key reserved_rbtree_key; static inline int match_hid_uid(struct device *dev, struct acpihid_map_entry *entry) { + struct acpi_device *adev = ACPI_COMPANION(dev); const char *hid, *uid; - hid = acpi_device_hid(ACPI_COMPANION(dev)); - uid = acpi_device_uid(ACPI_COMPANION(dev)); + if (!adev) + return -ENODEV; + + hid = acpi_device_hid(adev); + uid = acpi_device_uid(adev); if (!hid || !(*hid)) return -ENODEV; diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index ebb3fa2e1d00..362aa5450a5e 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -2032,10 +2032,19 @@ setup_hw(struct hfc_pci *hc) hc->hw.fifos = buffer; pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle); hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); + if (unlikely(!hc->hw.pci_io)) { + printk(KERN_WARNING + "HFC-PCI: Error in ioremap for PCI!\n"); + pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos, + hc->hw.dmahandle); + return 1; + } + printk(KERN_INFO "HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n", (u_long) hc->hw.pci_io, (u_long) hc->hw.fifos, (u_long) hc->hw.dmahandle, hc->irq, HZ); + /* enable memory mapped ports, disable busmaster */ pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); hc->hw.int_m2 = 0; diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c index 3e01012be4ab..0fe6ddcb3fdc 100644 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -712,8 +712,11 @@ setup_io(struct inf_hw *hw) (ulong)hw->addr.start, (ulong)hw->addr.size); return err; } - if (hw->ci->addr_mode == AM_MEMIO) + if (hw->ci->addr_mode == AM_MEMIO) { hw->addr.p = ioremap(hw->addr.start, hw->addr.size); + if (unlikely(!hw->addr.p)) + return -ENOMEM; + } hw->addr.mode = hw->ci->addr_mode; if (debug & DEBUG_HW) pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n", diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index f4253d468ae1..755c6bbc9553 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -570,7 +570,7 @@ isdnloop_atimeout(isdnloop_card *card, int ch) char buf[60]; spin_lock_irqsave(&card->isdnloop_lock, flags); - if (card->rcard) { + if (card->rcard[ch]) { isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1); card->rcard[ch]->rcard[card->rch[ch]] = NULL; card->rcard[ch] = NULL; diff --git a/drivers/lightnvm/pblk-rl.c b/drivers/lightnvm/pblk-rl.c index b014957dde0b..a5f8bc2defbc 100644 --- a/drivers/lightnvm/pblk-rl.c +++ b/drivers/lightnvm/pblk-rl.c @@ -233,10 +233,15 @@ void pblk_rl_init(struct pblk_rl *rl, int budget, int threshold) /* To start with, all buffer is available to user I/O writers */ rl->rb_budget = budget; rl->rb_user_max = budget; - rl->rb_max_io = threshold ? (budget - threshold) : (budget - 1); rl->rb_gc_max = 0; rl->rb_state = PBLK_RL_HIGH; + /* Maximize I/O size and ansure that back threshold is respected */ + if (threshold) + rl->rb_max_io = budget - pblk->min_write_pgs_data - threshold; + else + rl->rb_max_io = budget - pblk->min_write_pgs_data - 1; + atomic_set(&rl->rb_user_cnt, 0); atomic_set(&rl->rb_gc_cnt, 0); atomic_set(&rl->rb_space, -1); diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 0a0b8e1f4236..6a844125cf2d 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -485,7 +485,7 @@ int __init smu_init (void) * SMU based G5s need some memory below 2Gb. Thankfully this is * called at a time where memblock is still available. */ - smu_cmdbuf_abs = memblock_alloc_base(4096, 4096, 0x80000000UL); + smu_cmdbuf_abs = memblock_phys_alloc_range(4096, 4096, 0, 0x80000000UL); if (smu_cmdbuf_abs == 0) { printk(KERN_ERR "SMU: Command buffer allocation failed !\n"); ret = -EINVAL; @@ -493,6 +493,9 @@ int __init smu_init (void) } smu = memblock_alloc(sizeof(struct smu_device), SMP_CACHE_BYTES); + if (!smu) + panic("%s: Failed to allocate %zu bytes\n", __func__, + sizeof(struct smu_device)); spin_lock_init(&smu->lock); INIT_LIST_HEAD(&smu->cmd_list); diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 3eeb12e93e98..d86e7a4ac04d 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -205,4 +205,15 @@ config MTK_CMDQ_MBOX mailbox driver. The CMDQ is used to help read/write registers with critical time limitation, such as updating display configuration during the vblank. + +config ZYNQMP_IPI_MBOX + bool "Xilinx ZynqMP IPI Mailbox" + depends on ARCH_ZYNQMP && OF + help + Say yes here to add support for Xilinx IPI mailbox driver. + This mailbox driver is used to send notification or short message + between processors with Xilinx ZynqMP IPI. It will place the + message to the IPI buffer and will access the IPI control + registers to kick the other processor or enquire status. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index c818b5d011ae..8be3bcbcf882 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -44,3 +44,5 @@ obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o + +obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 774362a05159..85fc5b56f99b 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -187,8 +187,8 @@ static int imx_mu_startup(struct mbox_chan *chan) return 0; } - ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED, cp->irq_desc, - chan); + ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED | + IRQF_NO_SUSPEND, cp->irq_desc, chan); if (ret) { dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq); diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 58bfafc34bc4..4e4ac4be6423 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -31,7 +31,6 @@ (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) static bool mbox_data_ready; -static struct dentry *root_debugfs_dir; struct mbox_test_device { struct device *dev; @@ -45,6 +44,7 @@ struct mbox_test_device { spinlock_t lock; wait_queue_head_t waitq; struct fasync_struct *async_queue; + struct dentry *root_debugfs_dir; }; static ssize_t mbox_test_signal_write(struct file *filp, @@ -262,16 +262,16 @@ static int mbox_test_add_debugfs(struct platform_device *pdev, if (!debugfs_initialized()) return 0; - root_debugfs_dir = debugfs_create_dir("mailbox", NULL); - if (!root_debugfs_dir) { + tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL); + if (!tdev->root_debugfs_dir) { dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n"); return -EINVAL; } - debugfs_create_file("message", 0600, root_debugfs_dir, + debugfs_create_file("message", 0600, tdev->root_debugfs_dir, tdev, &mbox_test_message_ops); - debugfs_create_file("signal", 0200, root_debugfs_dir, + debugfs_create_file("signal", 0200, tdev->root_debugfs_dir, tdev, &mbox_test_signal_ops); return 0; @@ -363,22 +363,24 @@ static int mbox_test_probe(struct platform_device *pdev) /* It's okay for MMIO to be NULL */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - size = resource_size(res); tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res); - if (PTR_ERR(tdev->tx_mmio) == -EBUSY) + if (PTR_ERR(tdev->tx_mmio) == -EBUSY) { /* if reserved area in SRAM, try just ioremap */ + size = resource_size(res); tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size); - else if (IS_ERR(tdev->tx_mmio)) + } else if (IS_ERR(tdev->tx_mmio)) { tdev->tx_mmio = NULL; + } /* If specified, second reg entry is Rx MMIO */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - size = resource_size(res); tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res); - if (PTR_ERR(tdev->rx_mmio) == -EBUSY) + if (PTR_ERR(tdev->rx_mmio) == -EBUSY) { + size = resource_size(res); tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size); - else if (IS_ERR(tdev->rx_mmio)) + } else if (IS_ERR(tdev->rx_mmio)) { tdev->rx_mmio = tdev->tx_mmio; + } tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); @@ -416,7 +418,7 @@ static int mbox_test_remove(struct platform_device *pdev) { struct mbox_test_device *tdev = platform_get_drvdata(pdev); - debugfs_remove_recursive(root_debugfs_dir); + debugfs_remove_recursive(tdev->root_debugfs_dir); if (tdev->tx_channel) mbox_free_channel(tdev->tx_channel); diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index a338bd4cd7db..210fe504f5ae 100644 --- a/drivers/mailbox/stm32-ipcc.c +++ b/drivers/mailbox/stm32-ipcc.c @@ -270,14 +270,12 @@ static int stm32_ipcc_probe(struct platform_device *pdev) goto err_clk; } - device_init_wakeup(dev, true); + device_set_wakeup_capable(dev, true); ret = dev_pm_set_dedicated_wake_irq(dev, ipcc->wkp); if (ret) { dev_err(dev, "Failed to set wake up irq\n"); goto err_init_wkp; } - } else { - device_init_wakeup(dev, false); } /* mailbox controller */ diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index e443f6a2ec4b..11fc9fd6a94a 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -779,7 +779,7 @@ static int tegra_hsp_probe(struct platform_device *pdev) return 0; } -static int tegra_hsp_resume(struct device *dev) +static int __maybe_unused tegra_hsp_resume(struct device *dev) { struct tegra_hsp *hsp = dev_get_drvdata(dev); unsigned int i; diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c new file mode 100644 index 000000000000..86887c9a349a --- /dev/null +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -0,0 +1,725 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Inter Processor Interrupt(IPI) Mailbox Driver + * + * Copyright (C) 2018 Xilinx, Inc. + */ + +#include <linux/arm-smccc.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mailbox_controller.h> +#include <linux/mailbox/zynqmp-ipi-message.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> + +/* IPI agent ID any */ +#define IPI_ID_ANY 0xFFUL + +/* indicate if ZynqMP IPI mailbox driver uses SMC calls or HVC calls */ +#define USE_SMC 0 +#define USE_HVC 1 + +/* Default IPI SMC function IDs */ +#define SMC_IPI_MAILBOX_OPEN 0x82001000U +#define SMC_IPI_MAILBOX_RELEASE 0x82001001U +#define SMC_IPI_MAILBOX_STATUS_ENQUIRY 0x82001002U +#define SMC_IPI_MAILBOX_NOTIFY 0x82001003U +#define SMC_IPI_MAILBOX_ACK 0x82001004U +#define SMC_IPI_MAILBOX_ENABLE_IRQ 0x82001005U +#define SMC_IPI_MAILBOX_DISABLE_IRQ 0x82001006U + +/* IPI SMC Macros */ +#define IPI_SMC_ENQUIRY_DIRQ_MASK 0x00000001UL /* Flag to indicate if + * notification interrupt + * to be disabled. + */ +#define IPI_SMC_ACK_EIRQ_MASK 0x00000001UL /* Flag to indicate if + * notification interrupt + * to be enabled. + */ + +/* IPI mailbox status */ +#define IPI_MB_STATUS_IDLE 0 +#define IPI_MB_STATUS_SEND_PENDING 1 +#define IPI_MB_STATUS_RECV_PENDING 2 + +#define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */ +#define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */ + +/** + * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel + * @is_opened: indicate if the IPI channel is opened + * @req_buf: local to remote request buffer start address + * @resp_buf: local to remote response buffer start address + * @req_buf_size: request buffer size + * @resp_buf_size: response buffer size + * @rx_buf: receive buffer to pass received message to client + * @chan_type: channel type + */ +struct zynqmp_ipi_mchan { + int is_opened; + void __iomem *req_buf; + void __iomem *resp_buf; + void *rx_buf; + size_t req_buf_size; + size_t resp_buf_size; + unsigned int chan_type; +}; + +/** + * struct zynqmp_ipi_mbox - Description of a ZynqMP IPI mailbox + * platform data. + * @pdata: pointer to the IPI private data + * @dev: device pointer corresponding to the Xilinx ZynqMP + * IPI mailbox + * @remote_id: remote IPI agent ID + * @mbox: mailbox Controller + * @mchans: array for channels, tx channel and rx channel. + * @irq: IPI agent interrupt ID + */ +struct zynqmp_ipi_mbox { + struct zynqmp_ipi_pdata *pdata; + struct device dev; + u32 remote_id; + struct mbox_controller mbox; + struct zynqmp_ipi_mchan mchans[2]; +}; + +/** + * struct zynqmp_ipi_pdata - Description of z ZynqMP IPI agent platform data. + * + * @dev: device pointer corresponding to the Xilinx ZynqMP + * IPI agent + * @irq: IPI agent interrupt ID + * @method: IPI SMC or HVC is going to be used + * @local_id: local IPI agent ID + * @num_mboxes: number of mailboxes of this IPI agent + * @ipi_mboxes: IPI mailboxes of this IPI agent + */ +struct zynqmp_ipi_pdata { + struct device *dev; + int irq; + unsigned int method; + u32 local_id; + int num_mboxes; + struct zynqmp_ipi_mbox *ipi_mboxes; +}; + +static struct device_driver zynqmp_ipi_mbox_driver = { + .owner = THIS_MODULE, + .name = "zynqmp-ipi-mbox", +}; + +static void zynqmp_ipi_fw_call(struct zynqmp_ipi_mbox *ipi_mbox, + unsigned long a0, unsigned long a3, + struct arm_smccc_res *res) +{ + struct zynqmp_ipi_pdata *pdata = ipi_mbox->pdata; + unsigned long a1, a2; + + a1 = pdata->local_id; + a2 = ipi_mbox->remote_id; + if (pdata->method == USE_SMC) + arm_smccc_smc(a0, a1, a2, a3, 0, 0, 0, 0, res); + else + arm_smccc_hvc(a0, a1, a2, a3, 0, 0, 0, 0, res); +} + +/** + * zynqmp_ipi_interrupt - Interrupt handler for IPI notification + * + * @irq: Interrupt number + * @data: ZynqMP IPI mailbox platform data. + * + * Return: -EINVAL if there is no instance + * IRQ_NONE if the interrupt is not ours. + * IRQ_HANDLED if the rx interrupt was successfully handled. + */ +static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data) +{ + struct zynqmp_ipi_pdata *pdata = data; + struct mbox_chan *chan; + struct zynqmp_ipi_mbox *ipi_mbox; + struct zynqmp_ipi_mchan *mchan; + struct zynqmp_ipi_message *msg; + u64 arg0, arg3; + struct arm_smccc_res res; + int ret, i; + + (void)irq; + arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; + arg3 = IPI_SMC_ENQUIRY_DIRQ_MASK; + for (i = 0; i < pdata->num_mboxes; i++) { + ipi_mbox = &pdata->ipi_mboxes[i]; + mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; + chan = &ipi_mbox->mbox.chans[IPI_MB_CHNL_RX]; + zynqmp_ipi_fw_call(ipi_mbox, arg0, arg3, &res); + ret = (int)(res.a0 & 0xFFFFFFFF); + if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { + if (mchan->is_opened) { + msg = mchan->rx_buf; + msg->len = mchan->req_buf_size; + memcpy_fromio(msg->data, mchan->req_buf, + msg->len); + mbox_chan_received_data(chan, (void *)msg); + return IRQ_HANDLED; + } + } + } + return IRQ_NONE; +} + +/** + * zynqmp_ipi_peek_data - Peek to see if there are any rx messages. + * + * @chan: Channel Pointer + * + * Return: 'true' if there is pending rx data, 'false' if there is none. + */ +static bool zynqmp_ipi_peek_data(struct mbox_chan *chan) +{ + struct device *dev = chan->mbox->dev; + struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); + struct zynqmp_ipi_mchan *mchan = chan->con_priv; + int ret; + u64 arg0; + struct arm_smccc_res res; + + if (WARN_ON(!ipi_mbox)) { + dev_err(dev, "no platform drv data??\n"); + return false; + } + + arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; + zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); + ret = (int)(res.a0 & 0xFFFFFFFF); + + if (mchan->chan_type == IPI_MB_CHNL_TX) { + /* TX channel, check if the message has been acked + * by the remote, if yes, response is available. + */ + if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) + return false; + else + return true; + } else if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { + /* RX channel, check if there is message arrived. */ + return true; + } + return false; +} + +/** + * zynqmp_ipi_last_tx_done - See if the last tx message is sent + * + * @chan: Channel pointer + * + * Return: 'true' is no pending tx data, 'false' if there are any. + */ +static bool zynqmp_ipi_last_tx_done(struct mbox_chan *chan) +{ + struct device *dev = chan->mbox->dev; + struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); + struct zynqmp_ipi_mchan *mchan = chan->con_priv; + int ret; + u64 arg0; + struct arm_smccc_res res; + + if (WARN_ON(!ipi_mbox)) { + dev_err(dev, "no platform drv data??\n"); + return false; + } + + if (mchan->chan_type == IPI_MB_CHNL_TX) { + /* We only need to check if the message been taken + * by the remote in the TX channel + */ + arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; + zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); + /* Check the SMC call status, a0 of the result */ + ret = (int)(res.a0 & 0xFFFFFFFF); + if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) + return false; + return true; + } + /* Always true for the response message in RX channel */ + return true; +} + +/** + * zynqmp_ipi_send_data - Send data + * + * @chan: Channel Pointer + * @data: Message Pointer + * + * Return: 0 if all goes good, else appropriate error messages. + */ +static int zynqmp_ipi_send_data(struct mbox_chan *chan, void *data) +{ + struct device *dev = chan->mbox->dev; + struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); + struct zynqmp_ipi_mchan *mchan = chan->con_priv; + struct zynqmp_ipi_message *msg = data; + u64 arg0; + struct arm_smccc_res res; + + if (WARN_ON(!ipi_mbox)) { + dev_err(dev, "no platform drv data??\n"); + return -EINVAL; + } + + if (mchan->chan_type == IPI_MB_CHNL_TX) { + /* Send request message */ + if (msg && msg->len > mchan->req_buf_size) { + dev_err(dev, "channel %d message length %u > max %lu\n", + mchan->chan_type, (unsigned int)msg->len, + mchan->req_buf_size); + return -EINVAL; + } + if (msg && msg->len) + memcpy_toio(mchan->req_buf, msg->data, msg->len); + /* Kick IPI mailbox to send message */ + arg0 = SMC_IPI_MAILBOX_NOTIFY; + zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); + } else { + /* Send response message */ + if (msg && msg->len > mchan->resp_buf_size) { + dev_err(dev, "channel %d message length %u > max %lu\n", + mchan->chan_type, (unsigned int)msg->len, + mchan->resp_buf_size); + return -EINVAL; + } + if (msg && msg->len) + memcpy_toio(mchan->resp_buf, msg->data, msg->len); + arg0 = SMC_IPI_MAILBOX_ACK; + zynqmp_ipi_fw_call(ipi_mbox, arg0, IPI_SMC_ACK_EIRQ_MASK, + &res); + } + return 0; +} + +/** + * zynqmp_ipi_startup - Startup the IPI channel + * + * @chan: Channel pointer + * + * Return: 0 if all goes good, else return corresponding error message + */ +static int zynqmp_ipi_startup(struct mbox_chan *chan) +{ + struct device *dev = chan->mbox->dev; + struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); + struct zynqmp_ipi_mchan *mchan = chan->con_priv; + u64 arg0; + struct arm_smccc_res res; + int ret = 0; + unsigned int nchan_type; + + if (mchan->is_opened) + return 0; + + /* If no channel has been opened, open the IPI mailbox */ + nchan_type = (mchan->chan_type + 1) % 2; + if (!ipi_mbox->mchans[nchan_type].is_opened) { + arg0 = SMC_IPI_MAILBOX_OPEN; + zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); + /* Check the SMC call status, a0 of the result */ + ret = (int)(res.a0 & 0xFFFFFFFF); + if (ret < 0) { + dev_err(dev, "SMC to open the IPI channel failed.\n"); + return ret; + } + ret = 0; + } + + /* If it is RX channel, enable the IPI notification interrupt */ + if (mchan->chan_type == IPI_MB_CHNL_RX) { + arg0 = SMC_IPI_MAILBOX_ENABLE_IRQ; + zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); + } + mchan->is_opened = 1; + + return ret; +} + +/** + * zynqmp_ipi_shutdown - Shutdown the IPI channel + * + * @chan: Channel pointer + */ +static void zynqmp_ipi_shutdown(struct mbox_chan *chan) +{ + struct device *dev = chan->mbox->dev; + struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); + struct zynqmp_ipi_mchan *mchan = chan->con_priv; + u64 arg0; + struct arm_smccc_res res; + unsigned int chan_type; + + if (!mchan->is_opened) + return; + + /* If it is RX channel, disable notification interrupt */ + chan_type = mchan->chan_type; + if (chan_type == IPI_MB_CHNL_RX) { + arg0 = SMC_IPI_MAILBOX_DISABLE_IRQ; + zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); + } + /* Release IPI mailbox if no other channel is opened */ + chan_type = (chan_type + 1) % 2; + if (!ipi_mbox->mchans[chan_type].is_opened) { + arg0 = SMC_IPI_MAILBOX_RELEASE; + zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); + } + + mchan->is_opened = 0; +} + +/* ZynqMP IPI mailbox operations */ +static const struct mbox_chan_ops zynqmp_ipi_chan_ops = { + .startup = zynqmp_ipi_startup, + .shutdown = zynqmp_ipi_shutdown, + .peek_data = zynqmp_ipi_peek_data, + .last_tx_done = zynqmp_ipi_last_tx_done, + .send_data = zynqmp_ipi_send_data, +}; + +/** + * zynqmp_ipi_of_xlate - Translate of phandle to IPI mailbox channel + * + * @mbox: mailbox controller pointer + * @p: phandle pointer + * + * Return: Mailbox channel, else return error pointer. + */ +static struct mbox_chan *zynqmp_ipi_of_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *p) +{ + struct mbox_chan *chan; + struct device *dev = mbox->dev; + unsigned int chan_type; + + /* Only supports TX and RX channels */ + chan_type = p->args[0]; + if (chan_type != IPI_MB_CHNL_TX && chan_type != IPI_MB_CHNL_RX) { + dev_err(dev, "req chnl failure: invalid chnl type %u.\n", + chan_type); + return ERR_PTR(-EINVAL); + } + chan = &mbox->chans[chan_type]; + return chan; +} + +static const struct of_device_id zynqmp_ipi_of_match[] = { + { .compatible = "xlnx,zynqmp-ipi-mailbox" }, + {}, +}; +MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match); + +/** + * zynqmp_ipi_mbox_get_buf_res - Get buffer resource from the IPI dev node + * + * @node: IPI mbox device child node + * @name: name of the IPI buffer + * @res: pointer to where the resource information will be stored. + * + * Return: 0 for success, negative value for failure + */ +static int zynqmp_ipi_mbox_get_buf_res(struct device_node *node, + const char *name, + struct resource *res) +{ + int ret, index; + + index = of_property_match_string(node, "reg-names", name); + if (index >= 0) { + ret = of_address_to_resource(node, index, res); + if (ret < 0) + return -EINVAL; + return 0; + } + return -ENODEV; +} + +/** + * zynqmp_ipi_mbox_dev_release() - release the existence of a ipi mbox dev + * + * @dev: the ipi mailbox device + * + * This is to avoid the no device release() function kernel warning. + * + */ +static void zynqmp_ipi_mbox_dev_release(struct device *dev) +{ + (void)dev; +} + +/** + * zynqmp_ipi_mbox_probe - probe IPI mailbox resource from device node + * + * @ipi_mbox: pointer to IPI mailbox private data structure + * @node: IPI mailbox device node + * + * Return: 0 for success, negative value for failure + */ +static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, + struct device_node *node) +{ + struct zynqmp_ipi_mchan *mchan; + struct mbox_chan *chans; + struct mbox_controller *mbox; + struct resource res; + struct device *dev, *mdev; + const char *name; + int ret; + + dev = ipi_mbox->pdata->dev; + /* Initialize dev for IPI mailbox */ + ipi_mbox->dev.parent = dev; + ipi_mbox->dev.release = NULL; + ipi_mbox->dev.of_node = node; + dev_set_name(&ipi_mbox->dev, "%s", of_node_full_name(node)); + dev_set_drvdata(&ipi_mbox->dev, ipi_mbox); + ipi_mbox->dev.release = zynqmp_ipi_mbox_dev_release; + ipi_mbox->dev.driver = &zynqmp_ipi_mbox_driver; + ret = device_register(&ipi_mbox->dev); + if (ret) { + dev_err(dev, "Failed to register ipi mbox dev.\n"); + return ret; + } + mdev = &ipi_mbox->dev; + + mchan = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; + name = "local_request_region"; + ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); + if (!ret) { + mchan->req_buf_size = resource_size(&res); + mchan->req_buf = devm_ioremap(mdev, res.start, + mchan->req_buf_size); + if (IS_ERR(mchan->req_buf)) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + ret = PTR_ERR(mchan->req_buf); + return ret; + } + } else if (ret != -ENODEV) { + dev_err(mdev, "Unmatched resource %s, %d.\n", name, ret); + return ret; + } + + name = "remote_response_region"; + ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); + if (!ret) { + mchan->resp_buf_size = resource_size(&res); + mchan->resp_buf = devm_ioremap(mdev, res.start, + mchan->resp_buf_size); + if (IS_ERR(mchan->resp_buf)) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + ret = PTR_ERR(mchan->resp_buf); + return ret; + } + } else if (ret != -ENODEV) { + dev_err(mdev, "Unmatched resource %s.\n", name); + return ret; + } + mchan->rx_buf = devm_kzalloc(mdev, + mchan->resp_buf_size + + sizeof(struct zynqmp_ipi_message), + GFP_KERNEL); + if (!mchan->rx_buf) + return -ENOMEM; + + mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; + name = "remote_request_region"; + ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); + if (!ret) { + mchan->req_buf_size = resource_size(&res); + mchan->req_buf = devm_ioremap(mdev, res.start, + mchan->req_buf_size); + if (IS_ERR(mchan->req_buf)) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + ret = PTR_ERR(mchan->req_buf); + return ret; + } + } else if (ret != -ENODEV) { + dev_err(mdev, "Unmatched resource %s.\n", name); + return ret; + } + + name = "local_response_region"; + ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); + if (!ret) { + mchan->resp_buf_size = resource_size(&res); + mchan->resp_buf = devm_ioremap(mdev, res.start, + mchan->resp_buf_size); + if (IS_ERR(mchan->resp_buf)) { + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); + ret = PTR_ERR(mchan->resp_buf); + return ret; + } + } else if (ret != -ENODEV) { + dev_err(mdev, "Unmatched resource %s.\n", name); + return ret; + } + mchan->rx_buf = devm_kzalloc(mdev, + mchan->resp_buf_size + + sizeof(struct zynqmp_ipi_message), + GFP_KERNEL); + if (!mchan->rx_buf) + return -ENOMEM; + + /* Get the IPI remote agent ID */ + ret = of_property_read_u32(node, "xlnx,ipi-id", &ipi_mbox->remote_id); + if (ret < 0) { + dev_err(dev, "No IPI remote ID is specified.\n"); + return ret; + } + + mbox = &ipi_mbox->mbox; + mbox->dev = mdev; + mbox->ops = &zynqmp_ipi_chan_ops; + mbox->num_chans = 2; + mbox->txdone_irq = false; + mbox->txdone_poll = true; + mbox->txpoll_period = 5; + mbox->of_xlate = zynqmp_ipi_of_xlate; + chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL); + if (!chans) + return -ENOMEM; + mbox->chans = chans; + chans[IPI_MB_CHNL_TX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; + chans[IPI_MB_CHNL_RX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; + ipi_mbox->mchans[IPI_MB_CHNL_TX].chan_type = IPI_MB_CHNL_TX; + ipi_mbox->mchans[IPI_MB_CHNL_RX].chan_type = IPI_MB_CHNL_RX; + ret = devm_mbox_controller_register(mdev, mbox); + if (ret) + dev_err(mdev, + "Failed to register mbox_controller(%d)\n", ret); + else + dev_info(mdev, + "Registered ZynqMP IPI mbox with TX/RX channels.\n"); + return ret; +} + +/** + * zynqmp_ipi_free_mboxes - Free IPI mailboxes devices + * + * @pdata: IPI private data + */ +static void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata) +{ + struct zynqmp_ipi_mbox *ipi_mbox; + int i; + + i = pdata->num_mboxes; + for (; i >= 0; i--) { + ipi_mbox = &pdata->ipi_mboxes[i]; + if (ipi_mbox->dev.parent) { + mbox_controller_unregister(&ipi_mbox->mbox); + device_unregister(&ipi_mbox->dev); + } + } +} + +static int zynqmp_ipi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *nc, *np = pdev->dev.of_node; + struct zynqmp_ipi_pdata *pdata; + struct zynqmp_ipi_mbox *mbox; + int num_mboxes, ret = -EINVAL; + + num_mboxes = of_get_child_count(np); + pdata = devm_kzalloc(dev, sizeof(*pdata) + (num_mboxes * sizeof(*mbox)), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + pdata->dev = dev; + + /* Get the IPI local agents ID */ + ret = of_property_read_u32(np, "xlnx,ipi-id", &pdata->local_id); + if (ret < 0) { + dev_err(dev, "No IPI local ID is specified.\n"); + return ret; + } + + pdata->num_mboxes = num_mboxes; + pdata->ipi_mboxes = (struct zynqmp_ipi_mbox *) + ((char *)pdata + sizeof(*pdata)); + + mbox = pdata->ipi_mboxes; + for_each_available_child_of_node(np, nc) { + mbox->pdata = pdata; + ret = zynqmp_ipi_mbox_probe(mbox, nc); + if (ret) { + dev_err(dev, "failed to probe subdev.\n"); + ret = -EINVAL; + goto free_mbox_dev; + } + mbox++; + } + + /* IPI IRQ */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "unable to find IPI IRQ.\n"); + goto free_mbox_dev; + } + pdata->irq = ret; + ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt, + IRQF_SHARED, dev_name(dev), pdata); + if (ret) { + dev_err(dev, "IRQ %d is not requested successfully.\n", + pdata->irq); + goto free_mbox_dev; + } + + platform_set_drvdata(pdev, pdata); + return ret; + +free_mbox_dev: + zynqmp_ipi_free_mboxes(pdata); + return ret; +} + +static int zynqmp_ipi_remove(struct platform_device *pdev) +{ + struct zynqmp_ipi_pdata *pdata; + + pdata = platform_get_drvdata(pdev); + zynqmp_ipi_free_mboxes(pdata); + + return 0; +} + +static struct platform_driver zynqmp_ipi_driver = { + .probe = zynqmp_ipi_probe, + .remove = zynqmp_ipi_remove, + .driver = { + .name = "zynqmp-ipi", + .of_match_table = of_match_ptr(zynqmp_ipi_of_match), + }, +}; + +static int __init zynqmp_ipi_init(void) +{ + return platform_driver_register(&zynqmp_ipi_driver); +} +subsys_initcall(zynqmp_ipi_init); + +static void __exit zynqmp_ipi_exit(void) +{ + platform_driver_unregister(&zynqmp_ipi_driver); +} +module_exit(zynqmp_ipi_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Xilinx ZynqMP IPI Mailbox driver"); +MODULE_AUTHOR("Xilinx Inc."); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index abb5d382f64d..3b6880dd648d 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -3939,6 +3939,8 @@ static int raid10_run(struct mddev *mddev) set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, "reshape"); + if (!mddev->sync_thread) + goto out_free_conf; } return 0; @@ -4670,7 +4672,6 @@ read_more: atomic_inc(&r10_bio->remaining); read_bio->bi_next = NULL; generic_make_request(read_bio); - sector_nr += nr_sectors; sectors_done += nr_sectors; if (sector_nr <= last) goto read_more; diff --git a/drivers/md/raid5-log.h b/drivers/md/raid5-log.h index bfb811407061..43c714a8798c 100644 --- a/drivers/md/raid5-log.h +++ b/drivers/md/raid5-log.h @@ -45,6 +45,7 @@ extern void ppl_stripe_write_finished(struct stripe_head *sh); extern int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add); extern void ppl_quiesce(struct r5conf *conf, int quiesce); extern int ppl_handle_flush_request(struct r5l_log *log, struct bio *bio); +extern struct md_sysfs_entry ppl_write_hint; static inline bool raid5_has_log(struct r5conf *conf) { diff --git a/drivers/md/raid5-ppl.c b/drivers/md/raid5-ppl.c index 3a7c36326589..17e9e7d51097 100644 --- a/drivers/md/raid5-ppl.c +++ b/drivers/md/raid5-ppl.c @@ -16,11 +16,11 @@ #include <linux/blkdev.h> #include <linux/slab.h> #include <linux/crc32c.h> -#include <linux/flex_array.h> #include <linux/async_tx.h> #include <linux/raid/md_p.h> #include "md.h" #include "raid5.h" +#include "raid5-log.h" /* * PPL consists of a 4KB header (struct ppl_header) and at least 128KB for @@ -116,6 +116,8 @@ struct ppl_conf { /* stripes to retry if failed to allocate io_unit */ struct list_head no_mem_stripes; spinlock_t no_mem_stripes_lock; + + unsigned short write_hint; }; struct ppl_log { @@ -165,7 +167,7 @@ ops_run_partial_parity(struct stripe_head *sh, struct raid5_percpu *percpu, struct dma_async_tx_descriptor *tx) { int disks = sh->disks; - struct page **srcs = flex_array_get(percpu->scribble, 0); + struct page **srcs = percpu->scribble; int count = 0, pd_idx = sh->pd_idx, i; struct async_submit_ctl submit; @@ -196,8 +198,7 @@ ops_run_partial_parity(struct stripe_head *sh, struct raid5_percpu *percpu, } init_async_submit(&submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_ZERO_DST, tx, - NULL, sh, flex_array_get(percpu->scribble, 0) - + sizeof(struct page *) * (sh->disks + 2)); + NULL, sh, (void *) (srcs + sh->disks + 2)); if (count == 1) tx = async_memcpy(sh->ppl_page, srcs[0], 0, 0, PAGE_SIZE, @@ -476,6 +477,7 @@ static void ppl_submit_iounit(struct ppl_io_unit *io) bio_set_dev(bio, log->rdev->bdev); bio->bi_iter.bi_sector = log->next_io_sector; bio_add_page(bio, io->header_page, PAGE_SIZE, 0); + bio->bi_write_hint = ppl_conf->write_hint; pr_debug("%s: log->current_io_sector: %llu\n", __func__, (unsigned long long)log->next_io_sector); @@ -505,6 +507,7 @@ static void ppl_submit_iounit(struct ppl_io_unit *io) bio = bio_alloc_bioset(GFP_NOIO, BIO_MAX_PAGES, &ppl_conf->bs); bio->bi_opf = prev->bi_opf; + bio->bi_write_hint = prev->bi_write_hint; bio_copy_dev(bio, prev); bio->bi_iter.bi_sector = bio_end_sector(prev); bio_add_page(bio, sh->ppl_page, PAGE_SIZE, 0); @@ -1409,6 +1412,7 @@ int ppl_init_log(struct r5conf *conf) atomic64_set(&ppl_conf->seq, 0); INIT_LIST_HEAD(&ppl_conf->no_mem_stripes); spin_lock_init(&ppl_conf->no_mem_stripes_lock); + ppl_conf->write_hint = RWF_WRITE_LIFE_NOT_SET; if (!mddev->external) { ppl_conf->signature = ~crc32c_le(~0, mddev->uuid, sizeof(mddev->uuid)); @@ -1503,3 +1507,60 @@ int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add) return ret; } + +static ssize_t +ppl_write_hint_show(struct mddev *mddev, char *buf) +{ + size_t ret = 0; + struct r5conf *conf; + struct ppl_conf *ppl_conf = NULL; + + spin_lock(&mddev->lock); + conf = mddev->private; + if (conf && raid5_has_ppl(conf)) + ppl_conf = conf->log_private; + ret = sprintf(buf, "%d\n", ppl_conf ? ppl_conf->write_hint : 0); + spin_unlock(&mddev->lock); + + return ret; +} + +static ssize_t +ppl_write_hint_store(struct mddev *mddev, const char *page, size_t len) +{ + struct r5conf *conf; + struct ppl_conf *ppl_conf; + int err = 0; + unsigned short new; + + if (len >= PAGE_SIZE) + return -EINVAL; + if (kstrtou16(page, 10, &new)) + return -EINVAL; + + err = mddev_lock(mddev); + if (err) + return err; + + conf = mddev->private; + if (!conf) { + err = -ENODEV; + } else if (raid5_has_ppl(conf)) { + ppl_conf = conf->log_private; + if (!ppl_conf) + err = -EINVAL; + else + ppl_conf->write_hint = new; + } else { + err = -EINVAL; + } + + mddev_unlock(mddev); + + return err ?: len; +} + +struct md_sysfs_entry +ppl_write_hint = __ATTR(ppl_write_hint, S_IRUGO | S_IWUSR, + ppl_write_hint_show, + ppl_write_hint_store); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index cecea901ab8c..c033bfcb209e 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -54,7 +54,6 @@ #include <linux/slab.h> #include <linux/ratelimit.h> #include <linux/nodemask.h> -#include <linux/flex_array.h> #include <trace/events/block.h> #include <linux/list_sort.h> @@ -1394,22 +1393,16 @@ static void ops_complete_compute(void *stripe_head_ref) } /* return a pointer to the address conversion region of the scribble buffer */ -static addr_conv_t *to_addr_conv(struct stripe_head *sh, - struct raid5_percpu *percpu, int i) +static struct page **to_addr_page(struct raid5_percpu *percpu, int i) { - void *addr; - - addr = flex_array_get(percpu->scribble, i); - return addr + sizeof(struct page *) * (sh->disks + 2); + return percpu->scribble + i * percpu->scribble_obj_size; } /* return a pointer to the address conversion region of the scribble buffer */ -static struct page **to_addr_page(struct raid5_percpu *percpu, int i) +static addr_conv_t *to_addr_conv(struct stripe_head *sh, + struct raid5_percpu *percpu, int i) { - void *addr; - - addr = flex_array_get(percpu->scribble, i); - return addr; + return (void *) (to_addr_page(percpu, i) + sh->disks + 2); } static struct dma_async_tx_descriptor * @@ -2238,21 +2231,23 @@ static int grow_stripes(struct r5conf *conf, int num) * calculate over all devices (not just the data blocks), using zeros in place * of the P and Q blocks. */ -static struct flex_array *scribble_alloc(int num, int cnt, gfp_t flags) +static int scribble_alloc(struct raid5_percpu *percpu, + int num, int cnt, gfp_t flags) { - struct flex_array *ret; - size_t len; + size_t obj_size = + sizeof(struct page *) * (num+2) + + sizeof(addr_conv_t) * (num+2); + void *scribble; - len = sizeof(struct page *) * (num+2) + sizeof(addr_conv_t) * (num+2); - ret = flex_array_alloc(len, cnt, flags); - if (!ret) - return NULL; - /* always prealloc all elements, so no locking is required */ - if (flex_array_prealloc(ret, 0, cnt, flags)) { - flex_array_free(ret); - return NULL; - } - return ret; + scribble = kvmalloc_array(cnt, obj_size, flags); + if (!scribble) + return -ENOMEM; + + kvfree(percpu->scribble); + + percpu->scribble = scribble; + percpu->scribble_obj_size = obj_size; + return 0; } static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors) @@ -2270,23 +2265,18 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors) return 0; mddev_suspend(conf->mddev); get_online_cpus(); + for_each_present_cpu(cpu) { struct raid5_percpu *percpu; - struct flex_array *scribble; percpu = per_cpu_ptr(conf->percpu, cpu); - scribble = scribble_alloc(new_disks, - new_sectors / STRIPE_SECTORS, - GFP_NOIO); - - if (scribble) { - flex_array_free(percpu->scribble); - percpu->scribble = scribble; - } else { - err = -ENOMEM; + err = scribble_alloc(percpu, new_disks, + new_sectors / STRIPE_SECTORS, + GFP_NOIO); + if (err) break; - } } + put_online_cpus(); mddev_resume(conf->mddev); if (!err) { @@ -6660,6 +6650,7 @@ static struct attribute *raid5_attrs[] = { &raid5_skip_copy.attr, &raid5_rmw_level.attr, &r5c_journal_mode.attr, + &ppl_write_hint.attr, NULL, }; static struct attribute_group raid5_attrs_group = { @@ -6742,25 +6733,26 @@ raid5_size(struct mddev *mddev, sector_t sectors, int raid_disks) static void free_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu) { safe_put_page(percpu->spare_page); - if (percpu->scribble) - flex_array_free(percpu->scribble); percpu->spare_page = NULL; + kvfree(percpu->scribble); percpu->scribble = NULL; } static int alloc_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu) { - if (conf->level == 6 && !percpu->spare_page) + if (conf->level == 6 && !percpu->spare_page) { percpu->spare_page = alloc_page(GFP_KERNEL); - if (!percpu->scribble) - percpu->scribble = scribble_alloc(max(conf->raid_disks, - conf->previous_raid_disks), - max(conf->chunk_sectors, - conf->prev_chunk_sectors) - / STRIPE_SECTORS, - GFP_KERNEL); - - if (!percpu->scribble || (conf->level == 6 && !percpu->spare_page)) { + if (!percpu->spare_page) + return -ENOMEM; + } + + if (scribble_alloc(percpu, + max(conf->raid_disks, + conf->previous_raid_disks), + max(conf->chunk_sectors, + conf->prev_chunk_sectors) + / STRIPE_SECTORS, + GFP_KERNEL)) { free_scratch_buffer(conf, percpu); return -ENOMEM; } @@ -7402,6 +7394,8 @@ static int raid5_run(struct mddev *mddev) set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, "reshape"); + if (!mddev->sync_thread) + goto abort; } /* Ok, everything is just fine now */ diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 8474c224127b..cf991f13403e 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -638,10 +638,11 @@ struct r5conf { /* per cpu variables */ struct raid5_percpu { struct page *spare_page; /* Used when checking P/Q in raid6 */ - struct flex_array *scribble; /* space for constructing buffer - * lists and performing address - * conversions - */ + void *scribble; /* space for constructing buffer + * lists and performing address + * conversions + */ + int scribble_obj_size; } __percpu *percpu; int scribble_disks; int scribble_sectors; diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 22547d7a84ea..947a8adbc799 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -974,6 +974,36 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, break; } + /* Check a specific PEB for bitflips and scrub it if needed */ + case UBI_IOCRPEB: + { + int pnum; + + err = get_user(pnum, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + err = ubi_bitflip_check(ubi, pnum, 0); + break; + } + + /* Force scrubbing for a specific PEB */ + case UBI_IOCSPEB: + { + int pnum; + + err = get_user(pnum, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + err = ubi_bitflip_check(ubi, pnum, 1); + break; + } + default: err = -ENOTTY; break; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index d47b9e436e67..a1b9e764d489 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -929,6 +929,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *used_e, int ubi_is_erase_work(struct ubi_work *wrk); void ubi_refill_pools(struct ubi_device *ubi); int ubi_ensure_anchor_pebs(struct ubi_device *ubi); +int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force_scrub); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 6f2ac865ff05..2709dc02fc24 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -278,6 +278,27 @@ static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) } /** + * in_pq - check if a wear-leveling entry is present in the protection queue. + * @ubi: UBI device description object + * @e: the wear-leveling entry to check + * + * This function returns non-zero if @e is in the protection queue and zero + * if it is not. + */ +static inline int in_pq(const struct ubi_device *ubi, struct ubi_wl_entry *e) +{ + struct ubi_wl_entry *p; + int i; + + for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) + list_for_each_entry(p, &ubi->pq[i], u.list) + if (p == e) + return 1; + + return 0; +} + +/** * prot_queue_add - add physical eraseblock to the protection queue. * @ubi: UBI device description object * @e: the physical eraseblock to add @@ -1419,6 +1440,150 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum) return err; } +static bool scrub_possible(struct ubi_device *ubi, struct ubi_wl_entry *e) +{ + if (in_wl_tree(e, &ubi->scrub)) + return false; + else if (in_wl_tree(e, &ubi->erroneous)) + return false; + else if (ubi->move_from == e) + return false; + else if (ubi->move_to == e) + return false; + + return true; +} + +/** + * ubi_bitflip_check - Check an eraseblock for bitflips and scrub it if needed. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to schedule + * @force: dont't read the block, assume bitflips happened and take action. + * + * This function reads the given eraseblock and checks if bitflips occured. + * In case of bitflips, the eraseblock is scheduled for scrubbing. + * If scrubbing is forced with @force, the eraseblock is not read, + * but scheduled for scrubbing right away. + * + * Returns: + * %EINVAL, PEB is out of range + * %ENOENT, PEB is no longer used by UBI + * %EBUSY, PEB cannot be checked now or a check is currently running on it + * %EAGAIN, bit flips happened but scrubbing is currently not possible + * %EUCLEAN, bit flips happened and PEB is scheduled for scrubbing + * %0, no bit flips detected + */ +int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force) +{ + int err; + struct ubi_wl_entry *e; + + if (pnum < 0 || pnum >= ubi->peb_count) { + err = -EINVAL; + goto out; + } + + /* + * Pause all parallel work, otherwise it can happen that the + * erase worker frees a wl entry under us. + */ + down_write(&ubi->work_sem); + + /* + * Make sure that the wl entry does not change state while + * inspecting it. + */ + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + if (!e) { + spin_unlock(&ubi->wl_lock); + err = -ENOENT; + goto out_resume; + } + + /* + * Does it make sense to check this PEB? + */ + if (!scrub_possible(ubi, e)) { + spin_unlock(&ubi->wl_lock); + err = -EBUSY; + goto out_resume; + } + spin_unlock(&ubi->wl_lock); + + if (!force) { + mutex_lock(&ubi->buf_mutex); + err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size); + mutex_unlock(&ubi->buf_mutex); + } + + if (force || err == UBI_IO_BITFLIPS) { + /* + * Okay, bit flip happened, let's figure out what we can do. + */ + spin_lock(&ubi->wl_lock); + + /* + * Recheck. We released wl_lock, UBI might have killed the + * wl entry under us. + */ + e = ubi->lookuptbl[pnum]; + if (!e) { + spin_unlock(&ubi->wl_lock); + err = -ENOENT; + goto out_resume; + } + + /* + * Need to re-check state + */ + if (!scrub_possible(ubi, e)) { + spin_unlock(&ubi->wl_lock); + err = -EBUSY; + goto out_resume; + } + + if (in_pq(ubi, e)) { + prot_queue_del(ubi, e->pnum); + wl_tree_add(e, &ubi->scrub); + spin_unlock(&ubi->wl_lock); + + err = ensure_wear_leveling(ubi, 1); + } else if (in_wl_tree(e, &ubi->used)) { + rb_erase(&e->u.rb, &ubi->used); + wl_tree_add(e, &ubi->scrub); + spin_unlock(&ubi->wl_lock); + + err = ensure_wear_leveling(ubi, 1); + } else if (in_wl_tree(e, &ubi->free)) { + rb_erase(&e->u.rb, &ubi->free); + ubi->free_count--; + spin_unlock(&ubi->wl_lock); + + /* + * This PEB is empty we can schedule it for + * erasure right away. No wear leveling needed. + */ + err = schedule_erase(ubi, e, UBI_UNKNOWN, UBI_UNKNOWN, + force ? 0 : 1, true); + } else { + spin_unlock(&ubi->wl_lock); + err = -EAGAIN; + } + + if (!err && !force) + err = -EUCLEAN; + } else { + err = 0; + } + +out_resume: + up_write(&ubi->work_sem); +out: + + return err; +} + /** * tree_destroy - destroy an RB-tree. * @ubi: UBI device description object @@ -1848,16 +2013,11 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi, static int self_check_in_pq(const struct ubi_device *ubi, struct ubi_wl_entry *e) { - struct ubi_wl_entry *p; - int i; - if (!ubi_dbg_chk_gen(ubi)) return 0; - for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) - list_for_each_entry(p, &ubi->pq[i], u.list) - if (p == e) - return 0; + if (in_pq(ubi, e)) + return 0; ubi_err(ubi, "self-check failed for PEB %d, EC %d, Protect queue", e->pnum, e->ec); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index d516def846ab..b388406ac0f5 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -127,7 +127,7 @@ static u8 *pcan_msg_init_empty(struct pcan_usb_pro_msg *pm, /* * add one record to a message being built */ -static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, u8 id, ...) +static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, int id, ...) { int len, i; u8 *pc; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 96728d1e9824..f4e2db44ad91 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -569,6 +569,9 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, goto restore_link; } + if (speed == SPEED_MAX && chip->info->ops->port_max_speed_mode) + mode = chip->info->ops->port_max_speed_mode(port); + if (chip->info->ops->port_set_pause) { err = chip->info->ops->port_set_pause(chip, port, pause); if (err) @@ -3067,6 +3070,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6341_port_set_speed, + .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3385,6 +3389,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3429,6 +3434,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, + .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3473,6 +3479,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3566,6 +3573,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3697,6 +3705,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6341_port_set_speed, + .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3872,6 +3881,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3920,6 +3930,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, + .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index adcf60779895..19c07dff0440 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -377,6 +377,9 @@ struct mv88e6xxx_ops { */ int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); + /* What interface mode should be used for maximum speed? */ + phy_interface_t (*port_max_speed_mode)(int port); + int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port); int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 0796c6feec55..dce84a2a65c7 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -312,6 +312,14 @@ int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return mv88e6xxx_port_set_speed(chip, port, speed, !port, true); } +phy_interface_t mv88e6341_port_max_speed_mode(int port) +{ + if (port == 5) + return PHY_INTERFACE_MODE_2500BASEX; + + return PHY_INTERFACE_MODE_NA; +} + /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) { @@ -345,6 +353,14 @@ int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return mv88e6xxx_port_set_speed(chip, port, speed, true, true); } +phy_interface_t mv88e6390_port_max_speed_mode(int port) +{ + if (port == 9 || port == 10) + return PHY_INTERFACE_MODE_2500BASEX; + + return PHY_INTERFACE_MODE_NA; +} + /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) { @@ -360,6 +376,14 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return mv88e6xxx_port_set_speed(chip, port, speed, true, true); } +phy_interface_t mv88e6390x_port_max_speed_mode(int port) +{ + if (port == 9 || port == 10) + return PHY_INTERFACE_MODE_XAUI; + + return PHY_INTERFACE_MODE_NA; +} + int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode) { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 4aadf321edb7..c7bed263a0f4 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -285,6 +285,10 @@ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +phy_interface_t mv88e6341_port_max_speed_mode(int port); +phy_interface_t mv88e6390_port_max_speed_mode(int port); +phy_interface_t mv88e6390x_port_max_speed_mode(int port); + int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c index 61e43802b9a5..645efac6310d 100644 --- a/drivers/net/ethernet/8390/pcnet_cs.c +++ b/drivers/net/ethernet/8390/pcnet_cs.c @@ -289,6 +289,11 @@ static struct hw_info *get_hwinfo(struct pcmcia_device *link) virt = ioremap(link->resource[2]->start, resource_size(link->resource[2])); + if (unlikely(!virt)) { + pcmcia_release_window(link, link->resource[2]); + return NULL; + } + for (i = 0; i < NR_INFO; i++) { pcmcia_map_mem_page(link, link->resource[2], hw_info[i].offset & ~(resource_size(link->resource[2])-1)); @@ -1423,6 +1428,11 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg, /* Try scribbling on the buffer */ info->base = ioremap(link->resource[3]->start, resource_size(link->resource[3])); + if (unlikely(!info->base)) { + ret = -ENOMEM; + goto failed; + } + for (i = 0; i < (TX_PAGES<<8); i += 2) __raw_writew((i>>1), info->base+offset+i); udelay(100); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index e21bf3724611..1c50c10b5a16 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -1211,6 +1211,11 @@ int liquidio_change_mtu(struct net_device *netdev, int new_mtu) sc = (struct octeon_soft_command *) octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 16, 0); + if (!sc) { + netif_info(lio, rx_err, lio->netdev, + "Failed to allocate soft command\n"); + return -ENOMEM; + } ncmd = (union octnet_cmd *)sc->virtdptr; @@ -1684,6 +1689,11 @@ int liquidio_set_fec(struct lio *lio, int on_off) sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, sizeof(struct oct_nic_seapi_resp), 0); + if (!sc) { + dev_err(&oct->pci_dev->dev, + "Failed to allocate soft command\n"); + return -ENOMEM; + } ncmd = sc->virtdptr; resp = sc->virtrptr; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 9b7819fdc9de..fb6f813cff65 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1192,6 +1192,11 @@ static void send_rx_ctrl_cmd(struct lio *lio, int start_stop) sc = (struct octeon_soft_command *) octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 16, 0); + if (!sc) { + netif_info(lio, rx_err, lio->netdev, + "Failed to allocate octeon_soft_command\n"); + return; + } ncmd = (union octnet_cmd *)sc->virtdptr; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 503cfadff4ac..aa2be4807191 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -2234,6 +2234,12 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) nic->nicvf_rx_mode_wq = alloc_ordered_workqueue("nicvf_rx_mode_wq_VF%d", WQ_MEM_RECLAIM, nic->vf_id); + if (!nic->nicvf_rx_mode_wq) { + err = -ENOMEM; + dev_err(dev, "Failed to allocate work queue\n"); + goto err_unregister_interrupts; + } + INIT_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task); spin_lock_init(&nic->rx_mode_wq_lock); mutex_init(&nic->rx_mode_mtx); diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 9a7f70db20c7..733d9172425b 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -119,7 +119,7 @@ static void enic_init_affinity_hint(struct enic *enic) for (i = 0; i < enic->intr_count; i++) { if (enic_is_err_intr(enic, i) || enic_is_notify_intr(enic, i) || - (enic->msix[i].affinity_mask && + (cpumask_available(enic->msix[i].affinity_mask) && !cpumask_empty(enic->msix[i].affinity_mask))) continue; if (zalloc_cpumask_var(&enic->msix[i].affinity_mask, @@ -148,7 +148,7 @@ static void enic_set_affinity_hint(struct enic *enic) for (i = 0; i < enic->intr_count; i++) { if (enic_is_err_intr(enic, i) || enic_is_notify_intr(enic, i) || - !enic->msix[i].affinity_mask || + !cpumask_available(enic->msix[i].affinity_mask) || cpumask_empty(enic->msix[i].affinity_mask)) continue; err = irq_set_affinity_hint(enic->msix_entry[i].vector, @@ -161,7 +161,7 @@ static void enic_set_affinity_hint(struct enic *enic) for (i = 0; i < enic->wq_count; i++) { int wq_intr = enic_msix_wq_intr(enic, i); - if (enic->msix[wq_intr].affinity_mask && + if (cpumask_available(enic->msix[wq_intr].affinity_mask) && !cpumask_empty(enic->msix[wq_intr].affinity_mask)) netif_set_xps_queue(enic->netdev, enic->msix[wq_intr].affinity_mask, diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c index a69cd19a55ae..1eca0fdb9933 100644 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -547,6 +547,11 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) return -1; base = ioremap(link->resource[2]->start, resource_size(link->resource[2])); + if (!base) { + pcmcia_release_window(link, link->resource[2]); + return -ENOMEM; + } + pcmcia_map_mem_page(link, link->resource[2], 0); /* diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index 66d7a8b80e76..38b430f11fc1 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -194,6 +194,7 @@ struct hnae3_ae_dev { const struct hnae3_ae_ops *ops; struct list_head node; u32 flag; + u8 override_pci_need_reset; /* fix to stop multiple reset happening */ enum hnae3_dev_type dev_type; enum hnae3_reset_type reset_type; void *priv; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 3cb43b1f1c2e..1c1f17ec6be2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -1850,7 +1850,9 @@ static pci_ers_result_t hns3_slot_reset(struct pci_dev *pdev) /* request the reset */ if (ae_dev->ops->reset_event) { - ae_dev->ops->reset_event(pdev, NULL); + if (!ae_dev->override_pci_need_reset) + ae_dev->ops->reset_event(pdev, NULL); + return PCI_ERS_RESULT_RECOVERED; } @@ -2321,8 +2323,8 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb, if (!(bd_base_info & BIT(HNS3_RXD_L3L4P_B))) return; - if (unlikely(l234info & (BIT(HNS3_RXD_L3E_B) | BIT(HNS3_RXD_L4E_B) || - BIT(HNS3_RXD_OL3E_B) || + if (unlikely(l234info & (BIT(HNS3_RXD_L3E_B) | BIT(HNS3_RXD_L4E_B) | + BIT(HNS3_RXD_OL3E_B) | BIT(HNS3_RXD_OL4E_B)))) { u64_stats_update_begin(&ring->syncp); ring->stats.l3l4_csum_err++; @@ -2472,6 +2474,8 @@ static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc, desc = &ring->desc[ring->next_to_clean]; desc_cb = &ring->desc_cb[ring->next_to_clean]; bd_base_info = le32_to_cpu(desc->rx.bd_base_info); + /* make sure HW write desc complete */ + dma_rmb(); if (!(bd_base_info & BIT(HNS3_RXD_VLD_B))) return -ENXIO; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c index 1feceff1477c..1f52d11f77b5 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c @@ -1317,8 +1317,10 @@ pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev) hclge_handle_all_ras_errors(hdev); } else { if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) || - hdev->pdev->revision < 0x21) + hdev->pdev->revision < 0x21) { + ae_dev->override_pci_need_reset = 1; return PCI_ERS_RESULT_RECOVERED; + } } if (status & HCLGE_RAS_REG_ROCEE_ERR_MASK) { @@ -1327,8 +1329,11 @@ pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev) } if (status & HCLGE_RAS_REG_NFE_MASK || - status & HCLGE_RAS_REG_ROCEE_ERR_MASK) + status & HCLGE_RAS_REG_ROCEE_ERR_MASK) { + ae_dev->override_pci_need_reset = 0; return PCI_ERS_RESULT_NEED_RESET; + } + ae_dev->override_pci_need_reset = 1; return PCI_ERS_RESULT_RECOVERED; } diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index c19e74e6ac94..a5d5d6fc1da0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2645,6 +2645,8 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) if (!priv->cmd.context) return -ENOMEM; + if (mlx4_is_mfunc(dev)) + mutex_lock(&priv->cmd.slave_cmd_mutex); down_write(&priv->cmd.switch_sem); for (i = 0; i < priv->cmd.max_cmds; ++i) { priv->cmd.context[i].token = i; @@ -2670,6 +2672,8 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) down(&priv->cmd.poll_sem); priv->cmd.use_events = 1; up_write(&priv->cmd.switch_sem); + if (mlx4_is_mfunc(dev)) + mutex_unlock(&priv->cmd.slave_cmd_mutex); return err; } @@ -2682,6 +2686,8 @@ void mlx4_cmd_use_polling(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int i; + if (mlx4_is_mfunc(dev)) + mutex_lock(&priv->cmd.slave_cmd_mutex); down_write(&priv->cmd.switch_sem); priv->cmd.use_events = 0; @@ -2689,9 +2695,12 @@ void mlx4_cmd_use_polling(struct mlx4_dev *dev) down(&priv->cmd.event_sem); kfree(priv->cmd.context); + priv->cmd.context = NULL; up(&priv->cmd.poll_sem); up_write(&priv->cmd.switch_sem); + if (mlx4_is_mfunc(dev)) + mutex_unlock(&priv->cmd.slave_cmd_mutex); } struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index eb13d3618162..4356f3a58002 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2719,13 +2719,13 @@ static int qp_get_mtt_size(struct mlx4_qp_context *qpc) int total_pages; int total_mem; int page_offset = (be32_to_cpu(qpc->params2) >> 6) & 0x3f; + int tot; sq_size = 1 << (log_sq_size + log_sq_sride + 4); rq_size = (srq|rss|xrc) ? 0 : (1 << (log_rq_size + log_rq_stride + 4)); total_mem = sq_size + rq_size; - total_pages = - roundup_pow_of_two((total_mem + (page_offset << 6)) >> - page_shift); + tot = (total_mem + (page_offset << 6)) >> page_shift; + total_pages = !tot ? 1 : roundup_pow_of_two(tot); return total_pages; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 0804b478ad19..a0987cc5fe4a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -424,6 +424,9 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { priv->channels.params = new_channels.params; + if (!netif_is_rxfh_configured(priv->netdev)) + mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt, + MLX5E_INDIR_RQT_SIZE, count); goto out; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index a1a3e2774989..a66b6ed80b30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -1129,16 +1129,17 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *rep = rpriv->rep; - int ret, pf_num; + unsigned int fn; + int ret; - ret = mlx5_lag_get_pf_num(priv->mdev, &pf_num); - if (ret) - return ret; + fn = PCI_FUNC(priv->mdev->pdev->devfn); + if (fn >= MLX5_MAX_PORTS) + return -EOPNOTSUPP; if (rep->vport == MLX5_VPORT_UPLINK) - ret = snprintf(buf, len, "p%d", pf_num); + ret = snprintf(buf, len, "p%d", fn); else - ret = snprintf(buf, len, "pf%dvf%d", pf_num, rep->vport - 1); + ret = snprintf(buf, len, "pf%dvf%d", fn, rep->vport - 1); if (ret >= len) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index be396e5e4e39..3dde5c7e0739 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -1295,8 +1295,14 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq, skb->protocol = *((__be16 *)(skb->data)); - skb->ip_summed = CHECKSUM_COMPLETE; - skb->csum = csum_unfold((__force __sum16)cqe->check_sum); + if (netdev->features & NETIF_F_RXCSUM) { + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = csum_unfold((__force __sum16)cqe->check_sum); + stats->csum_complete++; + } else { + skb->ip_summed = CHECKSUM_NONE; + stats->csum_none++; + } if (unlikely(mlx5e_rx_hw_stamp(tstamp))) skb_hwtstamps(skb)->hwtstamp = @@ -1315,7 +1321,6 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq, skb->dev = netdev; - stats->csum_complete++; stats->packets++; stats->bytes += cqe_bcnt; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index d0b28251abf2..ecd2c747f726 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1931,7 +1931,7 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, u64 node_guid; int err = 0; - if (!MLX5_CAP_GEN(esw->dev, vport_group_manager)) + if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager)) return -EPERM; if (!LEGAL_VPORT(esw, vport) || is_multicast_ether_addr(mac)) return -EINVAL; @@ -2005,7 +2005,7 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, { struct mlx5_vport *evport; - if (!MLX5_CAP_GEN(esw->dev, vport_group_manager)) + if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager)) return -EPERM; if (!LEGAL_VPORT(esw, vport)) return -EINVAL; @@ -2297,19 +2297,24 @@ static int normalize_vports_min_rate(struct mlx5_eswitch *esw, u32 divider) int mlx5_eswitch_set_vport_rate(struct mlx5_eswitch *esw, int vport, u32 max_rate, u32 min_rate) { - u32 fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); - bool min_rate_supported = MLX5_CAP_QOS(esw->dev, esw_bw_share) && - fw_max_bw_share >= MLX5_MIN_BW_SHARE; - bool max_rate_supported = MLX5_CAP_QOS(esw->dev, esw_rate_limit); struct mlx5_vport *evport; + u32 fw_max_bw_share; u32 previous_min_rate; u32 divider; + bool min_rate_supported; + bool max_rate_supported; int err = 0; if (!ESW_ALLOWED(esw)) return -EPERM; if (!LEGAL_VPORT(esw, vport)) return -EINVAL; + + fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); + min_rate_supported = MLX5_CAP_QOS(esw->dev, esw_bw_share) && + fw_max_bw_share >= MLX5_MIN_BW_SHARE; + max_rate_supported = MLX5_CAP_QOS(esw->dev, esw_rate_limit); + if ((min_rate && !min_rate_supported) || (max_rate && !max_rate_supported)) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index f2cfa012315e..0be3eb86dd84 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -263,10 +263,11 @@ static void nested_down_write_ref_node(struct fs_node *node, } } -static void down_write_ref_node(struct fs_node *node) +static void down_write_ref_node(struct fs_node *node, bool locked) { if (node) { - down_write(&node->lock); + if (!locked) + down_write(&node->lock); refcount_inc(&node->refcount); } } @@ -277,13 +278,14 @@ static void up_read_ref_node(struct fs_node *node) up_read(&node->lock); } -static void up_write_ref_node(struct fs_node *node) +static void up_write_ref_node(struct fs_node *node, bool locked) { refcount_dec(&node->refcount); - up_write(&node->lock); + if (!locked) + up_write(&node->lock); } -static void tree_put_node(struct fs_node *node) +static void tree_put_node(struct fs_node *node, bool locked) { struct fs_node *parent_node = node->parent; @@ -294,27 +296,27 @@ static void tree_put_node(struct fs_node *node) /* Only root namespace doesn't have parent and we just * need to free its node. */ - down_write_ref_node(parent_node); + down_write_ref_node(parent_node, locked); list_del_init(&node->list); if (node->del_sw_func) node->del_sw_func(node); - up_write_ref_node(parent_node); + up_write_ref_node(parent_node, locked); } else { kfree(node); } node = NULL; } if (!node && parent_node) - tree_put_node(parent_node); + tree_put_node(parent_node, locked); } -static int tree_remove_node(struct fs_node *node) +static int tree_remove_node(struct fs_node *node, bool locked) { if (refcount_read(&node->refcount) > 1) { refcount_dec(&node->refcount); return -EEXIST; } - tree_put_node(node); + tree_put_node(node, locked); return 0; } @@ -420,22 +422,34 @@ static void del_sw_flow_table(struct fs_node *node) kfree(ft); } -static void del_sw_hw_rule(struct fs_node *node) +static void modify_fte(struct fs_fte *fte) { struct mlx5_flow_root_namespace *root; - struct mlx5_flow_rule *rule; struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; - struct fs_fte *fte; - int modify_mask; - struct mlx5_core_dev *dev = get_dev(node); + struct mlx5_core_dev *dev; int err; - bool update_fte = false; - fs_get_obj(rule, node); - fs_get_obj(fte, rule->node.parent); fs_get_obj(fg, fte->node.parent); fs_get_obj(ft, fg->node.parent); + dev = get_dev(&fte->node); + + root = find_root(&ft->node); + err = root->cmds->update_fte(dev, ft, fg->id, fte->modify_mask, fte); + if (err) + mlx5_core_warn(dev, + "%s can't del rule fg id=%d fte_index=%d\n", + __func__, fg->id, fte->index); + fte->modify_mask = 0; +} + +static void del_sw_hw_rule(struct fs_node *node) +{ + struct mlx5_flow_rule *rule; + struct fs_fte *fte; + + fs_get_obj(rule, node); + fs_get_obj(fte, rule->node.parent); trace_mlx5_fs_del_rule(rule); if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { mutex_lock(&rule->dest_attr.ft->lock); @@ -445,27 +459,19 @@ static void del_sw_hw_rule(struct fs_node *node) if (rule->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER && --fte->dests_size) { - modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION) | - BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS); + fte->modify_mask |= + BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION) | + BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS); fte->action.action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT; - update_fte = true; goto out; } if ((fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) && --fte->dests_size) { - modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST); - update_fte = true; + fte->modify_mask |= + BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST); } out: - root = find_root(&ft->node); - if (update_fte && fte->dests_size) { - err = root->cmds->update_fte(dev, ft, fg->id, modify_mask, fte); - if (err) - mlx5_core_warn(dev, - "%s can't del rule fg id=%d fte_index=%d\n", - __func__, fg->id, fte->index); - } kfree(rule); } @@ -491,6 +497,7 @@ static void del_hw_fte(struct fs_node *node) mlx5_core_warn(dev, "flow steering can't delete fte in index %d of flow group id %d\n", fte->index, fg->id); + node->active = 0; } } @@ -591,7 +598,7 @@ static struct fs_fte *alloc_fte(struct mlx5_flow_table *ft, fte->node.type = FS_TYPE_FLOW_ENTRY; fte->action = *flow_act; - tree_init_node(&fte->node, del_hw_fte, del_sw_fte); + tree_init_node(&fte->node, NULL, del_sw_fte); return fte; } @@ -858,7 +865,7 @@ static int _mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, fs_get_obj(fte, rule->node.parent); if (!(fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST)) return -EINVAL; - down_write_ref_node(&fte->node); + down_write_ref_node(&fte->node, false); fs_get_obj(fg, fte->node.parent); fs_get_obj(ft, fg->node.parent); @@ -866,7 +873,7 @@ static int _mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, root = find_root(&ft->node); err = root->cmds->update_fte(get_dev(&ft->node), ft, fg->id, modify_mask, fte); - up_write_ref_node(&fte->node); + up_write_ref_node(&fte->node, false); return err; } @@ -1016,11 +1023,11 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa if (err) goto destroy_ft; ft->node.active = true; - down_write_ref_node(&fs_prio->node); + down_write_ref_node(&fs_prio->node, false); tree_add_node(&ft->node, &fs_prio->node); list_add_flow_table(ft, fs_prio); fs_prio->num_ft++; - up_write_ref_node(&fs_prio->node); + up_write_ref_node(&fs_prio->node, false); mutex_unlock(&root->chain_lock); trace_mlx5_fs_add_ft(ft); return ft; @@ -1114,17 +1121,17 @@ struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, if (ft->autogroup.active) return ERR_PTR(-EPERM); - down_write_ref_node(&ft->node); + down_write_ref_node(&ft->node, false); fg = alloc_insert_flow_group(ft, match_criteria_enable, match_criteria, start_index, end_index, ft->node.children.prev); - up_write_ref_node(&ft->node); + up_write_ref_node(&ft->node, false); if (IS_ERR(fg)) return fg; err = root->cmds->create_flow_group(dev, ft, fg_in, &fg->id); if (err) { - tree_put_node(&fg->node); + tree_put_node(&fg->node, false); return ERR_PTR(err); } trace_mlx5_fs_add_fg(fg); @@ -1521,10 +1528,10 @@ static void free_match_list(struct match_list_head *head) struct match_list *iter, *match_tmp; list_del(&head->first.list); - tree_put_node(&head->first.g->node); + tree_put_node(&head->first.g->node, false); list_for_each_entry_safe(iter, match_tmp, &head->list, list) { - tree_put_node(&iter->g->node); + tree_put_node(&iter->g->node, false); list_del(&iter->list); kfree(iter); } @@ -1601,11 +1608,16 @@ lookup_fte_locked(struct mlx5_flow_group *g, fte_tmp = NULL; goto out; } + if (!fte_tmp->node.active) { + tree_put_node(&fte_tmp->node, false); + fte_tmp = NULL; + goto out; + } nested_down_write_ref_node(&fte_tmp->node, FS_LOCK_CHILD); out: if (take_write) - up_write_ref_node(&g->node); + up_write_ref_node(&g->node, false); else up_read_ref_node(&g->node); return fte_tmp; @@ -1647,8 +1659,8 @@ search_again_locked: continue; rule = add_rule_fg(g, spec->match_value, flow_act, dest, dest_num, fte_tmp); - up_write_ref_node(&fte_tmp->node); - tree_put_node(&fte_tmp->node); + up_write_ref_node(&fte_tmp->node, false); + tree_put_node(&fte_tmp->node, false); kmem_cache_free(steering->ftes_cache, fte); return rule; } @@ -1684,7 +1696,7 @@ skip_search: err = insert_fte(g, fte); if (err) { - up_write_ref_node(&g->node); + up_write_ref_node(&g->node, false); if (err == -ENOSPC) continue; kmem_cache_free(steering->ftes_cache, fte); @@ -1692,11 +1704,11 @@ skip_search: } nested_down_write_ref_node(&fte->node, FS_LOCK_CHILD); - up_write_ref_node(&g->node); + up_write_ref_node(&g->node, false); rule = add_rule_fg(g, spec->match_value, flow_act, dest, dest_num, fte); - up_write_ref_node(&fte->node); - tree_put_node(&fte->node); + up_write_ref_node(&fte->node, false); + tree_put_node(&fte->node, false); return rule; } rule = ERR_PTR(-ENOENT); @@ -1738,7 +1750,7 @@ search_again_locked: err = build_match_list(&match_head, ft, spec); if (err) { if (take_write) - up_write_ref_node(&ft->node); + up_write_ref_node(&ft->node, false); else up_read_ref_node(&ft->node); return ERR_PTR(err); @@ -1753,7 +1765,7 @@ search_again_locked: if (!IS_ERR(rule) || (PTR_ERR(rule) != -ENOENT && PTR_ERR(rule) != -EAGAIN)) { if (take_write) - up_write_ref_node(&ft->node); + up_write_ref_node(&ft->node, false); return rule; } @@ -1769,12 +1781,12 @@ search_again_locked: g = alloc_auto_flow_group(ft, spec); if (IS_ERR(g)) { rule = ERR_CAST(g); - up_write_ref_node(&ft->node); + up_write_ref_node(&ft->node, false); return rule; } nested_down_write_ref_node(&g->node, FS_LOCK_PARENT); - up_write_ref_node(&ft->node); + up_write_ref_node(&ft->node, false); err = create_auto_flow_group(ft, g); if (err) @@ -1793,17 +1805,17 @@ search_again_locked: } nested_down_write_ref_node(&fte->node, FS_LOCK_CHILD); - up_write_ref_node(&g->node); + up_write_ref_node(&g->node, false); rule = add_rule_fg(g, spec->match_value, flow_act, dest, dest_num, fte); - up_write_ref_node(&fte->node); - tree_put_node(&fte->node); - tree_put_node(&g->node); + up_write_ref_node(&fte->node, false); + tree_put_node(&fte->node, false); + tree_put_node(&g->node, false); return rule; err_release_fg: - up_write_ref_node(&g->node); - tree_put_node(&g->node); + up_write_ref_node(&g->node, false); + tree_put_node(&g->node, false); return ERR_PTR(err); } @@ -1866,10 +1878,33 @@ EXPORT_SYMBOL(mlx5_add_flow_rules); void mlx5_del_flow_rules(struct mlx5_flow_handle *handle) { + struct fs_fte *fte; int i; + /* In order to consolidate the HW changes we lock the FTE for other + * changes, and increase its refcount, in order not to perform the + * "del" functions of the FTE. Will handle them here. + * The removal of the rules is done under locked FTE. + * After removing all the handle's rules, if there are remaining + * rules, it means we just need to modify the FTE in FW, and + * unlock/decrease the refcount we increased before. + * Otherwise, it means the FTE should be deleted. First delete the + * FTE in FW. Then, unlock the FTE, and proceed the tree_put_node of + * the FTE, which will handle the last decrease of the refcount, as + * well as required handling of its parent. + */ + fs_get_obj(fte, handle->rule[0]->node.parent); + down_write_ref_node(&fte->node, false); for (i = handle->num_rules - 1; i >= 0; i--) - tree_remove_node(&handle->rule[i]->node); + tree_remove_node(&handle->rule[i]->node, true); + if (fte->modify_mask && fte->dests_size) { + modify_fte(fte); + up_write_ref_node(&fte->node, false); + } else { + del_hw_fte(&fte->node); + up_write(&fte->node.lock); + tree_put_node(&fte->node, false); + } kfree(handle); } EXPORT_SYMBOL(mlx5_del_flow_rules); @@ -1972,7 +2007,7 @@ int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) mutex_unlock(&root->chain_lock); return err; } - if (tree_remove_node(&ft->node)) + if (tree_remove_node(&ft->node, false)) mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n", ft->id); mutex_unlock(&root->chain_lock); @@ -1983,7 +2018,7 @@ EXPORT_SYMBOL(mlx5_destroy_flow_table); void mlx5_destroy_flow_group(struct mlx5_flow_group *fg) { - if (tree_remove_node(&fg->node)) + if (tree_remove_node(&fg->node, false)) mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n", fg->id); } @@ -2367,8 +2402,8 @@ static void clean_tree(struct fs_node *node) tree_get_node(node); list_for_each_entry_safe(iter, temp, &node->children, list) clean_tree(iter); - tree_put_node(node); - tree_remove_node(node); + tree_put_node(node, false); + tree_remove_node(node, false); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 2dc86347af58..87de0e4d9124 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -172,6 +172,7 @@ struct fs_fte { enum fs_fte_status status; struct mlx5_fc *counter; struct rhash_head hash; + int modify_mask; }; /* Type of children is mlx5_flow_table/namespace */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag.c index 48aa6e030bcf..959605559858 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag.c @@ -595,27 +595,6 @@ void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev) err); } -int mlx5_lag_get_pf_num(struct mlx5_core_dev *dev, int *pf_num) -{ - struct mlx5_lag *ldev; - int n; - - ldev = mlx5_lag_dev_get(dev); - if (!ldev) { - mlx5_core_warn(dev, "no lag device, can't get pf num\n"); - return -EINVAL; - } - - for (n = 0; n < MLX5_MAX_PORTS; n++) - if (ldev->pf[n].dev == dev) { - *pf_num = n; - return 0; - } - - mlx5_core_warn(dev, "wasn't able to locate pf in the lag device\n"); - return -EINVAL; -} - /* Must be called with intf_mutex held */ void mlx5_lag_remove(struct mlx5_core_dev *dev) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 9529cf9623e3..7b331674622c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -188,8 +188,6 @@ static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev) MLX5_CAP_GEN(dev, lag_master); } -int mlx5_lag_get_pf_num(struct mlx5_core_dev *dev, int *pf_num); - void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol); void mlx5_lag_update(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c index 370ca94b6775..b8ba74de9555 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c @@ -40,6 +40,9 @@ #include "mlx5_core.h" #include "lib/eq.h" +static int mlx5_core_drain_dct(struct mlx5_core_dev *dev, + struct mlx5_core_dct *dct); + static struct mlx5_core_rsc_common * mlx5_get_rsc(struct mlx5_qp_table *table, u32 rsn) { @@ -227,20 +230,49 @@ static void destroy_resource_common(struct mlx5_core_dev *dev, wait_for_completion(&qp->common.free); } +static int _mlx5_core_destroy_dct(struct mlx5_core_dev *dev, + struct mlx5_core_dct *dct, bool need_cleanup) +{ + u32 out[MLX5_ST_SZ_DW(destroy_dct_out)] = {0}; + u32 in[MLX5_ST_SZ_DW(destroy_dct_in)] = {0}; + struct mlx5_core_qp *qp = &dct->mqp; + int err; + + err = mlx5_core_drain_dct(dev, dct); + if (err) { + if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + goto destroy; + } else { + mlx5_core_warn( + dev, "failed drain DCT 0x%x with error 0x%x\n", + qp->qpn, err); + return err; + } + } + wait_for_completion(&dct->drained); +destroy: + if (need_cleanup) + destroy_resource_common(dev, &dct->mqp); + MLX5_SET(destroy_dct_in, in, opcode, MLX5_CMD_OP_DESTROY_DCT); + MLX5_SET(destroy_dct_in, in, dctn, qp->qpn); + MLX5_SET(destroy_dct_in, in, uid, qp->uid); + err = mlx5_cmd_exec(dev, (void *)&in, sizeof(in), + (void *)&out, sizeof(out)); + return err; +} + int mlx5_core_create_dct(struct mlx5_core_dev *dev, struct mlx5_core_dct *dct, - u32 *in, int inlen) + u32 *in, int inlen, + u32 *out, int outlen) { - u32 out[MLX5_ST_SZ_DW(create_dct_out)] = {0}; - u32 din[MLX5_ST_SZ_DW(destroy_dct_in)] = {0}; - u32 dout[MLX5_ST_SZ_DW(destroy_dct_out)] = {0}; struct mlx5_core_qp *qp = &dct->mqp; int err; init_completion(&dct->drained); MLX5_SET(create_dct_in, in, opcode, MLX5_CMD_OP_CREATE_DCT); - err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out)); + err = mlx5_cmd_exec(dev, in, inlen, out, outlen); if (err) { mlx5_core_warn(dev, "create DCT failed, ret %d\n", err); return err; @@ -254,11 +286,7 @@ int mlx5_core_create_dct(struct mlx5_core_dev *dev, return 0; err_cmd: - MLX5_SET(destroy_dct_in, din, opcode, MLX5_CMD_OP_DESTROY_DCT); - MLX5_SET(destroy_dct_in, din, dctn, qp->qpn); - MLX5_SET(destroy_dct_in, din, uid, qp->uid); - mlx5_cmd_exec(dev, (void *)&in, sizeof(din), - (void *)&out, sizeof(dout)); + _mlx5_core_destroy_dct(dev, dct, false); return err; } EXPORT_SYMBOL_GPL(mlx5_core_create_dct); @@ -323,29 +351,7 @@ static int mlx5_core_drain_dct(struct mlx5_core_dev *dev, int mlx5_core_destroy_dct(struct mlx5_core_dev *dev, struct mlx5_core_dct *dct) { - u32 out[MLX5_ST_SZ_DW(destroy_dct_out)] = {0}; - u32 in[MLX5_ST_SZ_DW(destroy_dct_in)] = {0}; - struct mlx5_core_qp *qp = &dct->mqp; - int err; - - err = mlx5_core_drain_dct(dev, dct); - if (err) { - if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { - goto destroy; - } else { - mlx5_core_warn(dev, "failed drain DCT 0x%x with error 0x%x\n", qp->qpn, err); - return err; - } - } - wait_for_completion(&dct->drained); -destroy: - destroy_resource_common(dev, &dct->mqp); - MLX5_SET(destroy_dct_in, in, opcode, MLX5_CMD_OP_DESTROY_DCT); - MLX5_SET(destroy_dct_in, in, dctn, qp->qpn); - MLX5_SET(destroy_dct_in, in, uid, qp->uid); - err = mlx5_cmd_exec(dev, (void *)&in, sizeof(in), - (void *)&out, sizeof(out)); - return err; + return _mlx5_core_destroy_dct(dev, dct, true); } EXPORT_SYMBOL_GPL(mlx5_core_destroy_dct); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c index 0b85c7252f9e..472f63f9fac5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c @@ -111,7 +111,6 @@ struct mlxsw_thermal { struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; enum thermal_device_mode mode; struct mlxsw_thermal_module *tz_module_arr; - unsigned int tz_module_num; }; static inline u8 mlxsw_state_to_duty(int state) @@ -711,6 +710,9 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core, module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0); module_tz = &thermal->tz_module_arr[module]; + /* Skip if parent is already set (case of port split). */ + if (module_tz->parent) + return 0; module_tz->module = module; module_tz->parent = thermal; memcpy(module_tz->trips, default_thermal_trips, @@ -718,13 +720,7 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core, /* Initialize all trip point. */ mlxsw_thermal_module_trips_reset(module_tz); /* Update trip point according to the module data. */ - err = mlxsw_thermal_module_trips_update(dev, core, module_tz); - if (err) - return err; - - thermal->tz_module_num++; - - return 0; + return mlxsw_thermal_module_trips_update(dev, core, module_tz); } static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz) @@ -732,6 +728,7 @@ static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz) if (module_tz && module_tz->tzdev) { mlxsw_thermal_module_tz_fini(module_tz->tzdev); module_tz->tzdev = NULL; + module_tz->parent = NULL; } } @@ -740,6 +737,7 @@ mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core, struct mlxsw_thermal *thermal) { unsigned int module_count = mlxsw_core_max_ports(core); + struct mlxsw_thermal_module *module_tz; int i, err; thermal->tz_module_arr = kcalloc(module_count, @@ -754,8 +752,11 @@ mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core, goto err_unreg_tz_module_arr; } - for (i = 0; i < thermal->tz_module_num; i++) { - err = mlxsw_thermal_module_tz_init(&thermal->tz_module_arr[i]); + for (i = 0; i < module_count - 1; i++) { + module_tz = &thermal->tz_module_arr[i]; + if (!module_tz->parent) + continue; + err = mlxsw_thermal_module_tz_init(module_tz); if (err) goto err_unreg_tz_module_arr; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 68bee9572a1b..00c390024350 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -34,6 +34,18 @@ struct mlxsw_m_port { u8 module; }; +static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m) +{ + char spad_pl[MLXSW_REG_SPAD_LEN] = {0}; + int err; + + err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(spad), spad_pl); + if (err) + return err; + mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_m->base_mac); + return 0; +} + static int mlxsw_m_port_dummy_open_stop(struct net_device *dev) { return 0; @@ -314,6 +326,12 @@ static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, mlxsw_m->core = mlxsw_core; mlxsw_m->bus_info = mlxsw_bus_info; + err = mlxsw_m_base_mac_get(mlxsw_m); + if (err) { + dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n"); + return err; + } + err = mlxsw_m_ports_create(mlxsw_m); if (err) { dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n"); diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index 4d1b4a24907f..13e6bf13ac4d 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -585,8 +585,7 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter) if (adapter->csr.flags & LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) { - flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR | - LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET | + flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET | LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET | LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR | LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR; @@ -599,12 +598,6 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter) /* map TX interrupt to vector */ int_vec_map1 |= INT_VEC_MAP1_TX_VEC_(index, vector); lan743x_csr_write(adapter, INT_VEC_MAP1, int_vec_map1); - if (flags & - LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR) { - int_vec_en_auto_clr |= INT_VEC_EN_(vector); - lan743x_csr_write(adapter, INT_VEC_EN_AUTO_CLR, - int_vec_en_auto_clr); - } /* Remove TX interrupt from shared mask */ intr->vector_list[0].int_mask &= ~int_bit; @@ -1902,7 +1895,17 @@ static int lan743x_rx_next_index(struct lan743x_rx *rx, int index) return ((++index) % rx->ring_size); } -static int lan743x_rx_allocate_ring_element(struct lan743x_rx *rx, int index) +static struct sk_buff *lan743x_rx_allocate_skb(struct lan743x_rx *rx) +{ + int length = 0; + + length = (LAN743X_MAX_FRAME_SIZE + ETH_HLEN + 4 + RX_HEAD_PADDING); + return __netdev_alloc_skb(rx->adapter->netdev, + length, GFP_ATOMIC | GFP_DMA); +} + +static int lan743x_rx_init_ring_element(struct lan743x_rx *rx, int index, + struct sk_buff *skb) { struct lan743x_rx_buffer_info *buffer_info; struct lan743x_rx_descriptor *descriptor; @@ -1911,9 +1914,7 @@ static int lan743x_rx_allocate_ring_element(struct lan743x_rx *rx, int index) length = (LAN743X_MAX_FRAME_SIZE + ETH_HLEN + 4 + RX_HEAD_PADDING); descriptor = &rx->ring_cpu_ptr[index]; buffer_info = &rx->buffer_info[index]; - buffer_info->skb = __netdev_alloc_skb(rx->adapter->netdev, - length, - GFP_ATOMIC | GFP_DMA); + buffer_info->skb = skb; if (!(buffer_info->skb)) return -ENOMEM; buffer_info->dma_ptr = dma_map_single(&rx->adapter->pdev->dev, @@ -2060,8 +2061,19 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx) /* packet is available */ if (first_index == last_index) { /* single buffer packet */ + struct sk_buff *new_skb = NULL; int packet_length; + new_skb = lan743x_rx_allocate_skb(rx); + if (!new_skb) { + /* failed to allocate next skb. + * Memory is very low. + * Drop this packet and reuse buffer. + */ + lan743x_rx_reuse_ring_element(rx, first_index); + goto process_extension; + } + buffer_info = &rx->buffer_info[first_index]; skb = buffer_info->skb; descriptor = &rx->ring_cpu_ptr[first_index]; @@ -2081,7 +2093,7 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx) skb_put(skb, packet_length - 4); skb->protocol = eth_type_trans(skb, rx->adapter->netdev); - lan743x_rx_allocate_ring_element(rx, first_index); + lan743x_rx_init_ring_element(rx, first_index, new_skb); } else { int index = first_index; @@ -2094,26 +2106,23 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx) if (first_index <= last_index) { while ((index >= first_index) && (index <= last_index)) { - lan743x_rx_release_ring_element(rx, - index); - lan743x_rx_allocate_ring_element(rx, - index); + lan743x_rx_reuse_ring_element(rx, + index); index = lan743x_rx_next_index(rx, index); } } else { while ((index >= first_index) || (index <= last_index)) { - lan743x_rx_release_ring_element(rx, - index); - lan743x_rx_allocate_ring_element(rx, - index); + lan743x_rx_reuse_ring_element(rx, + index); index = lan743x_rx_next_index(rx, index); } } } +process_extension: if (extension_index >= 0) { descriptor = &rx->ring_cpu_ptr[extension_index]; buffer_info = &rx->buffer_info[extension_index]; @@ -2290,7 +2299,9 @@ static int lan743x_rx_ring_init(struct lan743x_rx *rx) rx->last_head = 0; for (index = 0; index < rx->ring_size; index++) { - ret = lan743x_rx_allocate_ring_element(rx, index); + struct sk_buff *new_skb = lan743x_rx_allocate_skb(rx); + + ret = lan743x_rx_init_ring_element(rx, index, new_skb); if (ret) goto cleanup; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 166d7f71442e..372adea10e14 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -392,7 +392,7 @@ #define NFP_NET_CFG_MBOX_SIMPLE_CMD 0x0 #define NFP_NET_CFG_MBOX_SIMPLE_RET 0x4 #define NFP_NET_CFG_MBOX_SIMPLE_VAL 0x8 -#define NFP_NET_CFG_MBOX_SIMPLE_LEN 0x12 +#define NFP_NET_CFG_MBOX_SIMPLE_LEN 12 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2 diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index c6238083e898..b4c8949933f1 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -1663,8 +1663,11 @@ static int qede_selftest_run_loopback(struct qede_dev *edev, u32 loopback_mode) /* Wait for loopback configuration to apply */ msleep_interruptible(500); - /* prepare the loopback packet */ - pkt_size = edev->ndev->mtu + ETH_HLEN; + /* Setting max packet size to 1.5K to avoid data being split over + * multiple BDs in cases where MTU > PAGE_SIZE. + */ + pkt_size = (((edev->ndev->mtu < ETH_DATA_LEN) ? + edev->ndev->mtu : ETH_DATA_LEN) + ETH_HLEN); skb = netdev_alloc_skb(edev->ndev, pkt_size); if (!skb) { diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 10b075bc5959..b61b88cbc0c7 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -3886,6 +3886,12 @@ static int ql3xxx_probe(struct pci_dev *pdev, netif_stop_queue(ndev); qdev->workqueue = create_singlethread_workqueue(ndev->name); + if (!qdev->workqueue) { + unregister_netdev(ndev); + err = -ENOMEM; + goto err_out_iounmap; + } + INIT_DELAYED_WORK(&qdev->reset_work, ql_reset_work); INIT_DELAYED_WORK(&qdev->tx_timeout_work, ql_tx_timeout_work); INIT_DELAYED_WORK(&qdev->link_state_work, ql_link_state_machine_work); diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 096515c27263..07e1c623048e 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4681,6 +4681,11 @@ static int ql_init_device(struct pci_dev *pdev, struct net_device *ndev, */ qdev->workqueue = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, ndev->name); + if (!qdev->workqueue) { + err = -ENOMEM; + goto err_out2; + } + INIT_DELAYED_WORK(&qdev->asic_reset_work, ql_asic_reset_work); INIT_DELAYED_WORK(&qdev->mpi_reset_work, ql_mpi_reset_work); INIT_DELAYED_WORK(&qdev->mpi_work, ql_mpi_work); diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 69d752f0b621..55d01266e615 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -258,6 +258,7 @@ static const struct pci_device_id rtl8139_pci_tbl[] = { {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + {0x16ec, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, #ifdef CONFIG_SH_SECUREEDGE5410 /* Bogus 8139 silicon reports 8129 without external PROM :-( */ diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index d28c8f9ca55b..8154b38c08f7 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -458,7 +458,7 @@ static int ravb_dmac_init(struct net_device *ndev) RCR_EFFS | RCR_ENCF | RCR_ETS0 | RCR_ESF | 0x18000000, RCR); /* Set FIFO size */ - ravb_write(ndev, TGC_TQP_AVBMODE1 | 0x00222200, TGC); + ravb_write(ndev, TGC_TQP_AVBMODE1 | 0x00112200, TGC); /* Timestamp enable */ ravb_write(ndev, TCCR_TFEN, TCCR); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 339b2eae2100..e33af371b169 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -3181,12 +3181,16 @@ static struct sh_eth_plat_data *sh_eth_parse_dt(struct device *dev) struct device_node *np = dev->of_node; struct sh_eth_plat_data *pdata; const char *mac_addr; + int ret; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; - pdata->phy_interface = of_get_phy_mode(np); + ret = of_get_phy_mode(np); + if (ret < 0) + return NULL; + pdata->phy_interface = ret; mac_addr = of_get_mac_address(np); if (mac_addr) diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index c883aa89b7ca..a71c900ca04f 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2805,6 +2805,11 @@ static int rocker_switchdev_event(struct notifier_block *unused, memcpy(&switchdev_work->fdb_info, ptr, sizeof(switchdev_work->fdb_info)); switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + if (unlikely(!switchdev_work->fdb_info.addr)) { + kfree(switchdev_work); + return NOTIFY_BAD; + } + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, fdb_info->addr); /* Take a reference on the rocker device */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c index 7e2e79dedebf..062a600fa5a7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c @@ -25,9 +25,24 @@ #define SYSCFG_MCU_ETH_MASK BIT(23) #define SYSCFG_MP1_ETH_MASK GENMASK(23, 16) +#define SYSCFG_PMCCLRR_OFFSET 0x40 #define SYSCFG_PMCR_ETH_CLK_SEL BIT(16) #define SYSCFG_PMCR_ETH_REF_CLK_SEL BIT(17) + +/* Ethernet PHY interface selection in register SYSCFG Configuration + *------------------------------------------ + * src |BIT(23)| BIT(22)| BIT(21)|BIT(20)| + *------------------------------------------ + * MII | 0 | 0 | 0 | 1 | + *------------------------------------------ + * GMII | 0 | 0 | 0 | 0 | + *------------------------------------------ + * RGMII | 0 | 0 | 1 | n/a | + *------------------------------------------ + * RMII | 1 | 0 | 0 | n/a | + *------------------------------------------ + */ #define SYSCFG_PMCR_ETH_SEL_MII BIT(20) #define SYSCFG_PMCR_ETH_SEL_RGMII BIT(21) #define SYSCFG_PMCR_ETH_SEL_RMII BIT(23) @@ -35,14 +50,54 @@ #define SYSCFG_MCU_ETH_SEL_MII 0 #define SYSCFG_MCU_ETH_SEL_RMII 1 +/* STM32MP1 register definitions + * + * Below table summarizes the clock requirement and clock sources for + * supported phy interface modes. + * __________________________________________________________________________ + *|PHY_MODE | Normal | PHY wo crystal| PHY wo crystal |No 125Mhz from PHY| + *| | | 25MHz | 50MHz | | + * --------------------------------------------------------------------------- + *| MII | - | eth-ck | n/a | n/a | + *| | | | | | + * --------------------------------------------------------------------------- + *| GMII | - | eth-ck | n/a | n/a | + *| | | | | | + * --------------------------------------------------------------------------- + *| RGMII | - | eth-ck | n/a | eth-ck (no pin) | + *| | | | | st,eth-clk-sel | + * --------------------------------------------------------------------------- + *| RMII | - | eth-ck | eth-ck | n/a | + *| | | | st,eth-ref-clk-sel | | + * --------------------------------------------------------------------------- + * + * BIT(17) : set this bit in RMII mode when you have PHY without crystal 50MHz + * BIT(16) : set this bit in GMII/RGMII PHY when you do not want use 125Mhz + * from PHY + *----------------------------------------------------- + * src | BIT(17) | BIT(16) | + *----------------------------------------------------- + * MII | n/a | n/a | + *----------------------------------------------------- + * GMII | n/a | st,eth-clk-sel | + *----------------------------------------------------- + * RGMII | n/a | st,eth-clk-sel | + *----------------------------------------------------- + * RMII | st,eth-ref-clk-sel | n/a | + *----------------------------------------------------- + * + */ + struct stm32_dwmac { struct clk *clk_tx; struct clk *clk_rx; struct clk *clk_eth_ck; struct clk *clk_ethstp; struct clk *syscfg_clk; - bool int_phyclk; /* Clock from RCC to drive PHY */ - u32 mode_reg; /* MAC glue-logic mode register */ + int eth_clk_sel_reg; + int eth_ref_clk_sel_reg; + int irq_pwr_wakeup; + u32 mode_reg; /* MAC glue-logic mode register */ struct regmap *regmap; u32 speed; const struct stm32_ops *ops; @@ -102,7 +157,7 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare) if (ret) return ret; - if (dwmac->int_phyclk) { + if (dwmac->clk_eth_ck) { ret = clk_prepare_enable(dwmac->clk_eth_ck); if (ret) { clk_disable_unprepare(dwmac->syscfg_clk); @@ -111,7 +166,7 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare) } } else { clk_disable_unprepare(dwmac->syscfg_clk); - if (dwmac->int_phyclk) + if (dwmac->clk_eth_ck) clk_disable_unprepare(dwmac->clk_eth_ck); } return ret; @@ -121,7 +176,7 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) { struct stm32_dwmac *dwmac = plat_dat->bsp_priv; u32 reg = dwmac->mode_reg; - int val; + int val, ret; switch (plat_dat->interface) { case PHY_INTERFACE_MODE_MII: @@ -130,19 +185,22 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) break; case PHY_INTERFACE_MODE_GMII: val = SYSCFG_PMCR_ETH_SEL_GMII; - if (dwmac->int_phyclk) + if (dwmac->eth_clk_sel_reg) val |= SYSCFG_PMCR_ETH_CLK_SEL; pr_debug("SYSCFG init : PHY_INTERFACE_MODE_GMII\n"); break; case PHY_INTERFACE_MODE_RMII: val = SYSCFG_PMCR_ETH_SEL_RMII; - if (dwmac->int_phyclk) + if (dwmac->eth_ref_clk_sel_reg) val |= SYSCFG_PMCR_ETH_REF_CLK_SEL; pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RMII\n"); break; case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: val = SYSCFG_PMCR_ETH_SEL_RGMII; - if (dwmac->int_phyclk) + if (dwmac->eth_clk_sel_reg) val |= SYSCFG_PMCR_ETH_CLK_SEL; pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RGMII\n"); break; @@ -153,6 +211,11 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) return -EINVAL; } + /* Need to update PMCCLRR (clear register) */ + ret = regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET, + dwmac->ops->syscfg_eth_mask); + + /* Update PMCSETR (set register) */ return regmap_update_bits(dwmac->regmap, reg, dwmac->ops->syscfg_eth_mask, val); } @@ -180,7 +243,7 @@ static int stm32mcu_set_mode(struct plat_stmmacenet_data *plat_dat) } return regmap_update_bits(dwmac->regmap, reg, - dwmac->ops->syscfg_eth_mask, val); + dwmac->ops->syscfg_eth_mask, val << 23); } static void stm32_dwmac_clk_disable(struct stm32_dwmac *dwmac) @@ -232,24 +295,29 @@ static int stm32_dwmac_parse_data(struct stm32_dwmac *dwmac, static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; + int err = 0; - dwmac->int_phyclk = of_property_read_bool(np, "st,int-phyclk"); + /* Gigabit Ethernet 125MHz clock selection. */ + dwmac->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel"); - /* Check if internal clk from RCC selected */ - if (dwmac->int_phyclk) { - /* Get ETH_CLK clocks */ - dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); - if (IS_ERR(dwmac->clk_eth_ck)) { - dev_err(dev, "No ETH CK clock provided...\n"); - return PTR_ERR(dwmac->clk_eth_ck); - } + /* Ethernet 50Mhz RMII clock selection */ + dwmac->eth_ref_clk_sel_reg = + of_property_read_bool(np, "st,eth-ref-clk-sel"); + + /* Get ETH_CLK clocks */ + dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); + if (IS_ERR(dwmac->clk_eth_ck)) { + dev_warn(dev, "No phy clock provided...\n"); + dwmac->clk_eth_ck = NULL; } /* Clock used for low power mode */ dwmac->clk_ethstp = devm_clk_get(dev, "ethstp"); if (IS_ERR(dwmac->clk_ethstp)) { - dev_err(dev, "No ETH peripheral clock provided for CStop mode ...\n"); + dev_err(dev, + "No ETH peripheral clock provided for CStop mode ...\n"); return PTR_ERR(dwmac->clk_ethstp); } @@ -260,7 +328,26 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, return PTR_ERR(dwmac->syscfg_clk); } - return 0; + /* Get IRQ information early to have an ability to ask for deferred + * probe if needed before we went too far with resource allocation. + */ + dwmac->irq_pwr_wakeup = platform_get_irq_byname(pdev, + "stm32_pwr_wakeup"); + if (!dwmac->clk_eth_ck && dwmac->irq_pwr_wakeup >= 0) { + err = device_init_wakeup(&pdev->dev, true); + if (err) { + dev_err(&pdev->dev, "Failed to init wake up irq\n"); + return err; + } + err = dev_pm_set_dedicated_wake_irq(&pdev->dev, + dwmac->irq_pwr_wakeup); + if (err) { + dev_err(&pdev->dev, "Failed to set wake up irq\n"); + device_init_wakeup(&pdev->dev, false); + } + device_set_wakeup_enable(&pdev->dev, false); + } + return err; } static int stm32_dwmac_probe(struct platform_device *pdev) @@ -326,9 +413,15 @@ static int stm32_dwmac_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); int ret = stmmac_dvr_remove(&pdev->dev); + struct stm32_dwmac *dwmac = priv->plat->bsp_priv; stm32_dwmac_clk_disable(priv->plat->bsp_priv); + if (dwmac->irq_pwr_wakeup >= 0) { + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); + } + return ret; } @@ -342,7 +435,7 @@ static int stm32mp1_suspend(struct stm32_dwmac *dwmac) clk_disable_unprepare(dwmac->clk_tx); clk_disable_unprepare(dwmac->syscfg_clk); - if (dwmac->int_phyclk) + if (dwmac->clk_eth_ck) clk_disable_unprepare(dwmac->clk_eth_ck); return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 0f660af01a4b..195669f550f0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -1147,7 +1147,10 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) return ret; } - plat_dat->interface = of_get_phy_mode(dev->of_node); + ret = of_get_phy_mode(dev->of_node); + if (ret < 0) + return -EINVAL; + plat_dat->interface = ret; /* platform data specifying hardware features and callbacks. * hardware features were copied from Allwinner drivers. diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e2a13ec2e30b..97c5e1aad88f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -480,7 +480,7 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, struct sk_buff *skb) { struct skb_shared_hwtstamps shhwtstamp; - u64 ns; + u64 ns = 0; if (!priv->hwts_tx_en) return; @@ -519,7 +519,7 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, { struct skb_shared_hwtstamps *shhwtstamp = NULL; struct dma_desc *desc = p; - u64 ns; + u64 ns = 0; if (!priv->hwts_rx_en) return; @@ -564,8 +564,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) u32 snap_type_sel = 0; u32 ts_master_en = 0; u32 ts_event_en = 0; + u32 sec_inc = 0; u32 value = 0; - u32 sec_inc; bool xmac; xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 2b800ce1d5bf..3031f2bf15d6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -408,6 +408,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) /* Default to phy auto-detection */ plat->phy_addr = -1; + /* Get clk_csr from device tree */ + of_property_read_u32(np, "clk_csr", &plat->clk_csr); + /* "snps,phy-addr" is not a standard property. Mark it as deprecated * and warn of its use. Remove this when phy node support is added. */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index 2293e21f789f..cc60b3fb0892 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -105,7 +105,7 @@ static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts) struct stmmac_priv *priv = container_of(ptp, struct stmmac_priv, ptp_clock_ops); unsigned long flags; - u64 ns; + u64 ns = 0; spin_lock_irqsave(&priv->ptp_lock, flags); stmmac_get_systime(priv, priv->ptpaddr, &ns); diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index d84501441edd..6f99437a6962 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -7464,6 +7464,7 @@ static int niu_add_ethtool_tcam_entry(struct niu *np, class = CLASS_CODE_USER_PROG4; break; default: + class = CLASS_CODE_UNRECOG; break; } ret = tcam_user_ip_class_set(np, class, 0, diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 840820402cd0..57450b174fc4 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -2029,7 +2029,6 @@ static const struct dev_pm_ops davinci_emac_pm_ops = { .resume = davinci_emac_resume, }; -#if IS_ENABLED(CONFIG_OF) static const struct emac_platform_data am3517_emac_data = { .version = EMAC_VERSION_2, .hw_ram_addr = 0x01e20000, @@ -2046,14 +2045,13 @@ static const struct of_device_id davinci_emac_of_match[] = { {}, }; MODULE_DEVICE_TABLE(of, davinci_emac_of_match); -#endif /* davinci_emac_driver: EMAC platform driver structure */ static struct platform_driver davinci_emac_driver = { .driver = { .name = "davinci_emac", .pm = &davinci_emac_pm_ops, - .of_match_table = of_match_ptr(davinci_emac_of_match), + .of_match_table = davinci_emac_of_match, }, .probe = davinci_emac_probe, .remove = davinci_emac_remove, diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 8f09edd811e9..50c60550f295 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -532,6 +532,7 @@ static void pptp_sock_destruct(struct sock *sk) pppox_unbind_sock(sk); } skb_queue_purge(&sk->sk_receive_queue); + dst_release(rcu_dereference_protected(sk->sk_dst_cache, 1)); } static int pptp_create(struct net *net, struct socket *sock, int kern) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index a3c46d78d216..077f1b9f2761 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1731,6 +1731,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) goto drop; } + rcu_read_lock(); + + if (unlikely(!(vxlan->dev->flags & IFF_UP))) { + rcu_read_unlock(); + atomic_long_inc(&vxlan->dev->rx_dropped); + goto drop; + } + stats = this_cpu_ptr(vxlan->dev->tstats); u64_stats_update_begin(&stats->syncp); stats->rx_packets++; @@ -1738,6 +1746,9 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) u64_stats_update_end(&stats->syncp); gro_cells_receive(&vxlan->gro_cells, skb); + + rcu_read_unlock(); + return 0; drop: @@ -2690,7 +2701,7 @@ static void vxlan_cleanup(struct timer_list *t) for (h = 0; h < FDB_HASH_SIZE; ++h) { struct hlist_node *p, *n; - spin_lock_bh(&vxlan->hash_lock); + spin_lock(&vxlan->hash_lock); hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) { struct vxlan_fdb *f = container_of(p, struct vxlan_fdb, hlist); @@ -2712,7 +2723,7 @@ static void vxlan_cleanup(struct timer_list *t) } else if (time_before(timeout, next_timer)) next_timer = timeout; } - spin_unlock_bh(&vxlan->hash_lock); + spin_unlock(&vxlan->hash_lock); } mod_timer(&vxlan->age_timer, next_timer); @@ -2767,6 +2778,8 @@ static void vxlan_uninit(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); + gro_cells_destroy(&vxlan->gro_cells); + vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni); free_percpu(dev->tstats); @@ -3942,7 +3955,6 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head) vxlan_flush(vxlan, true); - gro_cells_destroy(&vxlan->gro_cells); list_del(&vxlan->next); unregister_netdevice_queue(dev, head); } diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c index 2ad263f708da..bb57ec239029 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen1.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c @@ -180,7 +180,7 @@ int ndev_mw_to_bar(struct intel_ntb_dev *ndev, int idx) return ndev->reg->mw_bar[idx]; } -static inline int ndev_db_addr(struct intel_ntb_dev *ndev, +void ndev_db_addr(struct intel_ntb_dev *ndev, phys_addr_t *db_addr, resource_size_t *db_size, phys_addr_t reg_addr, unsigned long reg) { @@ -196,8 +196,6 @@ static inline int ndev_db_addr(struct intel_ntb_dev *ndev, *db_size = ndev->reg->db_size; dev_dbg(&ndev->ntb.pdev->dev, "Peer db size %llx\n", *db_size); } - - return 0; } u64 ndev_db_read(struct intel_ntb_dev *ndev, @@ -1111,13 +1109,28 @@ int intel_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) ndev->self_reg->db_mask); } -int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, - resource_size_t *db_size) +static int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, + resource_size_t *db_size, u64 *db_data, int db_bit) { + u64 db_bits; struct intel_ntb_dev *ndev = ntb_ndev(ntb); - return ndev_db_addr(ndev, db_addr, db_size, ndev->peer_addr, + if (unlikely(db_bit >= BITS_PER_LONG_LONG)) + return -EINVAL; + + db_bits = BIT_ULL(db_bit); + + if (unlikely(db_bits & ~ntb_ndev(ntb)->db_valid_mask)) + return -EINVAL; + + ndev_db_addr(ndev, db_addr, db_size, ndev->peer_addr, ndev->peer_reg->db_bell); + + if (db_data) + *db_data = db_bits; + + + return 0; } static int intel_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.h b/drivers/ntb/hw/intel/ntb_hw_gen1.h index ad8ec1444436..544cf5c06f4d 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen1.h +++ b/drivers/ntb/hw/intel/ntb_hw_gen1.h @@ -147,6 +147,9 @@ extern struct intel_b2b_addr xeon_b2b_dsd_addr; int ndev_init_isr(struct intel_ntb_dev *ndev, int msix_min, int msix_max, int msix_shift, int total_shift); enum ntb_topo xeon_ppd_topo(struct intel_ntb_dev *ndev, u8 ppd); +void ndev_db_addr(struct intel_ntb_dev *ndev, + phys_addr_t *db_addr, resource_size_t *db_size, + phys_addr_t reg_addr, unsigned long reg); u64 ndev_db_read(struct intel_ntb_dev *ndev, void __iomem *mmio); int ndev_db_write(struct intel_ntb_dev *ndev, u64 db_bits, void __iomem *mmio); @@ -166,8 +169,6 @@ int intel_ntb_db_vector_count(struct ntb_dev *ntb); u64 intel_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector); int intel_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits); int intel_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits); -int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, - resource_size_t *db_size); int intel_ntb_spad_is_unsafe(struct ntb_dev *ntb); int intel_ntb_spad_count(struct ntb_dev *ntb); u32 intel_ntb_spad_read(struct ntb_dev *ntb, int idx); diff --git a/drivers/ntb/hw/intel/ntb_hw_gen3.c b/drivers/ntb/hw/intel/ntb_hw_gen3.c index b3fa24778f94..f475b56a3f49 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen3.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen3.c @@ -532,6 +532,37 @@ static int intel_ntb3_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, return 0; } +int intel_ntb3_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, + resource_size_t *db_size, + u64 *db_data, int db_bit) +{ + phys_addr_t db_addr_base; + struct intel_ntb_dev *ndev = ntb_ndev(ntb); + + if (unlikely(db_bit >= BITS_PER_LONG_LONG)) + return -EINVAL; + + if (unlikely(BIT_ULL(db_bit) & ~ntb_ndev(ntb)->db_valid_mask)) + return -EINVAL; + + ndev_db_addr(ndev, &db_addr_base, db_size, ndev->peer_addr, + ndev->peer_reg->db_bell); + + if (db_addr) { + *db_addr = db_addr_base + (db_bit * 4); + dev_dbg(&ndev->ntb.pdev->dev, "Peer db addr %llx db bit %d\n", + *db_addr, db_bit); + } + + if (db_data) { + *db_data = 1; + dev_dbg(&ndev->ntb.pdev->dev, "Peer db data %llx db bit %d\n", + *db_data, db_bit); + } + + return 0; +} + static int intel_ntb3_peer_db_set(struct ntb_dev *ntb, u64 db_bits) { struct intel_ntb_dev *ndev = ntb_ndev(ntb); @@ -584,7 +615,7 @@ const struct ntb_dev_ops intel_ntb3_ops = { .db_clear = intel_ntb3_db_clear, .db_set_mask = intel_ntb_db_set_mask, .db_clear_mask = intel_ntb_db_clear_mask, - .peer_db_addr = intel_ntb_peer_db_addr, + .peer_db_addr = intel_ntb3_peer_db_addr, .peer_db_set = intel_ntb3_peer_db_set, .spad_is_unsafe = intel_ntb_spad_is_unsafe, .spad_count = intel_ntb_spad_count, diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c index f2df2d39c65b..d905d368d28c 100644 --- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -236,6 +236,7 @@ static void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx) ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN; iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); iowrite32(0, &ctl->bar_entry[bar].win_size); + iowrite32(0, &ctl->bar_ext_entry[bar].win_size); iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr); } @@ -258,7 +259,9 @@ static void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx, ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN; iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); - iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size); + iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000), + &ctl->bar_entry[bar].win_size); + iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size); iowrite64(sndev->self_partition | addr, &ctl->bar_entry[bar].xlate_addr); } @@ -679,11 +682,16 @@ static u64 switchtec_ntb_db_read_mask(struct ntb_dev *ntb) static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, - resource_size_t *db_size) + resource_size_t *db_size, + u64 *db_data, + int db_bit) { struct switchtec_ntb *sndev = ntb_sndev(ntb); unsigned long offset; + if (unlikely(db_bit >= BITS_PER_LONG_LONG)) + return -EINVAL; + offset = (unsigned long)sndev->mmio_peer_dbmsg->odb - (unsigned long)sndev->stdev->mmio; @@ -693,6 +701,8 @@ static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb, *db_addr = pci_resource_start(ntb->pdev, 0) + offset; if (db_size) *db_size = sizeof(u32); + if (db_data) + *db_data = BIT_ULL(db_bit) << sndev->db_peer_shift; return 0; } @@ -1025,7 +1035,9 @@ static int crosslink_setup_mws(struct switchtec_ntb *sndev, int ntb_lut_idx, ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN; iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); - iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size); + iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000), + &ctl->bar_entry[bar].win_size); + iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size); iowrite64(sndev->peer_partition | addr, &ctl->bar_entry[bar].xlate_addr); } @@ -1092,7 +1104,7 @@ static int crosslink_enum_partition(struct switchtec_ntb *sndev, dev_dbg(&sndev->stdev->dev, "Crosslink BAR%d addr: %llx\n", - i, bar_addr); + i*2, bar_addr); if (bar_addr != bar_space * i) continue; diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 3bfdb4562408..d4f39ba1d976 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -144,7 +144,9 @@ struct ntb_transport_qp { struct list_head tx_free_q; spinlock_t ntb_tx_free_q_lock; void __iomem *tx_mw; - dma_addr_t tx_mw_phys; + phys_addr_t tx_mw_phys; + size_t tx_mw_size; + dma_addr_t tx_mw_dma_addr; unsigned int tx_index; unsigned int tx_max_entry; unsigned int tx_max_frame; @@ -862,6 +864,9 @@ static void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt) if (!nt->link_is_up) cancel_delayed_work_sync(&nt->link_work); + for (i = 0; i < nt->mw_count; i++) + ntb_free_mw(nt, i); + /* The scratchpad registers keep the values if the remote side * goes down, blast them now to give them a sane value the next * time they are accessed @@ -1049,6 +1054,7 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, tx_size = (unsigned int)mw_size / num_qps_mw; qp_offset = tx_size * (qp_num / mw_count); + qp->tx_mw_size = tx_size; qp->tx_mw = nt->mw_vec[mw_num].vbase + qp_offset; if (!qp->tx_mw) return -EINVAL; @@ -1644,7 +1650,7 @@ static int ntb_async_tx_submit(struct ntb_transport_qp *qp, dma_cookie_t cookie; device = chan->device; - dest = qp->tx_mw_phys + qp->tx_max_frame * entry->tx_index; + dest = qp->tx_mw_dma_addr + qp->tx_max_frame * entry->tx_index; buff_off = (size_t)buf & ~PAGE_MASK; dest_off = (size_t)dest & ~PAGE_MASK; @@ -1863,6 +1869,18 @@ ntb_transport_create_queue(void *data, struct device *client_dev, qp->rx_dma_chan = NULL; } + if (qp->tx_dma_chan) { + qp->tx_mw_dma_addr = + dma_map_resource(qp->tx_dma_chan->device->dev, + qp->tx_mw_phys, qp->tx_mw_size, + DMA_FROM_DEVICE, 0); + if (dma_mapping_error(qp->tx_dma_chan->device->dev, + qp->tx_mw_dma_addr)) { + qp->tx_mw_dma_addr = 0; + goto err1; + } + } + dev_dbg(&pdev->dev, "Using %s memcpy for TX\n", qp->tx_dma_chan ? "DMA" : "CPU"); @@ -1904,6 +1922,10 @@ err1: qp->rx_alloc_entry = 0; while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q))) kfree(entry); + if (qp->tx_mw_dma_addr) + dma_unmap_resource(qp->tx_dma_chan->device->dev, + qp->tx_mw_dma_addr, qp->tx_mw_size, + DMA_FROM_DEVICE, 0); if (qp->tx_dma_chan) dma_release_channel(qp->tx_dma_chan); if (qp->rx_dma_chan) @@ -1945,6 +1967,11 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp) */ dma_sync_wait(chan, qp->last_cookie); dmaengine_terminate_all(chan); + + dma_unmap_resource(chan->device->dev, + qp->tx_mw_dma_addr, qp->tx_mw_size, + DMA_FROM_DEVICE, 0); + dma_release_channel(chan); } diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index b123b0dcf274..4671776f5623 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -541,9 +541,9 @@ static int arena_clear_freelist_error(struct arena_info *arena, u32 lane) static int btt_freelist_init(struct arena_info *arena) { - int old, new, ret; - u32 i, map_entry; - struct log_entry log_new, log_old; + int new, ret; + struct log_entry log_new; + u32 i, map_entry, log_oldmap, log_newmap; arena->freelist = kcalloc(arena->nfree, sizeof(struct free_entry), GFP_KERNEL); @@ -551,24 +551,26 @@ static int btt_freelist_init(struct arena_info *arena) return -ENOMEM; for (i = 0; i < arena->nfree; i++) { - old = btt_log_read(arena, i, &log_old, LOG_OLD_ENT); - if (old < 0) - return old; - new = btt_log_read(arena, i, &log_new, LOG_NEW_ENT); if (new < 0) return new; + /* old and new map entries with any flags stripped out */ + log_oldmap = ent_lba(le32_to_cpu(log_new.old_map)); + log_newmap = ent_lba(le32_to_cpu(log_new.new_map)); + /* sub points to the next one to be overwritten */ arena->freelist[i].sub = 1 - new; arena->freelist[i].seq = nd_inc_seq(le32_to_cpu(log_new.seq)); - arena->freelist[i].block = le32_to_cpu(log_new.old_map); + arena->freelist[i].block = log_oldmap; /* * FIXME: if error clearing fails during init, we want to make * the BTT read-only */ - if (ent_e_flag(log_new.old_map)) { + if (ent_e_flag(log_new.old_map) && + !ent_normal(log_new.old_map)) { + arena->freelist[i].has_err = 1; ret = arena_clear_freelist_error(arena, i); if (ret) dev_err_ratelimited(to_dev(arena), @@ -576,7 +578,7 @@ static int btt_freelist_init(struct arena_info *arena) } /* This implies a newly created or untouched flog entry */ - if (log_new.old_map == log_new.new_map) + if (log_oldmap == log_newmap) continue; /* Check if map recovery is needed */ @@ -584,8 +586,15 @@ static int btt_freelist_init(struct arena_info *arena) NULL, NULL, 0); if (ret) return ret; - if ((le32_to_cpu(log_new.new_map) != map_entry) && - (le32_to_cpu(log_new.old_map) == map_entry)) { + + /* + * The map_entry from btt_read_map is stripped of any flag bits, + * so use the stripped out versions from the log as well for + * testing whether recovery is needed. For restoration, use the + * 'raw' version of the log entries as that captured what we + * were going to write originally. + */ + if ((log_newmap != map_entry) && (log_oldmap == map_entry)) { /* * Last transaction wrote the flog, but wasn't able * to complete the map write. So fix up the map. diff --git a/drivers/nvdimm/btt.h b/drivers/nvdimm/btt.h index db3cb6d4d0d4..ddff49c707b0 100644 --- a/drivers/nvdimm/btt.h +++ b/drivers/nvdimm/btt.h @@ -44,6 +44,8 @@ #define ent_e_flag(ent) (!!(ent & MAP_ERR_MASK)) #define ent_z_flag(ent) (!!(ent & MAP_TRIM_MASK)) #define set_e_flag(ent) (ent |= MAP_ERR_MASK) +/* 'normal' is both e and z flags set */ +#define ent_normal(ent) (ent_e_flag(ent) && ent_z_flag(ent)) enum btt_init_state { INIT_UNCHECKED = 0, diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 795ad4ff35ca..b72a303176c7 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -159,11 +159,19 @@ static ssize_t size_show(struct device *dev, } static DEVICE_ATTR_RO(size); +static ssize_t log_zero_flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "Y\n"); +} +static DEVICE_ATTR_RO(log_zero_flags); + static struct attribute *nd_btt_attributes[] = { &dev_attr_sector_size.attr, &dev_attr_namespace.attr, &dev_attr_uuid.attr, &dev_attr_size.attr, + &dev_attr_log_zero_flags.attr, NULL, }; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index efe412a6b5b9..91b9abbf689c 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -11,6 +11,7 @@ * General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/moduleparam.h> #include <linux/vmalloc.h> #include <linux/device.h> #include <linux/ndctl.h> @@ -25,6 +26,10 @@ static DEFINE_IDA(dimm_ida); +static bool noblk; +module_param(noblk, bool, 0444); +MODULE_PARM_DESC(noblk, "force disable BLK / local alias support"); + /* * Retrieve bus and dimm handle and return if this bus supports * get_config_data commands @@ -551,6 +556,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, nvdimm->dimm_id = dimm_id; nvdimm->provider_data = provider_data; + if (noblk) + flags |= 1 << NDD_NOBLK; nvdimm->flags = flags; nvdimm->cmd_mask = cmd_mask; nvdimm->num_flush = num_flush; diff --git a/drivers/nvdimm/e820.c b/drivers/nvdimm/e820.c index 521eaf53a52a..36be9b619187 100644 --- a/drivers/nvdimm/e820.c +++ b/drivers/nvdimm/e820.c @@ -47,6 +47,7 @@ static int e820_register_one(struct resource *res, void *data) ndr_desc.res = res; ndr_desc.attr_groups = e820_pmem_region_attribute_groups; ndr_desc.numa_node = e820_range_to_nid(res->start); + ndr_desc.target_node = ndr_desc.numa_node; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc)) return -ENXIO; diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index a11bf4e6b451..f3d753d3169c 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -392,6 +392,7 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) return 0; /* no label, nothing to reserve */ for_each_clear_bit_le(slot, free, nslot) { + struct nvdimm *nvdimm = to_nvdimm(ndd->dev); struct nd_namespace_label *nd_label; struct nd_region *nd_region = NULL; u8 label_uuid[NSLABEL_UUID_LEN]; @@ -406,6 +407,8 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); flags = __le32_to_cpu(nd_label->flags); + if (test_bit(NDD_NOBLK, &nvdimm->flags)) + flags &= ~NSLABEL_FLAG_LOCAL; nd_label_gen_id(&label_id, label_uuid, flags); res = nvdimm_allocate_dpa(ndd, &label_id, __le64_to_cpu(nd_label->dpa), @@ -755,7 +758,7 @@ static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, static int __pmem_label_update(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, - int pos) + int pos, unsigned long flags) { struct nd_namespace_common *ndns = &nspm->nsio.common; struct nd_interleave_set *nd_set = nd_region->nd_set; @@ -796,7 +799,7 @@ static int __pmem_label_update(struct nd_region *nd_region, memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN); if (nspm->alt_name) memcpy(nd_label->name, nspm->alt_name, NSLABEL_NAME_LEN); - nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_UPDATING); + nd_label->flags = __cpu_to_le32(flags); nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings); nd_label->position = __cpu_to_le16(pos); nd_label->isetcookie = __cpu_to_le64(cookie); @@ -1249,13 +1252,13 @@ static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) int nd_pmem_namespace_label_update(struct nd_region *nd_region, struct nd_namespace_pmem *nspm, resource_size_t size) { - int i; + int i, rc; for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct resource *res; - int rc, count = 0; + int count = 0; if (size == 0) { rc = del_labels(nd_mapping, nspm->uuid); @@ -1273,7 +1276,20 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region, if (rc < 0) return rc; - rc = __pmem_label_update(nd_region, nd_mapping, nspm, i); + rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, + NSLABEL_FLAG_UPDATING); + if (rc) + return rc; + } + + if (size == 0) + return 0; + + /* Clear the UPDATING flag per UEFI 2.7 expectations */ + for (i = 0; i < nd_region->ndr_mappings; i++) { + struct nd_mapping *nd_mapping = &nd_region->mapping[i]; + + rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0); if (rc) return rc; } diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 4b077555ac70..7849bf1812c4 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -138,6 +138,7 @@ bool nd_is_uuid_unique(struct device *dev, u8 *uuid) bool pmem_should_map_pages(struct device *dev) { struct nd_region *nd_region = to_nd_region(dev->parent); + struct nd_namespace_common *ndns = to_ndns(dev); struct nd_namespace_io *nsio; if (!IS_ENABLED(CONFIG_ZONE_DEVICE)) @@ -149,6 +150,9 @@ bool pmem_should_map_pages(struct device *dev) if (is_nd_pfn(dev) || is_nd_btt(dev)) return false; + if (ndns->force_raw) + return false; + nsio = to_nd_namespace_io(dev); if (region_intersects(nsio->res.start, resource_size(&nsio->res), IORESOURCE_SYSTEM_RAM, @@ -1506,13 +1510,13 @@ static ssize_t __holder_class_store(struct device *dev, const char *buf) if (dev->driver || ndns->claim) return -EBUSY; - if (strcmp(buf, "btt") == 0 || strcmp(buf, "btt\n") == 0) + if (sysfs_streq(buf, "btt")) ndns->claim_class = btt_claim_class(dev); - else if (strcmp(buf, "pfn") == 0 || strcmp(buf, "pfn\n") == 0) + else if (sysfs_streq(buf, "pfn")) ndns->claim_class = NVDIMM_CCLASS_PFN; - else if (strcmp(buf, "dax") == 0 || strcmp(buf, "dax\n") == 0) + else if (sysfs_streq(buf, "dax")) ndns->claim_class = NVDIMM_CCLASS_DAX; - else if (strcmp(buf, "") == 0 || strcmp(buf, "\n") == 0) + else if (sysfs_streq(buf, "")) ndns->claim_class = NVDIMM_CCLASS_NONE; else return -EINVAL; @@ -2492,6 +2496,12 @@ static int init_active_labels(struct nd_region *nd_region) if (!label_ent) break; label = nd_label_active(ndd, j); + if (test_bit(NDD_NOBLK, &nvdimm->flags)) { + u32 flags = __le32_to_cpu(label->flags); + + flags &= ~NSLABEL_FLAG_LOCAL; + label->flags = __cpu_to_le32(flags); + } label_ent->label = label; mutex_lock(&nd_mapping->lock); diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 379bf4305e61..a5ac3b240293 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -153,7 +153,7 @@ struct nd_region { u16 ndr_mappings; u64 ndr_size; u64 ndr_start; - int id, num_lanes, ro, numa_node; + int id, num_lanes, ro, numa_node, target_node; void *provider_data; struct kernfs_node *bb_state; struct badblocks bb; diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c index 0a701837dfc0..a0c8dcfa0bf9 100644 --- a/drivers/nvdimm/of_pmem.c +++ b/drivers/nvdimm/of_pmem.c @@ -68,6 +68,7 @@ static int of_pmem_region_probe(struct platform_device *pdev) memset(&ndr_desc, 0, sizeof(ndr_desc)); ndr_desc.attr_groups = region_attr_groups; ndr_desc.numa_node = dev_to_node(&pdev->dev); + ndr_desc.target_node = ndr_desc.numa_node; ndr_desc.res = &pdev->resource[i]; ndr_desc.of_node = np; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); @@ -108,7 +109,6 @@ static struct platform_driver of_pmem_region_driver = { .remove = of_pmem_region_remove, .driver = { .name = "of_pmem", - .owner = THIS_MODULE, .of_match_table = of_pmem_region_match, }, }; diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 6f22272e8d80..d271bd731af7 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -580,6 +580,11 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) } EXPORT_SYMBOL(nd_pfn_probe); +static u32 info_block_reserve(void) +{ + return ALIGN(SZ_8K, PAGE_SIZE); +} + /* * We hotplug memory at section granularity, pad the reserved area from * the previous section base to the namespace base address. @@ -593,7 +598,7 @@ static unsigned long init_altmap_base(resource_size_t base) static unsigned long init_altmap_reserve(resource_size_t base) { - unsigned long reserve = PHYS_PFN(SZ_8K); + unsigned long reserve = info_block_reserve() >> PAGE_SHIFT; unsigned long base_pfn = PHYS_PFN(base); reserve += base_pfn - PFN_SECTION_ALIGN_DOWN(base_pfn); @@ -608,6 +613,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) u64 offset = le64_to_cpu(pfn_sb->dataoff); u32 start_pad = __le32_to_cpu(pfn_sb->start_pad); u32 end_trunc = __le32_to_cpu(pfn_sb->end_trunc); + u32 reserve = info_block_reserve(); struct nd_namespace_common *ndns = nd_pfn->ndns; struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); resource_size_t base = nsio->res.start + start_pad; @@ -621,7 +627,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) res->end -= end_trunc; if (nd_pfn->mode == PFN_MODE_RAM) { - if (offset < SZ_8K) + if (offset < reserve) return -EINVAL; nd_pfn->npfns = le64_to_cpu(pfn_sb->npfns); pgmap->altmap_valid = false; @@ -634,7 +640,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) le64_to_cpu(nd_pfn->pfn_sb->npfns), nd_pfn->npfns); memcpy(altmap, &__altmap, sizeof(*altmap)); - altmap->free = PHYS_PFN(offset - SZ_8K); + altmap->free = PHYS_PFN(offset - reserve); altmap->alloc = 0; pgmap->altmap_valid = true; } else @@ -678,18 +684,17 @@ static void trim_pfn_device(struct nd_pfn *nd_pfn, u32 *start_pad, u32 *end_trun if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_MIXED || !IS_ALIGNED(end, nd_pfn->align) - || nd_region_conflict(nd_region, start, size + adjust)) + || nd_region_conflict(nd_region, start, size)) *end_trunc = end - phys_pmem_align_down(nd_pfn, end); } static int nd_pfn_init(struct nd_pfn *nd_pfn) { - u32 dax_label_reserve = is_nd_dax(&nd_pfn->dev) ? SZ_128K : 0; struct nd_namespace_common *ndns = nd_pfn->ndns; struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); + u32 start_pad, end_trunc, reserve = info_block_reserve(); resource_size_t start, size; struct nd_region *nd_region; - u32 start_pad, end_trunc; struct nd_pfn_sb *pfn_sb; unsigned long npfns; phys_addr_t offset; @@ -734,7 +739,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) */ start = nsio->res.start + start_pad; size = resource_size(&nsio->res); - npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - SZ_8K) + npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - reserve) / PAGE_SIZE); if (nd_pfn->mode == PFN_MODE_PMEM) { /* @@ -742,11 +747,10 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) * when populating the vmemmap. This *should* be equal to * PMD_SIZE for most architectures. */ - offset = ALIGN(start + SZ_8K + 64 * npfns + dax_label_reserve, + offset = ALIGN(start + reserve + 64 * npfns, max(nd_pfn->align, PMD_SIZE)) - start; } else if (nd_pfn->mode == PFN_MODE_RAM) - offset = ALIGN(start + SZ_8K + dax_label_reserve, - nd_pfn->align) - start; + offset = ALIGN(start + reserve, nd_pfn->align) - start; else return -ENXIO; diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index e2818f94f292..b4ef7d9ff22e 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -1003,6 +1003,13 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, if (test_bit(NDD_UNARMED, &nvdimm->flags)) ro = 1; + + if (test_bit(NDD_NOBLK, &nvdimm->flags) + && dev_type == &nd_blk_device_type) { + dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not BLK capable\n", + caller, dev_name(&nvdimm->dev), i); + return NULL; + } } if (dev_type == &nd_blk_device_type) { @@ -1065,6 +1072,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, nd_region->flags = ndr_desc->flags; nd_region->ro = ro; nd_region->numa_node = ndr_desc->numa_node; + nd_region->target_node = ndr_desc->target_node; ida_init(&nd_region->ns_ida); ida_init(&nd_region->btt_ida); ida_init(&nd_region->pfn_ida); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 07bf2bff3a76..470601980794 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -179,8 +179,8 @@ static int nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl) int ret = 0; /* - * Keep a reference until the work is flushed since ->delete_ctrl - * can free the controller. + * Keep a reference until nvme_do_delete_ctrl() complete, + * since ->delete_ctrl can free the controller. */ nvme_get_ctrl(ctrl); if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_DELETING)) @@ -1250,7 +1250,7 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns, if (ns) { if (ctrl->effects) effects = le32_to_cpu(ctrl->effects->iocs[opcode]); - if (effects & ~NVME_CMD_EFFECTS_CSUPP) + if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC)) dev_warn(ctrl->device, "IO command:%02x has unhandled effects:%08x\n", opcode, effects); @@ -1495,10 +1495,10 @@ static void nvme_set_chunk_size(struct nvme_ns *ns) blk_queue_chunk_sectors(ns->queue, rounddown_pow_of_two(chunk_size)); } -static void nvme_config_discard(struct nvme_ns *ns) +static void nvme_config_discard(struct gendisk *disk, struct nvme_ns *ns) { struct nvme_ctrl *ctrl = ns->ctrl; - struct request_queue *queue = ns->queue; + struct request_queue *queue = disk->queue; u32 size = queue_logical_block_size(queue); if (!(ctrl->oncs & NVME_CTRL_ONCS_DSM)) { @@ -1526,12 +1526,13 @@ static void nvme_config_discard(struct nvme_ns *ns) blk_queue_max_write_zeroes_sectors(queue, UINT_MAX); } -static inline void nvme_config_write_zeroes(struct nvme_ns *ns) +static void nvme_config_write_zeroes(struct gendisk *disk, struct nvme_ns *ns) { u32 max_sectors; unsigned short bs = 1 << ns->lba_shift; - if (!(ns->ctrl->oncs & NVME_CTRL_ONCS_WRITE_ZEROES)) + if (!(ns->ctrl->oncs & NVME_CTRL_ONCS_WRITE_ZEROES) || + (ns->ctrl->quirks & NVME_QUIRK_DISABLE_WRITE_ZEROES)) return; /* * Even though NVMe spec explicitly states that MDTS is not @@ -1548,13 +1549,7 @@ static inline void nvme_config_write_zeroes(struct nvme_ns *ns) else max_sectors = ((u32)(ns->ctrl->max_hw_sectors + 1) * bs) >> 9; - blk_queue_max_write_zeroes_sectors(ns->queue, max_sectors); -} - -static inline void nvme_ns_config_oncs(struct nvme_ns *ns) -{ - nvme_config_discard(ns); - nvme_config_write_zeroes(ns); + blk_queue_max_write_zeroes_sectors(disk->queue, max_sectors); } static void nvme_report_ns_ids(struct nvme_ctrl *ctrl, unsigned int nsid, @@ -1610,7 +1605,9 @@ static void nvme_update_disk_info(struct gendisk *disk, capacity = 0; set_capacity(disk, capacity); - nvme_ns_config_oncs(ns); + + nvme_config_discard(disk, ns); + nvme_config_write_zeroes(disk, ns); if (id->nsattr & (1 << 0)) set_disk_ro(disk, true); @@ -3304,6 +3301,7 @@ static int nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) mutex_lock(&ctrl->subsys->lock); list_del_rcu(&ns->siblings); mutex_unlock(&ctrl->subsys->lock); + nvme_put_ns_head(ns->head); out_free_id: kfree(id); out_free_queue: diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index b29b12498a1a..f3b9d91ba0df 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2107,7 +2107,7 @@ nvme_fc_map_data(struct nvme_fc_ctrl *ctrl, struct request *rq, freq->sg_cnt = 0; - if (!blk_rq_payload_bytes(rq)) + if (!blk_rq_nr_phys_segments(rq)) return 0; freq->sg_table.sgl = freq->first_sgl; @@ -2304,12 +2304,23 @@ nvme_fc_queue_rq(struct blk_mq_hw_ctx *hctx, if (ret) return ret; - data_len = blk_rq_payload_bytes(rq); - if (data_len) + /* + * nvme core doesn't quite treat the rq opaquely. Commands such + * as WRITE ZEROES will return a non-zero rq payload_bytes yet + * there is no actual payload to be transferred. + * To get it right, key data transmission on there being 1 or + * more physical segments in the sg list. If there is no + * physical segments, there is no payload. + */ + if (blk_rq_nr_phys_segments(rq)) { + data_len = blk_rq_payload_bytes(rq); io_dir = ((rq_data_dir(rq) == WRITE) ? NVMEFC_FCP_WRITE : NVMEFC_FCP_READ); - else + } else { + data_len = 0; io_dir = NVMEFC_FCP_NODATA; + } + return nvme_fc_start_fcp_op(ctrl, queue, op, data_len, io_dir); } @@ -2464,6 +2475,7 @@ static int nvme_fc_recreate_io_queues(struct nvme_fc_ctrl *ctrl) { struct nvmf_ctrl_options *opts = ctrl->ctrl.opts; + u32 prior_ioq_cnt = ctrl->ctrl.queue_count - 1; unsigned int nr_io_queues; int ret; @@ -2476,6 +2488,13 @@ nvme_fc_recreate_io_queues(struct nvme_fc_ctrl *ctrl) return ret; } + if (!nr_io_queues && prior_ioq_cnt) { + dev_info(ctrl->ctrl.device, + "Fail Reconnect: At least 1 io queue " + "required (was %d)\n", prior_ioq_cnt); + return -ENOSPC; + } + ctrl->ctrl.queue_count = nr_io_queues + 1; /* check for io queues existing */ if (ctrl->ctrl.queue_count == 1) @@ -2489,6 +2508,10 @@ nvme_fc_recreate_io_queues(struct nvme_fc_ctrl *ctrl) if (ret) goto out_delete_hw_queues; + if (prior_ioq_cnt != nr_io_queues) + dev_info(ctrl->ctrl.device, + "reconnect: revising io queue count from %d to %d\n", + prior_ioq_cnt, nr_io_queues); blk_mq_update_nr_hw_queues(&ctrl->tag_set, nr_io_queues); return 0; @@ -3006,7 +3029,10 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, ctrl->ctrl.opts = opts; ctrl->ctrl.nr_reconnects = 0; - ctrl->ctrl.numa_node = dev_to_node(lport->dev); + if (lport->dev) + ctrl->ctrl.numa_node = dev_to_node(lport->dev); + else + ctrl->ctrl.numa_node = NUMA_NO_NODE; INIT_LIST_HEAD(&ctrl->ctrl_list); ctrl->lport = lport; ctrl->rport = rport; diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index b91f1838bbd5..527d64545023 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -87,6 +87,11 @@ enum nvme_quirks { * Ignore device provided subnqn. */ NVME_QUIRK_IGNORE_DEV_SUBNQN = (1 << 8), + + /* + * Broken Write Zeroes. + */ + NVME_QUIRK_DISABLE_WRITE_ZEROES = (1 << 9), }; /* diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 92bad1c810ac..a90cf5d63aac 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2937,7 +2937,8 @@ static const struct pci_device_id nvme_id_table[] = { { PCI_VDEVICE(INTEL, 0xf1a6), /* Intel 760p/Pro 7600p */ .driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN, }, { PCI_VDEVICE(INTEL, 0x5845), /* Qemu emulated controller */ - .driver_data = NVME_QUIRK_IDENTIFY_CNS, }, + .driver_data = NVME_QUIRK_IDENTIFY_CNS | + NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x1bb1, 0x0100), /* Seagate Nytro Flash Storage */ .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, { PCI_DEVICE(0x1c58, 0x0003), /* HGST adapter */ diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 208ee518af65..e7e08889865e 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -463,6 +463,15 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue, queue->data_remaining = le32_to_cpu(pdu->data_length); + if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS && + unlikely(!(pdu->hdr.flags & NVME_TCP_F_DATA_LAST))) { + dev_err(queue->ctrl->ctrl.device, + "queue %d tag %#x SUCCESS set but not last PDU\n", + nvme_tcp_queue_id(queue), rq->tag); + nvme_tcp_error_recovery(&queue->ctrl->ctrl); + return -EPROTO; + } + return 0; } @@ -618,6 +627,14 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb, return ret; } +static inline void nvme_tcp_end_request(struct request *rq, __le16 status) +{ + union nvme_result res = {}; + + nvme_end_request(rq, cpu_to_le16(status << 1), res); +} + + static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb, unsigned int *offset, size_t *len) { @@ -685,6 +702,8 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb, nvme_tcp_ddgst_final(queue->rcv_hash, &queue->exp_ddgst); queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH; } else { + if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) + nvme_tcp_end_request(rq, NVME_SC_SUCCESS); nvme_tcp_init_recv_ctx(queue); } } @@ -695,6 +714,7 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb, static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue, struct sk_buff *skb, unsigned int *offset, size_t *len) { + struct nvme_tcp_data_pdu *pdu = (void *)queue->pdu; char *ddgst = (char *)&queue->recv_ddgst; size_t recv_len = min_t(size_t, *len, queue->ddgst_remaining); off_t off = NVME_TCP_DIGEST_LENGTH - queue->ddgst_remaining; @@ -718,6 +738,13 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue, return -EIO; } + if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) { + struct request *rq = blk_mq_tag_to_rq(nvme_tcp_tagset(queue), + pdu->command_id); + + nvme_tcp_end_request(rq, NVME_SC_SUCCESS); + } + nvme_tcp_init_recv_ctx(queue); return 0; } @@ -815,10 +842,7 @@ static inline void nvme_tcp_done_send_req(struct nvme_tcp_queue *queue) static void nvme_tcp_fail_request(struct nvme_tcp_request *req) { - union nvme_result res = {}; - - nvme_end_request(blk_mq_rq_from_pdu(req), - cpu_to_le16(NVME_SC_DATA_XFER_ERROR), res); + nvme_tcp_end_request(blk_mq_rq_from_pdu(req), NVME_SC_DATA_XFER_ERROR); } static int nvme_tcp_try_send_data(struct nvme_tcp_request *req) diff --git a/drivers/nvme/host/trace.c b/drivers/nvme/host/trace.c index 58456de78bb2..5f24ea7a28eb 100644 --- a/drivers/nvme/host/trace.c +++ b/drivers/nvme/host/trace.c @@ -50,7 +50,19 @@ static const char *nvme_trace_admin_identify(struct trace_seq *p, u8 *cdw10) return ret; } +static const char *nvme_trace_admin_get_features(struct trace_seq *p, + u8 *cdw10) +{ + const char *ret = trace_seq_buffer_ptr(p); + u8 fid = cdw10[0]; + u8 sel = cdw10[1] & 0x7; + u32 cdw11 = get_unaligned_le32(cdw10 + 4); + + trace_seq_printf(p, "fid=0x%x sel=0x%x cdw11=0x%x", fid, sel, cdw11); + trace_seq_putc(p, 0); + return ret; +} static const char *nvme_trace_read_write(struct trace_seq *p, u8 *cdw10) { @@ -101,6 +113,8 @@ const char *nvme_trace_parse_admin_cmd(struct trace_seq *p, return nvme_trace_create_cq(p, cdw10); case nvme_admin_identify: return nvme_trace_admin_identify(p, cdw10); + case nvme_admin_get_features: + return nvme_trace_admin_get_features(p, cdw10); default: return nvme_trace_common(p, cdw10); } diff --git a/drivers/nvme/host/trace.h b/drivers/nvme/host/trace.h index 244d7c177e5a..97d3c77365b8 100644 --- a/drivers/nvme/host/trace.h +++ b/drivers/nvme/host/trace.h @@ -108,7 +108,7 @@ TRACE_EVENT(nvme_setup_cmd, __entry->metadata = le64_to_cpu(cmd->common.metadata); __assign_disk_name(__entry->disk, req->rq_disk); memcpy(__entry->cdw10, &cmd->common.cdw10, - 6 * sizeof(__entry->cdw10)); + sizeof(__entry->cdw10)); ), TP_printk("nvme%d: %sqid=%d, cmdid=%u, nsid=%u, flags=0x%x, meta=0x%llx, cmd=(%s %s)", __entry->ctrl_id, __print_disk_name(__entry->disk), diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index d44ede147263..2d73b66e3686 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -1163,6 +1163,15 @@ static void nvmet_release_p2p_ns_map(struct nvmet_ctrl *ctrl) put_device(ctrl->p2p_client); } +static void nvmet_fatal_error_handler(struct work_struct *work) +{ + struct nvmet_ctrl *ctrl = + container_of(work, struct nvmet_ctrl, fatal_err_work); + + pr_err("ctrl %d fatal error occurred!\n", ctrl->cntlid); + ctrl->ops->delete_ctrl(ctrl); +} + u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn, struct nvmet_req *req, u32 kato, struct nvmet_ctrl **ctrlp) { @@ -1205,6 +1214,7 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn, INIT_WORK(&ctrl->async_event_work, nvmet_async_event_work); INIT_LIST_HEAD(&ctrl->async_events); INIT_RADIX_TREE(&ctrl->p2p_ns_map, GFP_KERNEL); + INIT_WORK(&ctrl->fatal_err_work, nvmet_fatal_error_handler); memcpy(ctrl->subsysnqn, subsysnqn, NVMF_NQN_SIZE); memcpy(ctrl->hostnqn, hostnqn, NVMF_NQN_SIZE); @@ -1308,21 +1318,11 @@ void nvmet_ctrl_put(struct nvmet_ctrl *ctrl) kref_put(&ctrl->ref, nvmet_ctrl_free); } -static void nvmet_fatal_error_handler(struct work_struct *work) -{ - struct nvmet_ctrl *ctrl = - container_of(work, struct nvmet_ctrl, fatal_err_work); - - pr_err("ctrl %d fatal error occurred!\n", ctrl->cntlid); - ctrl->ops->delete_ctrl(ctrl); -} - void nvmet_ctrl_fatal_error(struct nvmet_ctrl *ctrl) { mutex_lock(&ctrl->lock); if (!(ctrl->csts & NVME_CSTS_CFS)) { ctrl->csts |= NVME_CSTS_CFS; - INIT_WORK(&ctrl->fatal_err_work, nvmet_fatal_error_handler); schedule_work(&ctrl->fatal_err_work); } mutex_unlock(&ctrl->lock); diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c index 1e9654f04c60..98b7b1f4ee96 100644 --- a/drivers/nvme/target/fc.c +++ b/drivers/nvme/target/fc.c @@ -1143,10 +1143,8 @@ __nvmet_fc_free_assocs(struct nvmet_fc_tgtport *tgtport) &tgtport->assoc_list, a_list) { if (!nvmet_fc_tgt_a_get(assoc)) continue; - spin_unlock_irqrestore(&tgtport->lock, flags); - nvmet_fc_delete_target_assoc(assoc); - nvmet_fc_tgt_a_put(assoc); - spin_lock_irqsave(&tgtport->lock, flags); + if (!schedule_work(&assoc->del_work)) + nvmet_fc_tgt_a_put(assoc); } spin_unlock_irqrestore(&tgtport->lock, flags); } @@ -1185,7 +1183,8 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl) nvmet_fc_tgtport_put(tgtport); if (found_ctrl) { - schedule_work(&assoc->del_work); + if (!schedule_work(&assoc->del_work)) + nvmet_fc_tgt_a_put(assoc); return; } @@ -1503,10 +1502,8 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport, (struct fcnvme_ls_disconnect_rqst *)iod->rqstbuf; struct fcnvme_ls_disconnect_acc *acc = (struct fcnvme_ls_disconnect_acc *)iod->rspbuf; - struct nvmet_fc_tgt_queue *queue = NULL; struct nvmet_fc_tgt_assoc *assoc; int ret = 0; - bool del_assoc = false; memset(acc, 0, sizeof(*acc)); @@ -1537,18 +1534,7 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport, assoc = nvmet_fc_find_target_assoc(tgtport, be64_to_cpu(rqst->associd.association_id)); iod->assoc = assoc; - if (assoc) { - if (rqst->discon_cmd.scope == - FCNVME_DISCONN_CONNECTION) { - queue = nvmet_fc_find_target_queue(tgtport, - be64_to_cpu( - rqst->discon_cmd.id)); - if (!queue) { - nvmet_fc_tgt_a_put(assoc); - ret = VERR_NO_CONN; - } - } - } else + if (!assoc) ret = VERR_NO_ASSOC; } @@ -1576,26 +1562,10 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport, sizeof(struct fcnvme_ls_disconnect_acc)), FCNVME_LS_DISCONNECT); - - /* are we to delete a Connection ID (queue) */ - if (queue) { - int qid = queue->qid; - - nvmet_fc_delete_target_queue(queue); - - /* release the get taken by find_target_queue */ - nvmet_fc_tgt_q_put(queue); - - /* tear association down if io queue terminated */ - if (!qid) - del_assoc = true; - } - /* release get taken in nvmet_fc_find_target_assoc */ nvmet_fc_tgt_a_put(iod->assoc); - if (del_assoc) - nvmet_fc_delete_target_assoc(iod->assoc); + nvmet_fc_delete_target_assoc(iod->assoc); } diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c index 71dfedbadc26..a065dbfc43b1 100644 --- a/drivers/nvme/target/io-cmd-bdev.c +++ b/drivers/nvme/target/io-cmd-bdev.c @@ -194,11 +194,11 @@ static u16 nvmet_bdev_discard_range(struct nvmet_req *req, le64_to_cpu(range->slba) << (ns->blksize_shift - 9), le32_to_cpu(range->nlb) << (ns->blksize_shift - 9), GFP_KERNEL, 0, bio); - - if (ret) + if (ret && ret != -EOPNOTSUPP) { req->error_slba = le64_to_cpu(range->slba); - - return blk_to_nvme_status(req, errno_to_blk_status(ret)); + return blk_to_nvme_status(req, errno_to_blk_status(ret)); + } + return NVME_SC_SUCCESS; } static void nvmet_bdev_execute_discard(struct nvmet_req *req) diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c index 517522305e5c..3e43212d3c1c 100644 --- a/drivers/nvme/target/io-cmd-file.c +++ b/drivers/nvme/target/io-cmd-file.c @@ -297,7 +297,7 @@ static void nvmet_file_execute_discard(struct nvmet_req *req) } ret = vfs_fallocate(req->ns->file, mode, offset, len); - if (ret) { + if (ret && ret != -EOPNOTSUPP) { req->error_slba = le64_to_cpu(range.slba); status = errno_to_nvme_status(req, ret); break; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 9cc1461aac7d..4734223ab702 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -1181,7 +1181,13 @@ int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, static void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) { - return memblock_alloc(size, align); + void *ptr = memblock_alloc(size, align); + + if (!ptr) + panic("%s: Failed to allocate %llu bytes align=0x%llx\n", + __func__, size, align); + + return ptr; } bool __init early_init_dt_verify(void *params) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index e773063c6de9..6a36bc0b3d64 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -31,28 +31,18 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, phys_addr_t *res_base) { phys_addr_t base; - /* - * We use __memblock_alloc_base() because memblock_alloc_base() - * panic()s on allocation failure. - */ + end = !end ? MEMBLOCK_ALLOC_ANYWHERE : end; align = !align ? SMP_CACHE_BYTES : align; - base = __memblock_alloc_base(size, align, end); + base = memblock_find_in_range(start, end, size, align); if (!base) return -ENOMEM; - /* - * Check if the allocated region fits in to start..end window - */ - if (base < start) { - memblock_free(base, size); - return -ENOMEM; - } - *res_base = base; if (nomap) return memblock_remove(base, size); - return 0; + + return memblock_reserve(base, size); } /** diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 66037511f2d7..cccde756b510 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -2241,7 +2241,13 @@ static struct device_node *overlay_base_root; static void * __init dt_alloc_memory(u64 size, u64 align) { - return memblock_alloc(size, align); + void *ptr = memblock_alloc(size, align); + + if (!ptr) + panic("%s: Failed to allocate %llu bytes align=0x%llx\n", + __func__, size, align); + + return ptr; } /* diff --git a/drivers/opp/core.c b/drivers/opp/core.c index d7f97167cac3..0420f7e8ad5b 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -760,7 +760,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) old_freq, freq); /* Scaling up? Configure required OPPs before frequency */ - if (freq > old_freq) { + if (freq >= old_freq) { ret = _set_required_opps(dev, opp_table, opp); if (ret) goto put_opp; diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 62504b18f198..c10c782d15aa 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -173,7 +173,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, struct opp_table **required_opp_tables; struct device **genpd_virt_devs = NULL; struct device_node *required_np, *np; - int count, i; + int count, count_pd, i; /* Traversing the first OPP node is all we need */ np = of_get_next_available_child(opp_np, NULL); @@ -186,7 +186,19 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, if (!count) goto put_np; - if (count > 1) { + /* + * Check the number of power-domains to know if we need to deal + * with virtual devices. In some cases we have devices with multiple + * power domains but with only one of them being scalable, hence + * 'count' could be 1, but we still have to deal with multiple genpds + * and virtual devices. + */ + count_pd = of_count_phandle_with_args(dev->of_node, "power-domains", + "#power-domain-cells"); + if (!count_pd) + goto put_np; + + if (count_pd > 1) { genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs), GFP_KERNEL); if (!genpd_virt_devs) diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index f180aa44a422..183d1ffe6a75 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -341,6 +341,7 @@ static const struct gpio_chip bcm2835_gpio_chip = { .get_direction = bcm2835_gpio_get_direction, .get = bcm2835_gpio_get, .set = bcm2835_gpio_set, + .set_config = gpiochip_generic_config, .base = -1, .ngpio = BCM2835_NUM_GPIOS, .can_sleep = false, @@ -960,7 +961,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, break; default: - return -EINVAL; + return -ENOTSUPP; } /* switch param type */ } /* for each config */ @@ -969,6 +970,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, } static const struct pinconf_ops bcm2835_pinconf_ops = { + .is_generic = true, .pin_config_get = bcm2835_pinconf_get, .pin_config_set = bcm2835_pinconf_set, }; diff --git a/drivers/pinctrl/berlin/pinctrl-as370.c b/drivers/pinctrl/berlin/pinctrl-as370.c index d2bb811fc5fa..44f8ccdbeeff 100644 --- a/drivers/pinctrl/berlin/pinctrl-as370.c +++ b/drivers/pinctrl/berlin/pinctrl-as370.c @@ -36,13 +36,13 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("I2S1_DO2", 0x0, 0x3, 0x0c, BERLIN_PINCTRL_FUNCTION(0x0, "por"), /* CORE RSTB */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* DO2 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm4"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM4 */ BERLIN_PINCTRL_FUNCTION(0x3, "gpio"), /* GPIO4 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG4 */ BERLIN_PINCTRL_GROUP("I2S1_DO3", 0x0, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO5 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* DO3 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm5"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM5 */ BERLIN_PINCTRL_FUNCTION(0x3, "spififib"), /* SPDIFIB */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG5 */ @@ -61,24 +61,24 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("I2S2_DI0", 0x0, 0x3, 0x1b, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO9 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI0 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm2"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM2 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG9 */ BERLIN_PINCTRL_GROUP("I2S2_DI1", 0x4, 0x3, 0x00, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO10 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI1 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm3"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM3 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG10 */ BERLIN_PINCTRL_GROUP("I2S2_DI2", 0x4, 0x3, 0x03, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO11 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI2 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdific"), /* SPDIFIC */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG11 */ BERLIN_PINCTRL_GROUP("I2S2_DI3", 0x4, 0x3, 0x06, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO12 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI3 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdifia"), /* SPDIFIA */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG12 */ @@ -98,14 +98,14 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("PDM_DI2", 0x4, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO16 */ BERLIN_PINCTRL_FUNCTION(0x1, "pdm"), /* DI2 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm4"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM4 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdifid"), /* SPDIFID */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG16 */ BERLIN_PINCTRL_GROUP("PDM_DI3", 0x4, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO17 */ BERLIN_PINCTRL_FUNCTION(0x1, "pdm"), /* DI3 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm5"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM5 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdifi"), /* SPDIFI */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG17 */ @@ -139,11 +139,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_FUNCTION(0x1, "emmc")), /* DATA7 */ BERLIN_PINCTRL_GROUP("NAND_ALE", 0x8, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* ALE */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */ BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO18 */ BERLIN_PINCTRL_GROUP("NAND_CLE", 0x8, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* CLE */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */ BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO19 */ BERLIN_PINCTRL_GROUP("NAND_WEn", 0x8, 0x3, 0x18, BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* WEn */ @@ -169,12 +169,12 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("SPI1_SS1n", 0xc, 0x3, 0x0c, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS1n */ BERLIN_PINCTRL_FUNCTION(0x2, "gpio"), /* GPIO26 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm2")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM2 */ BERLIN_PINCTRL_GROUP("SPI1_SS2n", 0xc, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* RXD */ BERLIN_PINCTRL_FUNCTION(0x1, "spi1"), /* SS2n */ BERLIN_PINCTRL_FUNCTION(0x2, "gpio"), /* GPIO27 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm3")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM3 */ BERLIN_PINCTRL_GROUP("SPI1_SS3n", 0xc, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* TXD */ BERLIN_PINCTRL_FUNCTION(0x1, "spi1"), /* SS3n */ @@ -182,11 +182,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("SPI1_SCLK", 0xc, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SCLK */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO29 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm4")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM4 */ BERLIN_PINCTRL_GROUP("SPI1_SDO", 0xc, 0x3, 0x18, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDO */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO30 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm5")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM5 */ BERLIN_PINCTRL_GROUP("SPI1_SDI", 0xc, 0x3, 0x1b, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDI */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* GPIO31 */ @@ -209,51 +209,51 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("TMS", 0x10, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TMS */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO37 */ - BERLIN_PINCTRL_FUNCTION(0x4, "pwm0")), + BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM0 */ BERLIN_PINCTRL_GROUP("TDI", 0x10, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDI */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO38 */ - BERLIN_PINCTRL_FUNCTION(0x4, "pwm1")), + BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM1 */ BERLIN_PINCTRL_GROUP("TDO", 0x10, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDO */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO39 */ - BERLIN_PINCTRL_FUNCTION(0x4, "pwm0")), + BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM0 */ BERLIN_PINCTRL_GROUP("PWM6", 0x10, 0x3, 0x18, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO40 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm6")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM6 */ BERLIN_PINCTRL_GROUP("PWM7", 0x10, 0x3, 0x1b, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO41 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm7")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM7 */ BERLIN_PINCTRL_GROUP("PWM0", 0x14, 0x3, 0x00, BERLIN_PINCTRL_FUNCTION(0x0, "por"), /* VDDCPUSOC RSTB */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm0"), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm"), /* PWM0 */ BERLIN_PINCTRL_FUNCTION(0x2, "gpio")), /* GPIO42 */ BERLIN_PINCTRL_GROUP("PWM1", 0x14, 0x3, 0x03, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO43 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm1")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM1 */ BERLIN_PINCTRL_GROUP("PWM2", 0x14, 0x3, 0x06, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO44 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm2")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM2 */ BERLIN_PINCTRL_GROUP("PWM3", 0x14, 0x3, 0x09, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO45 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm3")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM3 */ BERLIN_PINCTRL_GROUP("PWM4", 0x14, 0x3, 0x0c, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO46 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm4")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM4 */ BERLIN_PINCTRL_GROUP("PWM5", 0x14, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO47 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm5")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM5 */ BERLIN_PINCTRL_GROUP("URT1_RTSn", 0x14, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO48 */ BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* RTSn */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */ BERLIN_PINCTRL_FUNCTION(0x3, "tw1a"), /* SCL */ BERLIN_PINCTRL_FUNCTION(0x4, "aio"), /* DBG0 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG18 */ BERLIN_PINCTRL_GROUP("URT1_CTSn", 0x14, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO49 */ BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* CTSn */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */ BERLIN_PINCTRL_FUNCTION(0x3, "tw1a"), /* SDA */ BERLIN_PINCTRL_FUNCTION(0x4, "aio"), /* DBG1 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG19 */ @@ -308,11 +308,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("SD0_CDn", 0x1c, 0x3, 0x00, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO62 */ BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* CDn */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm2")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM2 */ BERLIN_PINCTRL_GROUP("SD0_WP", 0x1c, 0x3, 0x03, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO63 */ BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* WP */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm3")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM3 */ }; static const struct berlin_pinctrl_desc as370_soc_pinctrl_data = { diff --git a/drivers/pinctrl/cirrus/pinctrl-madera-core.c b/drivers/pinctrl/cirrus/pinctrl-madera-core.c index a5dda832024a..7c9694593f79 100644 --- a/drivers/pinctrl/cirrus/pinctrl-madera-core.c +++ b/drivers/pinctrl/cirrus/pinctrl-madera-core.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig index 72b869d888e2..0d8387851b87 100644 --- a/drivers/pinctrl/freescale/Kconfig +++ b/drivers/pinctrl/freescale/Kconfig @@ -122,6 +122,13 @@ config PINCTRL_IMX7ULP help Say Y here to enable the imx7ulp pinctrl driver +config PINCTRL_IMX8MM + bool "IMX8MM pinctrl driver" + depends on ARCH_MXC && ARM64 + select PINCTRL_IMX + help + Say Y here to enable the imx8mm pinctrl driver + config PINCTRL_IMX8MQ bool "IMX8MQ pinctrl driver" depends on ARCH_MXC && ARM64 @@ -129,9 +136,16 @@ config PINCTRL_IMX8MQ help Say Y here to enable the imx8mq pinctrl driver +config PINCTRL_IMX8QM + bool "IMX8QM pinctrl driver" + depends on IMX_SCU && ARCH_MXC && ARM64 + select PINCTRL_IMX_SCU + help + Say Y here to enable the imx8qm pinctrl driver + config PINCTRL_IMX8QXP bool "IMX8QXP pinctrl driver" - depends on ARCH_MXC && ARM64 + depends on IMX_SCU && ARCH_MXC && ARM64 select PINCTRL_IMX_SCU help Say Y here to enable the imx8qxp pinctrl driver diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile index 6ee398a3e406..02020a76bd9c 100644 --- a/drivers/pinctrl/freescale/Makefile +++ b/drivers/pinctrl/freescale/Makefile @@ -18,7 +18,9 @@ obj-$(CONFIG_PINCTRL_IMX6SX) += pinctrl-imx6sx.o obj-$(CONFIG_PINCTRL_IMX6UL) += pinctrl-imx6ul.o obj-$(CONFIG_PINCTRL_IMX7D) += pinctrl-imx7d.o obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o +obj-$(CONFIG_PINCTRL_IMX8MM) += pinctrl-imx8mm.o obj-$(CONFIG_PINCTRL_IMX8MQ) += pinctrl-imx8mq.o +obj-$(CONFIG_PINCTRL_IMX8QM) += pinctrl-imx8qm.o obj-$(CONFIG_PINCTRL_IMX8QXP) += pinctrl-imx8qxp.o obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o diff --git a/drivers/pinctrl/freescale/pinctrl-imx8mm.c b/drivers/pinctrl/freescale/pinctrl-imx8mm.c new file mode 100644 index 000000000000..6d1038af59f4 --- /dev/null +++ b/drivers/pinctrl/freescale/pinctrl-imx8mm.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> + +#include "pinctrl-imx.h" + +enum imx8mm_pads { + MX8MM_PAD_RESERVE0 = 0, + MX8MM_PAD_RESERVE1 = 1, + MX8MM_PAD_RESERVE2 = 2, + MX8MM_PAD_RESERVE3 = 3, + MX8MM_PAD_RESERVE4 = 4, + MX8MM_PAD_RESERVE5 = 5, + MX8MM_PAD_RESERVE6 = 6, + MX8MM_PAD_RESERVE7 = 7, + MX8MM_PAD_RESERVE8 = 8, + MX8MM_PAD_RESERVE9 = 9, + MX8MM_IOMUXC_GPIO1_IO00 = 10, + MX8MM_IOMUXC_GPIO1_IO01 = 11, + MX8MM_IOMUXC_GPIO1_IO02 = 12, + MX8MM_IOMUXC_GPIO1_IO03 = 13, + MX8MM_IOMUXC_GPIO1_IO04 = 14, + MX8MM_IOMUXC_GPIO1_IO05 = 15, + MX8MM_IOMUXC_GPIO1_IO06 = 16, + MX8MM_IOMUXC_GPIO1_IO07 = 17, + MX8MM_IOMUXC_GPIO1_IO08 = 18, + MX8MM_IOMUXC_GPIO1_IO09 = 19, + MX8MM_IOMUXC_GPIO1_IO10 = 20, + MX8MM_IOMUXC_GPIO1_IO11 = 21, + MX8MM_IOMUXC_GPIO1_IO12 = 22, + MX8MM_IOMUXC_GPIO1_IO13 = 23, + MX8MM_IOMUXC_GPIO1_IO14 = 24, + MX8MM_IOMUXC_GPIO1_IO15 = 25, + MX8MM_IOMUXC_ENET_MDC = 26, + MX8MM_IOMUXC_ENET_MDIO = 27, + MX8MM_IOMUXC_ENET_TD3 = 28, + MX8MM_IOMUXC_ENET_TD2 = 29, + MX8MM_IOMUXC_ENET_TD1 = 30, + MX8MM_IOMUXC_ENET_TD0 = 31, + MX8MM_IOMUXC_ENET_TX_CTL = 32, + MX8MM_IOMUXC_ENET_TXC = 33, + MX8MM_IOMUXC_ENET_RX_CTL = 34, + MX8MM_IOMUXC_ENET_RXC = 35, + MX8MM_IOMUXC_ENET_RD0 = 36, + MX8MM_IOMUXC_ENET_RD1 = 37, + MX8MM_IOMUXC_ENET_RD2 = 38, + MX8MM_IOMUXC_ENET_RD3 = 39, + MX8MM_IOMUXC_SD1_CLK = 40, + MX8MM_IOMUXC_SD1_CMD = 41, + MX8MM_IOMUXC_SD1_DATA0 = 42, + MX8MM_IOMUXC_SD1_DATA1 = 43, + MX8MM_IOMUXC_SD1_DATA2 = 44, + MX8MM_IOMUXC_SD1_DATA3 = 45, + MX8MM_IOMUXC_SD1_DATA4 = 46, + MX8MM_IOMUXC_SD1_DATA5 = 47, + MX8MM_IOMUXC_SD1_DATA6 = 48, + MX8MM_IOMUXC_SD1_DATA7 = 49, + MX8MM_IOMUXC_SD1_RESET_B = 50, + MX8MM_IOMUXC_SD1_STROBE = 51, + MX8MM_IOMUXC_SD2_CD_B = 52, + MX8MM_IOMUXC_SD2_CLK = 53, + MX8MM_IOMUXC_SD2_CMD = 54, + MX8MM_IOMUXC_SD2_DATA0 = 55, + MX8MM_IOMUXC_SD2_DATA1 = 56, + MX8MM_IOMUXC_SD2_DATA2 = 57, + MX8MM_IOMUXC_SD2_DATA3 = 58, + MX8MM_IOMUXC_SD2_RESET_B = 59, + MX8MM_IOMUXC_SD2_WP = 60, + MX8MM_IOMUXC_NAND_ALE = 61, + MX8MM_IOMUXC_NAND_CE0 = 62, + MX8MM_IOMUXC_NAND_CE1 = 63, + MX8MM_IOMUXC_NAND_CE2 = 64, + MX8MM_IOMUXC_NAND_CE3 = 65, + MX8MM_IOMUXC_NAND_CLE = 66, + MX8MM_IOMUXC_NAND_DATA00 = 67, + MX8MM_IOMUXC_NAND_DATA01 = 68, + MX8MM_IOMUXC_NAND_DATA02 = 69, + MX8MM_IOMUXC_NAND_DATA03 = 70, + MX8MM_IOMUXC_NAND_DATA04 = 71, + MX8MM_IOMUXC_NAND_DATA05 = 72, + MX8MM_IOMUXC_NAND_DATA06 = 73, + MX8MM_IOMUXC_NAND_DATA07 = 74, + MX8MM_IOMUXC_NAND_DQS = 75, + MX8MM_IOMUXC_NAND_RE_B = 76, + MX8MM_IOMUXC_NAND_READY_B = 77, + MX8MM_IOMUXC_NAND_WE_B = 78, + MX8MM_IOMUXC_NAND_WP_B = 79, + MX8MM_IOMUXC_SAI5_RXFS = 80, + MX8MM_IOMUXC_SAI5_RXC = 81, + MX8MM_IOMUXC_SAI5_RXD0 = 82, + MX8MM_IOMUXC_SAI5_RXD1 = 83, + MX8MM_IOMUXC_SAI5_RXD2 = 84, + MX8MM_IOMUXC_SAI5_RXD3 = 85, + MX8MM_IOMUXC_SAI5_MCLK = 86, + MX8MM_IOMUXC_SAI1_RXFS = 87, + MX8MM_IOMUXC_SAI1_RXC = 88, + MX8MM_IOMUXC_SAI1_RXD0 = 89, + MX8MM_IOMUXC_SAI1_RXD1 = 90, + MX8MM_IOMUXC_SAI1_RXD2 = 91, + MX8MM_IOMUXC_SAI1_RXD3 = 92, + MX8MM_IOMUXC_SAI1_RXD4 = 93, + MX8MM_IOMUXC_SAI1_RXD5 = 94, + MX8MM_IOMUXC_SAI1_RXD6 = 95, + MX8MM_IOMUXC_SAI1_RXD7 = 96, + MX8MM_IOMUXC_SAI1_TXFS = 97, + MX8MM_IOMUXC_SAI1_TXC = 98, + MX8MM_IOMUXC_SAI1_TXD0 = 99, + MX8MM_IOMUXC_SAI1_TXD1 = 100, + MX8MM_IOMUXC_SAI1_TXD2 = 101, + MX8MM_IOMUXC_SAI1_TXD3 = 102, + MX8MM_IOMUXC_SAI1_TXD4 = 103, + MX8MM_IOMUXC_SAI1_TXD5 = 104, + MX8MM_IOMUXC_SAI1_TXD6 = 105, + MX8MM_IOMUXC_SAI1_TXD7 = 106, + MX8MM_IOMUXC_SAI1_MCLK = 107, + MX8MM_IOMUXC_SAI2_RXFS = 108, + MX8MM_IOMUXC_SAI2_RXC = 109, + MX8MM_IOMUXC_SAI2_RXD0 = 110, + MX8MM_IOMUXC_SAI2_TXFS = 111, + MX8MM_IOMUXC_SAI2_TXC = 112, + MX8MM_IOMUXC_SAI2_TXD0 = 113, + MX8MM_IOMUXC_SAI2_MCLK = 114, + MX8MM_IOMUXC_SAI3_RXFS = 115, + MX8MM_IOMUXC_SAI3_RXC = 116, + MX8MM_IOMUXC_SAI3_RXD = 117, + MX8MM_IOMUXC_SAI3_TXFS = 118, + MX8MM_IOMUXC_SAI3_TXC = 119, + MX8MM_IOMUXC_SAI3_TXD = 120, + MX8MM_IOMUXC_SAI3_MCLK = 121, + MX8MM_IOMUXC_SPDIF_TX = 122, + MX8MM_IOMUXC_SPDIF_RX = 123, + MX8MM_IOMUXC_SPDIF_EXT_CLK = 124, + MX8MM_IOMUXC_ECSPI1_SCLK = 125, + MX8MM_IOMUXC_ECSPI1_MOSI = 126, + MX8MM_IOMUXC_ECSPI1_MISO = 127, + MX8MM_IOMUXC_ECSPI1_SS0 = 128, + MX8MM_IOMUXC_ECSPI2_SCLK = 129, + MX8MM_IOMUXC_ECSPI2_MOSI = 130, + MX8MM_IOMUXC_ECSPI2_MISO = 131, + MX8MM_IOMUXC_ECSPI2_SS0 = 132, + MX8MM_IOMUXC_I2C1_SCL = 133, + MX8MM_IOMUXC_I2C1_SDA = 134, + MX8MM_IOMUXC_I2C2_SCL = 135, + MX8MM_IOMUXC_I2C2_SDA = 136, + MX8MM_IOMUXC_I2C3_SCL = 137, + MX8MM_IOMUXC_I2C3_SDA = 138, + MX8MM_IOMUXC_I2C4_SCL = 139, + MX8MM_IOMUXC_I2C4_SDA = 140, + MX8MM_IOMUXC_UART1_RXD = 141, + MX8MM_IOMUXC_UART1_TXD = 142, + MX8MM_IOMUXC_UART2_RXD = 143, + MX8MM_IOMUXC_UART2_TXD = 144, + MX8MM_IOMUXC_UART3_RXD = 145, + MX8MM_IOMUXC_UART3_TXD = 146, + MX8MM_IOMUXC_UART4_RXD = 147, + MX8MM_IOMUXC_UART4_TXD = 148, +}; + +/* Pad names for the pinmux subsystem */ +static const struct pinctrl_pin_desc imx8mm_pinctrl_pads[] = { + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE0), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE1), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE2), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE3), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE4), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE5), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE6), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE7), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE8), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE9), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO00), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO01), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO02), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO03), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO04), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO05), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO06), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO07), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO08), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO09), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO10), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO11), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO12), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO13), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO14), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO15), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_MDC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_MDIO), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TX_CTL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RX_CTL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_CLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_CMD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA4), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA5), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA6), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA7), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_RESET_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_STROBE), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CD_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CMD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_RESET_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_WP), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_ALE), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CLE), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA00), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA01), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA02), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA03), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA04), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA05), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA06), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA07), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DQS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_RE_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_READY_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_WE_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_WP_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD4), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD5), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD6), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD7), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD4), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD5), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD6), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD7), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_TX), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_RX), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_EXT_CLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_SCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_MOSI), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_MISO), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_SS0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_SCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_MOSI), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_MISO), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_SS0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C1_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C1_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C2_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C2_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C3_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C3_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C4_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C4_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART1_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART1_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART2_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART2_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART3_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART3_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART4_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART4_TXD), +}; + +static const struct imx_pinctrl_soc_info imx8mm_pinctrl_info = { + .pins = imx8mm_pinctrl_pads, + .npins = ARRAY_SIZE(imx8mm_pinctrl_pads), + .gpr_compatible = "fsl,imx8mm-iomuxc-gpr", +}; + +static const struct of_device_id imx8mm_pinctrl_of_match[] = { + { .compatible = "fsl,imx8mm-iomuxc", .data = &imx8mm_pinctrl_info, }, + { /* sentinel */ } +}; + +static int imx8mm_pinctrl_probe(struct platform_device *pdev) +{ + return imx_pinctrl_probe(pdev, &imx8mm_pinctrl_info); +} + +static struct platform_driver imx8mm_pinctrl_driver = { + .driver = { + .name = "imx8mm-pinctrl", + .of_match_table = of_match_ptr(imx8mm_pinctrl_of_match), + .suppress_bind_attrs = true, + }, + .probe = imx8mm_pinctrl_probe, +}; + +static int __init imx8mm_pinctrl_init(void) +{ + return platform_driver_register(&imx8mm_pinctrl_driver); +} +arch_initcall(imx8mm_pinctrl_init); diff --git a/drivers/pinctrl/freescale/pinctrl-imx8qm.c b/drivers/pinctrl/freescale/pinctrl-imx8qm.c new file mode 100644 index 000000000000..0b6029b29731 --- /dev/null +++ b/drivers/pinctrl/freescale/pinctrl-imx8qm.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <dt-bindings/pinctrl/pads-imx8qm.h> +#include <linux/err.h> +#include <linux/firmware/imx/sci.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> + +#include "pinctrl-imx.h" + +static const struct pinctrl_pin_desc imx8qm_pinctrl_pads[] = { + IMX_PINCTRL_PIN(IMX8QM_SIM0_CLK), + IMX_PINCTRL_PIN(IMX8QM_SIM0_RST), + IMX_PINCTRL_PIN(IMX8QM_SIM0_IO), + IMX_PINCTRL_PIN(IMX8QM_SIM0_PD), + IMX_PINCTRL_PIN(IMX8QM_SIM0_POWER_EN), + IMX_PINCTRL_PIN(IMX8QM_SIM0_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_SIM), + IMX_PINCTRL_PIN(IMX8QM_M40_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_M40_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_M40_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_M40_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_M41_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_M41_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_M41_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_M41_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_GPT0_CLK), + IMX_PINCTRL_PIN(IMX8QM_GPT0_CAPTURE), + IMX_PINCTRL_PIN(IMX8QM_GPT0_COMPARE), + IMX_PINCTRL_PIN(IMX8QM_GPT1_CLK), + IMX_PINCTRL_PIN(IMX8QM_GPT1_CAPTURE), + IMX_PINCTRL_PIN(IMX8QM_GPT1_COMPARE), + IMX_PINCTRL_PIN(IMX8QM_UART0_RX), + IMX_PINCTRL_PIN(IMX8QM_UART0_TX), + IMX_PINCTRL_PIN(IMX8QM_UART0_RTS_B), + IMX_PINCTRL_PIN(IMX8QM_UART0_CTS_B), + IMX_PINCTRL_PIN(IMX8QM_UART1_TX), + IMX_PINCTRL_PIN(IMX8QM_UART1_RX), + IMX_PINCTRL_PIN(IMX8QM_UART1_RTS_B), + IMX_PINCTRL_PIN(IMX8QM_UART1_CTS_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOLH), + IMX_PINCTRL_PIN(IMX8QM_SCU_PMIC_MEMC_ON), + IMX_PINCTRL_PIN(IMX8QM_SCU_WDOG_OUT), + IMX_PINCTRL_PIN(IMX8QM_PMIC_I2C_SDA), + IMX_PINCTRL_PIN(IMX8QM_PMIC_I2C_SCL), + IMX_PINCTRL_PIN(IMX8QM_PMIC_EARLY_WARNING), + IMX_PINCTRL_PIN(IMX8QM_PMIC_INT_B), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_02), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_03), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_04), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_05), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_06), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_07), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE0), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE1), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE2), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE3), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE4), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE5), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_GPIO00), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_GPIO01), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C1_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C1_SDA), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_GPIO00), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_GPIO01), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C1_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C1_SDA), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_LVDSGPIO), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_MIPIDSIGPIO), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_MCLK_OUT), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_MCLK_OUT), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_HDMI_TX0_TS_SCL), + IMX_PINCTRL_PIN(IMX8QM_HDMI_TX0_TS_SDA), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_3V3_HDMIGPIO), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_FSR), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_FST), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_SCKR), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_SCKT), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX0), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX2_RX3), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX3_RX2), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX4_RX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX5_RX0), + IMX_PINCTRL_PIN(IMX8QM_SPDIF0_RX), + IMX_PINCTRL_PIN(IMX8QM_SPDIF0_TX), + IMX_PINCTRL_PIN(IMX8QM_SPDIF0_EXT_CLK), + IMX_PINCTRL_PIN(IMX8QM_SPI3_SCK), + IMX_PINCTRL_PIN(IMX8QM_SPI3_SDO), + IMX_PINCTRL_PIN(IMX8QM_SPI3_SDI), + IMX_PINCTRL_PIN(IMX8QM_SPI3_CS0), + IMX_PINCTRL_PIN(IMX8QM_SPI3_CS1), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHB), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_FSR), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_FST), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_SCKR), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_SCKT), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX0), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX2_RX3), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX3_RX2), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX4_RX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX5_RX0), + IMX_PINCTRL_PIN(IMX8QM_MCLK_IN0), + IMX_PINCTRL_PIN(IMX8QM_MCLK_OUT0), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHC), + IMX_PINCTRL_PIN(IMX8QM_SPI0_SCK), + IMX_PINCTRL_PIN(IMX8QM_SPI0_SDO), + IMX_PINCTRL_PIN(IMX8QM_SPI0_SDI), + IMX_PINCTRL_PIN(IMX8QM_SPI0_CS0), + IMX_PINCTRL_PIN(IMX8QM_SPI0_CS1), + IMX_PINCTRL_PIN(IMX8QM_SPI2_SCK), + IMX_PINCTRL_PIN(IMX8QM_SPI2_SDO), + IMX_PINCTRL_PIN(IMX8QM_SPI2_SDI), + IMX_PINCTRL_PIN(IMX8QM_SPI2_CS0), + IMX_PINCTRL_PIN(IMX8QM_SPI2_CS1), + IMX_PINCTRL_PIN(IMX8QM_SAI1_RXC), + IMX_PINCTRL_PIN(IMX8QM_SAI1_RXD), + IMX_PINCTRL_PIN(IMX8QM_SAI1_RXFS), + IMX_PINCTRL_PIN(IMX8QM_SAI1_TXC), + IMX_PINCTRL_PIN(IMX8QM_SAI1_TXD), + IMX_PINCTRL_PIN(IMX8QM_SAI1_TXFS), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHT), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN7), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN6), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN5), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN4), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN3), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN2), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN1), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN0), + IMX_PINCTRL_PIN(IMX8QM_MLB_SIG), + IMX_PINCTRL_PIN(IMX8QM_MLB_CLK), + IMX_PINCTRL_PIN(IMX8QM_MLB_DATA), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOLHT), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN0_RX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN0_TX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN1_RX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN1_TX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN2_RX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN2_TX), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOTHR), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC0), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC1), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC2), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_3V3_USB3IO), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_RESET_B), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_VSELECT), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_RESET_B), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_VSELECT), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_WP), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_CD_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSELSEP), + IMX_PINCTRL_PIN(IMX8QM_ENET0_MDIO), + IMX_PINCTRL_PIN(IMX8QM_ENET0_MDC), + IMX_PINCTRL_PIN(IMX8QM_ENET0_REFCLK_125M_25M), + IMX_PINCTRL_PIN(IMX8QM_ENET1_REFCLK_125M_25M), + IMX_PINCTRL_PIN(IMX8QM_ENET1_MDIO), + IMX_PINCTRL_PIN(IMX8QM_ENET1_MDC), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOCT), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SS0_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SS1_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SCLK), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DQS), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA3), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA2), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA1), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA0), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_QSPI1), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA0), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA1), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA2), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA3), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DQS), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SS0_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SS1_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SCLK), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SCLK), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA0), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA1), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA2), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA3), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DQS), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SS0_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SS1_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_QSPI0), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_CLKREQ_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_WAKE_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_PERST_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_CLKREQ_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_WAKE_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_PERST_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_PCIESEP), + IMX_PINCTRL_PIN(IMX8QM_USB_HSIC0_DATA), + IMX_PINCTRL_PIN(IMX8QM_USB_HSIC0_STROBE), + IMX_PINCTRL_PIN(IMX8QM_CALIBRATION_0_HSIC), + IMX_PINCTRL_PIN(IMX8QM_CALIBRATION_1_HSIC), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_CLK), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_CMD), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA0), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA1), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA2), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA3), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA4), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA5), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA6), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA7), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_STROBE), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_RESET_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_SD1FIX), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_CLK), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_CMD), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA0), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA1), + IMX_PINCTRL_PIN(IMX8QM_CTL_NAND_RE_P_N), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA2), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA3), + IMX_PINCTRL_PIN(IMX8QM_CTL_NAND_DQS_P_N), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA4), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA5), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA6), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA7), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_STROBE), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSEL2), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_CLK), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_CMD), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA0), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA1), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA2), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSEL3), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXC), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD3), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXC), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETB), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXC), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD3), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXC), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETA), +}; + +static const struct imx_pinctrl_soc_info imx8qm_pinctrl_info = { + .pins = imx8qm_pinctrl_pads, + .npins = ARRAY_SIZE(imx8qm_pinctrl_pads), + .flags = IMX_USE_SCU, +}; + +static const struct of_device_id imx8qm_pinctrl_of_match[] = { + { .compatible = "fsl,imx8qm-iomuxc", }, + { /* sentinel */ } +}; + +static int imx8qm_pinctrl_probe(struct platform_device *pdev) +{ + int ret; + + ret = imx_pinctrl_sc_ipc_init(pdev); + if (ret) + return ret; + + return imx_pinctrl_probe(pdev, &imx8qm_pinctrl_info); +} + +static struct platform_driver imx8qm_pinctrl_driver = { + .driver = { + .name = "imx8qm-pinctrl", + .of_match_table = of_match_ptr(imx8qm_pinctrl_of_match), + .suppress_bind_attrs = true, + }, + .probe = imx8qm_pinctrl_probe, +}; + +static int __init imx8qm_pinctrl_init(void) +{ + return platform_driver_register(&imx8qm_pinctrl_driver); +} +arch_initcall(imx8qm_pinctrl_init); diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c index 4a9e0d4c2bbc..b1c368455d30 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c @@ -290,7 +290,13 @@ static int mtk_xt_set_gpio_as_eint(void *data, unsigned long eint_n) return err; err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_SMT, MTK_ENABLE); - if (err) + /* SMT is supposed to be supported by every real GPIO and doesn't + * support virtual GPIOs, so the extra condition err != -ENOTSUPP + * is just for adding EINT support to these virtual GPIOs. It should + * add an extra flag in the pin descriptor when more pins with + * distinctive characteristic come out. + */ + if (err && err != -ENOTSUPP) return err; return 0; diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index ea87d739f534..96a4a72708e4 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -31,6 +31,9 @@ * In some cases the register ranges for pull enable and pull * direction are the same and thus there are only 3 register ranges. * + * Since Meson G12A SoC, the ao register ranges for gpio, pull enable + * and pull direction are the same, so there are only 2 register ranges. + * * For the pull and GPIO configuration every bank uses a contiguous * set of bits in the register sets described above; the same register * can be shared by more banks with different offsets. @@ -488,21 +491,26 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc, return PTR_ERR(pc->reg_mux); } - pc->reg_pull = meson_map_resource(pc, gpio_np, "pull"); - if (IS_ERR(pc->reg_pull)) { - dev_err(pc->dev, "pull registers not found\n"); - return PTR_ERR(pc->reg_pull); + pc->reg_gpio = meson_map_resource(pc, gpio_np, "gpio"); + if (IS_ERR(pc->reg_gpio)) { + dev_err(pc->dev, "gpio registers not found\n"); + return PTR_ERR(pc->reg_gpio); } + pc->reg_pull = meson_map_resource(pc, gpio_np, "pull"); + /* Use gpio region if pull one is not present */ + if (IS_ERR(pc->reg_pull)) + pc->reg_pull = pc->reg_gpio; + pc->reg_pullen = meson_map_resource(pc, gpio_np, "pull-enable"); /* Use pull region if pull-enable one is not present */ if (IS_ERR(pc->reg_pullen)) pc->reg_pullen = pc->reg_pull; - pc->reg_gpio = meson_map_resource(pc, gpio_np, "gpio"); - if (IS_ERR(pc->reg_gpio)) { - dev_err(pc->dev, "gpio registers not found\n"); - return PTR_ERR(pc->reg_gpio); + pc->reg_ds = meson_map_resource(pc, gpio_np, "ds"); + if (IS_ERR(pc->reg_ds)) { + dev_dbg(pc->dev, "ds registers not found - skipping\n"); + pc->reg_ds = NULL; } return 0; diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h index eff61ea1c67e..5eaab925f427 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.h +++ b/drivers/pinctrl/meson/pinctrl-meson.h @@ -120,6 +120,7 @@ struct meson_pinctrl { struct regmap *reg_pullen; struct regmap *reg_pull; struct regmap *reg_gpio; + struct regmap *reg_ds; struct gpio_chip chip; struct device_node *of_node; }; diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c index 0f140a802137..7f76000cc12e 100644 --- a/drivers/pinctrl/meson/pinctrl-meson8b.c +++ b/drivers/pinctrl/meson/pinctrl-meson8b.c @@ -346,6 +346,8 @@ static const unsigned int eth_rx_dv_pins[] = { DIF_1_P }; static const unsigned int eth_rx_clk_pins[] = { DIF_1_N }; static const unsigned int eth_txd0_1_pins[] = { DIF_2_P }; static const unsigned int eth_txd1_1_pins[] = { DIF_2_N }; +static const unsigned int eth_rxd3_pins[] = { DIF_2_P }; +static const unsigned int eth_rxd2_pins[] = { DIF_2_N }; static const unsigned int eth_tx_en_pins[] = { DIF_3_P }; static const unsigned int eth_ref_clk_pins[] = { DIF_3_N }; static const unsigned int eth_mdc_pins[] = { DIF_4_P }; @@ -599,6 +601,8 @@ static struct meson_pmx_group meson8b_cbus_groups[] = { GROUP(eth_ref_clk, 6, 8), GROUP(eth_mdc, 6, 9), GROUP(eth_mdio_en, 6, 10), + GROUP(eth_rxd3, 7, 22), + GROUP(eth_rxd2, 7, 23), }; static struct meson_pmx_group meson8b_aobus_groups[] = { @@ -748,7 +752,7 @@ static const char * const ethernet_groups[] = { "eth_tx_clk", "eth_tx_en", "eth_txd1_0", "eth_txd1_1", "eth_txd0_0", "eth_txd0_1", "eth_rx_clk", "eth_rx_dv", "eth_rxd1", "eth_rxd0", "eth_mdio_en", "eth_mdc", "eth_ref_clk", - "eth_txd2", "eth_txd3" + "eth_txd2", "eth_txd3", "eth_rxd3", "eth_rxd2" }; static const char * const i2c_a_groups[] = { diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index aa48b3f23c7f..6462d3ca7ceb 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -170,8 +170,8 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { PIN_GRP_GPIO("pwm1", 12, 1, BIT(4), "pwm"), PIN_GRP_GPIO("pwm2", 13, 1, BIT(5), "pwm"), PIN_GRP_GPIO("pwm3", 14, 1, BIT(6), "pwm"), - PIN_GRP_GPIO("pmic1", 17, 1, BIT(7), "pmic"), - PIN_GRP_GPIO("pmic0", 16, 1, BIT(8), "pmic"), + PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"), + PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"), PIN_GRP_GPIO("i2c2", 2, 2, BIT(9), "i2c"), PIN_GRP_GPIO("i2c1", 0, 2, BIT(10), "i2c"), PIN_GRP_GPIO("spi_cs1", 17, 1, BIT(12), "spi"), @@ -195,8 +195,11 @@ static struct armada_37xx_pin_group armada_37xx_sb_groups[] = { PIN_GRP_GPIO("usb2_drvvbus1", 1, 1, BIT(1), "drvbus"), PIN_GRP_GPIO("sdio_sb", 24, 6, BIT(2), "sdio"), PIN_GRP_GPIO("rgmii", 6, 12, BIT(3), "mii"), - PIN_GRP_GPIO("pcie1", 3, 2, BIT(4), "pcie"), - PIN_GRP_GPIO("ptp", 20, 3, BIT(5), "ptp"), + PIN_GRP_GPIO("smi", 18, 2, BIT(4), "smi"), + PIN_GRP_GPIO("pcie1", 3, 1, BIT(5), "pcie"), + PIN_GRP_GPIO("pcie1_clkreq", 4, 1, BIT(9), "pcie"), + PIN_GRP_GPIO("pcie1_wakeup", 5, 1, BIT(10), "pcie"), + PIN_GRP_GPIO("ptp", 20, 3, BIT(11) | BIT(12) | BIT(13), "ptp"), PIN_GRP("ptp_clk", 21, 1, BIT(6), "ptp", "mii"), PIN_GRP("ptp_trig", 22, 1, BIT(7), "ptp", "mii"), PIN_GRP_GPIO_3("mii_col", 23, 1, BIT(8) | BIT(14), 0, BIT(8), BIT(14), @@ -1104,8 +1107,8 @@ static int armada_3700_pinctrl_resume(struct device *dev) * to other IO drivers. */ static const struct dev_pm_ops armada_3700_pinctrl_pm_ops = { - .suspend_late = armada_3700_pinctrl_suspend, - .resume_early = armada_3700_pinctrl_resume, + .suspend_noirq = armada_3700_pinctrl_suspend, + .resume_noirq = armada_3700_pinctrl_resume, }; #define PINCTRL_ARMADA_37XX_DEV_PM_OPS (&armada_3700_pinctrl_pm_ops) diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c index 4cc2c47f8778..ec02739bd21b 100644 --- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c +++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c @@ -1056,17 +1056,22 @@ static struct nmk_gpio_chip *nmk_gpio_populate_chip(struct device_node *np, } if (of_property_read_u32(np, "gpio-bank", &id)) { dev_err(&pdev->dev, "populate: gpio-bank property not found\n"); + platform_device_put(gpio_pdev); return ERR_PTR(-EINVAL); } /* Already populated? */ nmk_chip = nmk_gpio_chips[id]; - if (nmk_chip) + if (nmk_chip) { + platform_device_put(gpio_pdev); return nmk_chip; + } nmk_chip = devm_kzalloc(&pdev->dev, sizeof(*nmk_chip), GFP_KERNEL); - if (!nmk_chip) + if (!nmk_chip) { + platform_device_put(gpio_pdev); return ERR_PTR(-ENOMEM); + } nmk_chip->bank = id; chip = &nmk_chip->chip; @@ -1077,13 +1082,17 @@ static struct nmk_gpio_chip *nmk_gpio_populate_chip(struct device_node *np, res = platform_get_resource(gpio_pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) + if (IS_ERR(base)) { + platform_device_put(gpio_pdev); return ERR_CAST(base); + } nmk_chip->addr = base; clk = clk_get(&gpio_pdev->dev, NULL); - if (IS_ERR(clk)) + if (IS_ERR(clk)) { + platform_device_put(gpio_pdev); return (void *) clk; + } clk_prepare(clk); nmk_chip->clk = clk; diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 2c7229380f08..2678603df14b 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -17,7 +17,6 @@ #include <linux/slab.h> #include <linux/debugfs.h> #include <linux/seq_file.h> -#include <linux/uaccess.h> #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinconf.h> @@ -369,225 +368,6 @@ static int pinconf_groups_show(struct seq_file *s, void *what) DEFINE_SHOW_ATTRIBUTE(pinconf_pins); DEFINE_SHOW_ATTRIBUTE(pinconf_groups); -#define MAX_NAME_LEN 15 - -struct dbg_cfg { - enum pinctrl_map_type map_type; - char dev_name[MAX_NAME_LEN + 1]; - char state_name[MAX_NAME_LEN + 1]; - char pin_name[MAX_NAME_LEN + 1]; -}; - -/* - * Goal is to keep this structure as global in order to simply read the - * pinconf-config file after a write to check config is as expected - */ -static struct dbg_cfg pinconf_dbg_conf; - -/** - * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl - * map, of the dev/pin/state that was last written to pinconf-config file. - * @s: string filled in with config description - * @d: not used - */ -static int pinconf_dbg_config_print(struct seq_file *s, void *d) -{ - struct pinctrl_maps *maps_node; - const struct pinctrl_map *map; - const struct pinctrl_map *found = NULL; - struct pinctrl_dev *pctldev; - struct dbg_cfg *dbg = &pinconf_dbg_conf; - int i; - - mutex_lock(&pinctrl_maps_mutex); - - /* Parse the pinctrl map and look for the elected pin/state */ - for_each_maps(maps_node, i, map) { - if (map->type != dbg->map_type) - continue; - if (strcmp(map->dev_name, dbg->dev_name)) - continue; - if (strcmp(map->name, dbg->state_name)) - continue; - - if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { - /* We found the right pin */ - found = map; - break; - } - } - - if (!found) { - seq_printf(s, "No config found for dev/state/pin, expected:\n"); - seq_printf(s, "Searched dev:%s\n", dbg->dev_name); - seq_printf(s, "Searched state:%s\n", dbg->state_name); - seq_printf(s, "Searched pin:%s\n", dbg->pin_name); - seq_printf(s, "Use: modify config_pin <devname> "\ - "<state> <pinname> <value>\n"); - goto exit; - } - - pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name); - seq_printf(s, "Dev %s has config of %s in state %s:\n", - dbg->dev_name, dbg->pin_name, dbg->state_name); - pinconf_show_config(s, pctldev, found->data.configs.configs, - found->data.configs.num_configs); - -exit: - mutex_unlock(&pinctrl_maps_mutex); - - return 0; -} - -/** - * pinconf_dbg_config_write() - modify the pinctrl config in the pinctrl - * map, of a dev/pin/state entry based on user entries to pinconf-config - * @user_buf: contains the modification request with expected format: - * modify <config> <devicename> <state> <name> <newvalue> - * modify is literal string, alternatives like add/delete not supported yet - * <config> is the configuration to be changed. Supported configs are - * "config_pin" or "config_group", alternatives like config_mux are not - * supported yet. - * <devicename> <state> <name> are values that should match the pinctrl-maps - * <newvalue> reflects the new config and is driver dependent - */ -static ssize_t pinconf_dbg_config_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct pinctrl_maps *maps_node; - const struct pinctrl_map *map; - const struct pinctrl_map *found = NULL; - struct pinctrl_dev *pctldev; - const struct pinconf_ops *confops = NULL; - struct dbg_cfg *dbg = &pinconf_dbg_conf; - const struct pinctrl_map_configs *configs; - char config[MAX_NAME_LEN + 1]; - char buf[128]; - char *b = &buf[0]; - int buf_size; - char *token; - int i; - - /* Get userspace string and assure termination */ - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - /* - * need to parse entry and extract parameters: - * modify configs_pin devicename state pinname newvalue - */ - - /* Get arg: 'modify' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strcmp(token, "modify")) - return -EINVAL; - - /* - * Get arg type: "config_pin" and "config_group" - * types are supported so far - */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (!strcmp(token, "config_pin")) - dbg->map_type = PIN_MAP_TYPE_CONFIGS_PIN; - else if (!strcmp(token, "config_group")) - dbg->map_type = PIN_MAP_TYPE_CONFIGS_GROUP; - else - return -EINVAL; - - /* get arg 'device_name' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(dbg->dev_name, token, MAX_NAME_LEN); - - /* get arg 'state_name' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(dbg->state_name, token, MAX_NAME_LEN); - - /* get arg 'pin_name' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(dbg->pin_name, token, MAX_NAME_LEN); - - /* get new_value of config' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(config, token, MAX_NAME_LEN); - - mutex_lock(&pinctrl_maps_mutex); - - /* Parse the pinctrl map and look for the selected dev/state/pin */ - for_each_maps(maps_node, i, map) { - if (strcmp(map->dev_name, dbg->dev_name)) - continue; - if (map->type != dbg->map_type) - continue; - if (strcmp(map->name, dbg->state_name)) - continue; - - /* we found the right pin / state, so overwrite config */ - if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { - found = map; - break; - } - } - - if (!found) { - count = -EINVAL; - goto exit; - } - - pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name); - if (pctldev) - confops = pctldev->desc->confops; - - if (confops && confops->pin_config_dbg_parse_modify) { - configs = &found->data.configs; - for (i = 0; i < configs->num_configs; i++) { - confops->pin_config_dbg_parse_modify(pctldev, - config, - &configs->configs[i]); - } - } - -exit: - mutex_unlock(&pinctrl_maps_mutex); - - return count; -} - -static int pinconf_dbg_config_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinconf_dbg_config_print, inode->i_private); -} - -static const struct file_operations pinconf_dbg_pinconfig_fops = { - .open = pinconf_dbg_config_open, - .write = pinconf_dbg_config_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { @@ -595,8 +375,6 @@ void pinconf_init_device_debugfs(struct dentry *devroot, devroot, pctldev, &pinconf_pins_fops); debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO, devroot, pctldev, &pinconf_groups_fops); - debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP), - devroot, pctldev, &pinconf_dbg_pinconfig_fops); } #endif diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 2a7d638978d8..6689995fa3aa 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -489,7 +489,7 @@ static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type) /* * If WAKE_INT_MASTER_REG.MaskStsEn is set, a software write to the * debounce registers of any GPIO will block wake/interrupt status - * generation for *all* GPIOs for a lenght of time that depends on + * generation for *all* GPIOs for a length of time that depends on * WAKE_INT_MASTER_REG.MaskStsLength[11:0]. During this period the * INTERRUPT_ENABLE bit will read as 0. * diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 3d49bbbcdbc7..cb7c432769a5 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -59,6 +59,9 @@ static int gpio_banks; #define OUTPUT (1 << 7) #define OUTPUT_VAL_SHIFT 8 #define OUTPUT_VAL (0x1 << OUTPUT_VAL_SHIFT) +#define SLEWRATE_SHIFT 9 +#define SLEWRATE_MASK 0x1 +#define SLEWRATE (SLEWRATE_MASK << SLEWRATE_SHIFT) #define DEBOUNCE (1 << 16) #define DEBOUNCE_VAL_SHIFT 17 #define DEBOUNCE_VAL (0x3fff << DEBOUNCE_VAL_SHIFT) @@ -72,10 +75,22 @@ static int gpio_banks; * DRIVE_STRENGTH_DEFAULT is just a placeholder to avoid changing the drive * strength when there is no dt config for it. */ -#define DRIVE_STRENGTH_DEFAULT (0 << DRIVE_STRENGTH_SHIFT) -#define DRIVE_STRENGTH_LOW (1 << DRIVE_STRENGTH_SHIFT) -#define DRIVE_STRENGTH_MED (2 << DRIVE_STRENGTH_SHIFT) -#define DRIVE_STRENGTH_HI (3 << DRIVE_STRENGTH_SHIFT) +enum drive_strength_bit { + DRIVE_STRENGTH_BIT_DEF, + DRIVE_STRENGTH_BIT_LOW, + DRIVE_STRENGTH_BIT_MED, + DRIVE_STRENGTH_BIT_HI, +}; + +#define DRIVE_STRENGTH_BIT_MSK(name) (DRIVE_STRENGTH_BIT_##name << \ + DRIVE_STRENGTH_SHIFT) + +enum slewrate_bit { + SLEWRATE_BIT_DIS, + SLEWRATE_BIT_ENA, +}; + +#define SLEWRATE_BIT_MSK(name) (SLEWRATE_BIT_##name << SLEWRATE_SHIFT) /** * struct at91_pmx_func - describes AT91 pinmux functions @@ -166,6 +181,8 @@ struct at91_pinctrl_mux_ops { unsigned (*get_drivestrength)(void __iomem *pio, unsigned pin); void (*set_drivestrength)(void __iomem *pio, unsigned pin, u32 strength); + unsigned (*get_slewrate)(void __iomem *pio, unsigned pin); + void (*set_slewrate)(void __iomem *pio, unsigned pin, u32 slewrate); /* irq */ int (*irq_type)(struct irq_data *d, unsigned type); }; @@ -551,7 +568,7 @@ static unsigned at91_mux_sama5d3_get_drivestrength(void __iomem *pio, /* SAMA5 strength is 1:1 with our defines, * except 0 is equivalent to low per datasheet */ if (!tmp) - tmp = DRIVE_STRENGTH_LOW; + tmp = DRIVE_STRENGTH_BIT_MSK(LOW); return tmp; } @@ -564,11 +581,32 @@ static unsigned at91_mux_sam9x5_get_drivestrength(void __iomem *pio, /* strength is inverse in SAM9x5s hardware with the pinctrl defines * hardware: 0 = hi, 1 = med, 2 = low, 3 = rsvd */ - tmp = DRIVE_STRENGTH_HI - tmp; + tmp = DRIVE_STRENGTH_BIT_MSK(HI) - tmp; return tmp; } +static unsigned at91_mux_sam9x60_get_drivestrength(void __iomem *pio, + unsigned pin) +{ + unsigned tmp = readl_relaxed(pio + SAM9X60_PIO_DRIVER1); + + if (tmp & BIT(pin)) + return DRIVE_STRENGTH_BIT_HI; + + return DRIVE_STRENGTH_BIT_LOW; +} + +static unsigned at91_mux_sam9x60_get_slewrate(void __iomem *pio, unsigned pin) +{ + unsigned tmp = readl_relaxed(pio + SAM9X60_PIO_SLEWR); + + if ((tmp & BIT(pin))) + return SLEWRATE_BIT_ENA; + + return SLEWRATE_BIT_DIS; +} + static void set_drive_strength(void __iomem *reg, unsigned pin, u32 strength) { unsigned tmp = readl_relaxed(reg); @@ -600,12 +638,51 @@ static void at91_mux_sam9x5_set_drivestrength(void __iomem *pio, unsigned pin, /* strength is inverse on SAM9x5s with our defines * 0 = hi, 1 = med, 2 = low, 3 = rsvd */ - setting = DRIVE_STRENGTH_HI - setting; + setting = DRIVE_STRENGTH_BIT_MSK(HI) - setting; set_drive_strength(pio + at91sam9x5_get_drive_register(pin), pin, setting); } +static void at91_mux_sam9x60_set_drivestrength(void __iomem *pio, unsigned pin, + u32 setting) +{ + unsigned int tmp; + + if (setting <= DRIVE_STRENGTH_BIT_DEF || + setting == DRIVE_STRENGTH_BIT_MED || + setting > DRIVE_STRENGTH_BIT_HI) + return; + + tmp = readl_relaxed(pio + SAM9X60_PIO_DRIVER1); + + /* Strength is 0: low, 1: hi */ + if (setting == DRIVE_STRENGTH_BIT_LOW) + tmp &= ~BIT(pin); + else + tmp |= BIT(pin); + + writel_relaxed(tmp, pio + SAM9X60_PIO_DRIVER1); +} + +static void at91_mux_sam9x60_set_slewrate(void __iomem *pio, unsigned pin, + u32 setting) +{ + unsigned int tmp; + + if (setting < SLEWRATE_BIT_DIS || setting > SLEWRATE_BIT_ENA) + return; + + tmp = readl_relaxed(pio + SAM9X60_PIO_SLEWR); + + if (setting == SLEWRATE_BIT_DIS) + tmp &= ~BIT(pin); + else + tmp |= BIT(pin); + + writel_relaxed(tmp, pio + SAM9X60_PIO_SLEWR); +} + static struct at91_pinctrl_mux_ops at91rm9200_ops = { .get_periph = at91_mux_get_periph, .mux_A_periph = at91_mux_set_A_periph, @@ -634,6 +711,28 @@ static struct at91_pinctrl_mux_ops at91sam9x5_ops = { .irq_type = alt_gpio_irq_type, }; +static const struct at91_pinctrl_mux_ops sam9x60_ops = { + .get_periph = at91_mux_pio3_get_periph, + .mux_A_periph = at91_mux_pio3_set_A_periph, + .mux_B_periph = at91_mux_pio3_set_B_periph, + .mux_C_periph = at91_mux_pio3_set_C_periph, + .mux_D_periph = at91_mux_pio3_set_D_periph, + .get_deglitch = at91_mux_pio3_get_deglitch, + .set_deglitch = at91_mux_pio3_set_deglitch, + .get_debounce = at91_mux_pio3_get_debounce, + .set_debounce = at91_mux_pio3_set_debounce, + .get_pulldown = at91_mux_pio3_get_pulldown, + .set_pulldown = at91_mux_pio3_set_pulldown, + .get_schmitt_trig = at91_mux_pio3_get_schmitt_trig, + .disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig, + .get_drivestrength = at91_mux_sam9x60_get_drivestrength, + .set_drivestrength = at91_mux_sam9x60_set_drivestrength, + .get_slewrate = at91_mux_sam9x60_get_slewrate, + .set_slewrate = at91_mux_sam9x60_set_slewrate, + .irq_type = alt_gpio_irq_type, + +}; + static struct at91_pinctrl_mux_ops sama5d3_ops = { .get_periph = at91_mux_pio3_get_periph, .mux_A_periph = at91_mux_pio3_set_A_periph, @@ -893,6 +992,8 @@ static int at91_pinconf_get(struct pinctrl_dev *pctldev, if (info->ops->get_drivestrength) *config |= (info->ops->get_drivestrength(pio, pin) << DRIVE_STRENGTH_SHIFT); + if (info->ops->get_slewrate) + *config |= (info->ops->get_slewrate(pio, pin) << SLEWRATE_SHIFT); if (at91_mux_get_output(pio, pin, &out)) *config |= OUTPUT | (out << OUTPUT_VAL_SHIFT); @@ -944,6 +1045,9 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev, info->ops->set_drivestrength(pio, pin, (config & DRIVE_STRENGTH) >> DRIVE_STRENGTH_SHIFT); + if (info->ops->set_slewrate) + info->ops->set_slewrate(pio, pin, + (config & SLEWRATE) >> SLEWRATE_SHIFT); } /* for each config */ @@ -959,11 +1063,11 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev, } \ } while (0) -#define DBG_SHOW_FLAG_MASKED(mask,flag) do { \ +#define DBG_SHOW_FLAG_MASKED(mask, flag, name) do { \ if ((config & mask) == flag) { \ if (num_conf) \ seq_puts(s, "|"); \ - seq_puts(s, #flag); \ + seq_puts(s, #name); \ num_conf++; \ } \ } while (0) @@ -981,9 +1085,13 @@ static void at91_pinconf_dbg_show(struct pinctrl_dev *pctldev, DBG_SHOW_FLAG(PULL_DOWN); DBG_SHOW_FLAG(DIS_SCHMIT); DBG_SHOW_FLAG(DEGLITCH); - DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_LOW); - DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_MED); - DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_HI); + DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(LOW), + DRIVE_STRENGTH_LOW); + DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(MED), + DRIVE_STRENGTH_MED); + DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(HI), + DRIVE_STRENGTH_HI); + DBG_SHOW_FLAG(SLEWRATE); DBG_SHOW_FLAG(DEBOUNCE); if (config & DEBOUNCE) { val = config >> DEBOUNCE_VAL_SHIFT; @@ -1155,6 +1263,7 @@ static const struct of_device_id at91_pinctrl_of_match[] = { { .compatible = "atmel,sama5d3-pinctrl", .data = &sama5d3_ops }, { .compatible = "atmel,at91sam9x5-pinctrl", .data = &at91sam9x5_ops }, { .compatible = "atmel,at91rm9200-pinctrl", .data = &at91rm9200_ops }, + { .compatible = "microchip,sam9x60-pinctrl", .data = &sam9x60_ops }, { /* sentinel */ } }; @@ -1697,6 +1806,7 @@ static const struct gpio_chip at91_gpio_template = { static const struct of_device_id at91_gpio_of_match[] = { { .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, }, { .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops }, + { .compatible = "microchip,sam9x60-gpio", .data = &sam9x60_ops }, { /* sentinel */ } }; diff --git a/drivers/pinctrl/pinctrl-at91.h b/drivers/pinctrl/pinctrl-at91.h index 79b957f1dfa2..223620f14b05 100644 --- a/drivers/pinctrl/pinctrl-at91.h +++ b/drivers/pinctrl/pinctrl-at91.h @@ -69,4 +69,7 @@ #define AT91SAM9X5_PIO_DRIVER1 0x114 /*PIO Driver 1 register offset*/ #define AT91SAM9X5_PIO_DRIVER2 0x118 /*PIO Driver 2 register offset*/ +#define SAM9X60_PIO_SLEWR 0x110 /* PIO Slew Rate Control Register */ +#define SAM9X60_PIO_DRIVER1 0x118 /* PIO Driver 1 register offset */ + #endif diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index db6b48ea5f47..bc21ceb15d68 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -233,6 +233,19 @@ static int jz4725b_pwm_pwm2_pins[] = { 0x4c, }; static int jz4725b_pwm_pwm3_pins[] = { 0x4d, }; static int jz4725b_pwm_pwm4_pins[] = { 0x4e, }; static int jz4725b_pwm_pwm5_pins[] = { 0x4f, }; +static int jz4725b_lcd_8bit_pins[] = { + 0x72, 0x73, 0x74, + 0x60, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, +}; +static int jz4725b_lcd_16bit_pins[] = { + 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, +}; +static int jz4725b_lcd_18bit_pins[] = { 0x70, 0x71, }; +static int jz4725b_lcd_24bit_pins[] = { 0x76, 0x77, 0x78, 0x79, }; +static int jz4725b_lcd_special_pins[] = { 0x76, 0x77, 0x78, 0x79, }; +static int jz4725b_lcd_generic_pins[] = { 0x75, }; static int jz4725b_mmc0_1bit_funcs[] = { 1, 1, 1, }; static int jz4725b_mmc0_4bit_funcs[] = { 1, 0, 1, }; @@ -251,6 +264,12 @@ static int jz4725b_pwm_pwm2_funcs[] = { 0, }; static int jz4725b_pwm_pwm3_funcs[] = { 0, }; static int jz4725b_pwm_pwm4_funcs[] = { 0, }; static int jz4725b_pwm_pwm5_funcs[] = { 0, }; +static int jz4725b_lcd_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4725b_lcd_16bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4725b_lcd_18bit_funcs[] = { 0, 0, }; +static int jz4725b_lcd_24bit_funcs[] = { 1, 1, 1, 1, }; +static int jz4725b_lcd_special_funcs[] = { 0, 0, 0, 0, }; +static int jz4725b_lcd_generic_funcs[] = { 0, }; static const struct group_desc jz4725b_groups[] = { INGENIC_PIN_GROUP("mmc0-1bit", jz4725b_mmc0_1bit), @@ -270,6 +289,12 @@ static const struct group_desc jz4725b_groups[] = { INGENIC_PIN_GROUP("pwm3", jz4725b_pwm_pwm3), INGENIC_PIN_GROUP("pwm4", jz4725b_pwm_pwm4), INGENIC_PIN_GROUP("pwm5", jz4725b_pwm_pwm5), + INGENIC_PIN_GROUP("lcd-8bit", jz4725b_lcd_8bit), + INGENIC_PIN_GROUP("lcd-16bit", jz4725b_lcd_16bit), + INGENIC_PIN_GROUP("lcd-18bit", jz4725b_lcd_18bit), + INGENIC_PIN_GROUP("lcd-24bit", jz4725b_lcd_24bit), + INGENIC_PIN_GROUP("lcd-special", jz4725b_lcd_special), + INGENIC_PIN_GROUP("lcd-generic", jz4725b_lcd_generic), }; static const char *jz4725b_mmc0_groups[] = { "mmc0-1bit", "mmc0-4bit", }; @@ -285,6 +310,10 @@ static const char *jz4725b_pwm2_groups[] = { "pwm2", }; static const char *jz4725b_pwm3_groups[] = { "pwm3", }; static const char *jz4725b_pwm4_groups[] = { "pwm4", }; static const char *jz4725b_pwm5_groups[] = { "pwm5", }; +static const char *jz4725b_lcd_groups[] = { + "lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-24bit", + "lcd-special", "lcd-generic", +}; static const struct function_desc jz4725b_functions[] = { { "mmc0", jz4725b_mmc0_groups, ARRAY_SIZE(jz4725b_mmc0_groups), }, @@ -297,6 +326,7 @@ static const struct function_desc jz4725b_functions[] = { { "pwm3", jz4725b_pwm3_groups, ARRAY_SIZE(jz4725b_pwm3_groups), }, { "pwm4", jz4725b_pwm4_groups, ARRAY_SIZE(jz4725b_pwm4_groups), }, { "pwm5", jz4725b_pwm5_groups, ARRAY_SIZE(jz4725b_pwm5_groups), }, + { "lcd", jz4725b_lcd_groups, ARRAY_SIZE(jz4725b_lcd_groups), }, }; static const struct ingenic_chip_info jz4725b_chip_info = { @@ -321,47 +351,57 @@ static int jz4770_uart0_data_pins[] = { 0xa0, 0xa3, }; static int jz4770_uart0_hwflow_pins[] = { 0xa1, 0xa2, }; static int jz4770_uart1_data_pins[] = { 0x7a, 0x7c, }; static int jz4770_uart1_hwflow_pins[] = { 0x7b, 0x7d, }; -static int jz4770_uart2_data_pins[] = { 0x66, 0x67, }; -static int jz4770_uart2_hwflow_pins[] = { 0x65, 0x64, }; +static int jz4770_uart2_data_pins[] = { 0x5c, 0x5e, }; +static int jz4770_uart2_hwflow_pins[] = { 0x5d, 0x5f, }; static int jz4770_uart3_data_pins[] = { 0x6c, 0x85, }; static int jz4770_uart3_hwflow_pins[] = { 0x88, 0x89, }; -static int jz4770_uart4_data_pins[] = { 0x54, 0x4a, }; -static int jz4770_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, }; -static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, }; static int jz4770_mmc0_1bit_a_pins[] = { 0x12, 0x13, 0x14, }; -static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, }; static int jz4770_mmc0_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, }; -static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, }; +static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc0_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, }; static int jz4770_mmc1_1bit_d_pins[] = { 0x78, 0x79, 0x74, }; -static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, }; static int jz4770_mmc1_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, }; -static int jz4770_nemc_data_pins[] = { +static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc1_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, }; +static int jz4770_mmc2_1bit_b_pins[] = { 0x3c, 0x3d, 0x34, }; +static int jz4770_mmc2_4bit_b_pins[] = { 0x35, 0x3e, 0x3f, }; +static int jz4770_mmc2_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, }; +static int jz4770_mmc2_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc2_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, }; +static int jz4770_nemc_8bit_data_pins[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, }; +static int jz4770_nemc_16bit_data_pins[] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +}; static int jz4770_nemc_cle_ale_pins[] = { 0x20, 0x21, }; static int jz4770_nemc_addr_pins[] = { 0x22, 0x23, 0x24, 0x25, }; static int jz4770_nemc_rd_we_pins[] = { 0x10, 0x11, }; static int jz4770_nemc_frd_fwe_pins[] = { 0x12, 0x13, }; +static int jz4770_nemc_wait_pins[] = { 0x1b, }; static int jz4770_nemc_cs1_pins[] = { 0x15, }; static int jz4770_nemc_cs2_pins[] = { 0x16, }; static int jz4770_nemc_cs3_pins[] = { 0x17, }; static int jz4770_nemc_cs4_pins[] = { 0x18, }; static int jz4770_nemc_cs5_pins[] = { 0x19, }; static int jz4770_nemc_cs6_pins[] = { 0x1a, }; -static int jz4770_i2c0_pins[] = { 0x6e, 0x6f, }; -static int jz4770_i2c1_pins[] = { 0x8e, 0x8f, }; +static int jz4770_i2c0_pins[] = { 0x7e, 0x7f, }; +static int jz4770_i2c1_pins[] = { 0x9e, 0x9f, }; static int jz4770_i2c2_pins[] = { 0xb0, 0xb1, }; -static int jz4770_i2c3_pins[] = { 0x6a, 0x6b, }; -static int jz4770_i2c4_e_pins[] = { 0x8c, 0x8d, }; -static int jz4770_i2c4_f_pins[] = { 0xb9, 0xb8, }; -static int jz4770_cim_pins[] = { - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, +static int jz4770_cim_8bit_pins[] = { + 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, +}; +static int jz4770_cim_12bit_pins[] = { + 0x32, 0x33, 0xb0, 0xb1, }; -static int jz4770_lcd_32bit_pins[] = { +static int jz4770_lcd_24bit_pins[] = { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x51, + 0x58, 0x59, 0x5a, 0x5b, }; static int jz4770_pwm_pwm0_pins[] = { 0x80, }; static int jz4770_pwm_pwm1_pins[] = { 0x81, }; @@ -371,30 +411,41 @@ static int jz4770_pwm_pwm4_pins[] = { 0x84, }; static int jz4770_pwm_pwm5_pins[] = { 0x85, }; static int jz4770_pwm_pwm6_pins[] = { 0x6a, }; static int jz4770_pwm_pwm7_pins[] = { 0x6b, }; +static int jz4770_mac_rmii_pins[] = { + 0xa9, 0xab, 0xaa, 0xac, 0xa5, 0xa4, 0xad, 0xae, 0xa6, 0xa8, +}; +static int jz4770_mac_mii_pins[] = { 0xa7, 0xaf, }; static int jz4770_uart0_data_funcs[] = { 0, 0, }; static int jz4770_uart0_hwflow_funcs[] = { 0, 0, }; static int jz4770_uart1_data_funcs[] = { 0, 0, }; static int jz4770_uart1_hwflow_funcs[] = { 0, 0, }; -static int jz4770_uart2_data_funcs[] = { 1, 1, }; -static int jz4770_uart2_hwflow_funcs[] = { 1, 1, }; +static int jz4770_uart2_data_funcs[] = { 0, 0, }; +static int jz4770_uart2_hwflow_funcs[] = { 0, 0, }; static int jz4770_uart3_data_funcs[] = { 0, 1, }; static int jz4770_uart3_hwflow_funcs[] = { 0, 0, }; -static int jz4770_uart4_data_funcs[] = { 2, 2, }; -static int jz4770_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, }; -static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, }; static int jz4770_mmc0_1bit_a_funcs[] = { 1, 1, 0, }; -static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, }; static int jz4770_mmc0_1bit_e_funcs[] = { 0, 0, 0, }; -static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc0_8bit_e_funcs[] = { 0, 0, 0, 0, }; static int jz4770_mmc1_1bit_d_funcs[] = { 0, 0, 0, }; -static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, }; +static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, }; static int jz4770_mmc1_1bit_e_funcs[] = { 1, 1, 1, }; -static int jz4770_nemc_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, }; +static int jz4770_mmc1_8bit_e_funcs[] = { 1, 1, 1, 1, }; +static int jz4770_mmc2_1bit_b_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc2_4bit_b_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc2_1bit_e_funcs[] = { 2, 2, 2, }; +static int jz4770_mmc2_4bit_e_funcs[] = { 2, 2, 2, }; +static int jz4770_mmc2_8bit_e_funcs[] = { 2, 2, 2, 2, }; +static int jz4770_nemc_8bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_nemc_16bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; static int jz4770_nemc_cle_ale_funcs[] = { 0, 0, }; static int jz4770_nemc_addr_funcs[] = { 0, 0, 0, 0, }; static int jz4770_nemc_rd_we_funcs[] = { 0, 0, }; static int jz4770_nemc_frd_fwe_funcs[] = { 0, 0, }; +static int jz4770_nemc_wait_funcs[] = { 0, }; static int jz4770_nemc_cs1_funcs[] = { 0, }; static int jz4770_nemc_cs2_funcs[] = { 0, }; static int jz4770_nemc_cs3_funcs[] = { 0, }; @@ -404,14 +455,13 @@ static int jz4770_nemc_cs6_funcs[] = { 0, }; static int jz4770_i2c0_funcs[] = { 0, 0, }; static int jz4770_i2c1_funcs[] = { 0, 0, }; static int jz4770_i2c2_funcs[] = { 2, 2, }; -static int jz4770_i2c3_funcs[] = { 1, 1, }; -static int jz4770_i2c4_e_funcs[] = { 1, 1, }; -static int jz4770_i2c4_f_funcs[] = { 1, 1, }; -static int jz4770_cim_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -static int jz4770_lcd_32bit_funcs[] = { +static int jz4770_cim_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_cim_12bit_funcs[] = { 0, 0, 0, 0, }; +static int jz4770_lcd_24bit_funcs[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, + 0, 0, 0, 0, }; static int jz4770_pwm_pwm0_funcs[] = { 0, }; static int jz4770_pwm_pwm1_funcs[] = { 0, }; @@ -421,6 +471,8 @@ static int jz4770_pwm_pwm4_funcs[] = { 0, }; static int jz4770_pwm_pwm5_funcs[] = { 0, }; static int jz4770_pwm_pwm6_funcs[] = { 0, }; static int jz4770_pwm_pwm7_funcs[] = { 0, }; +static int jz4770_mac_rmii_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_mac_mii_funcs[] = { 0, 0, }; static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data), @@ -431,21 +483,28 @@ static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("uart2-hwflow", jz4770_uart2_hwflow), INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data), INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow), - INGENIC_PIN_GROUP("uart4-data", jz4770_uart4_data), - INGENIC_PIN_GROUP("mmc0-8bit-a", jz4770_mmc0_8bit_a), - INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a), INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a), - INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e), + INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a), INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e), - INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d), + INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e), + INGENIC_PIN_GROUP("mmc0-8bit-e", jz4770_mmc0_8bit_e), INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d), - INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), + INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d), INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e), - INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_data), + INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), + INGENIC_PIN_GROUP("mmc1-8bit-e", jz4770_mmc1_8bit_e), + INGENIC_PIN_GROUP("mmc2-1bit-b", jz4770_mmc2_1bit_b), + INGENIC_PIN_GROUP("mmc2-4bit-b", jz4770_mmc2_4bit_b), + INGENIC_PIN_GROUP("mmc2-1bit-e", jz4770_mmc2_1bit_e), + INGENIC_PIN_GROUP("mmc2-4bit-e", jz4770_mmc2_4bit_e), + INGENIC_PIN_GROUP("mmc2-8bit-e", jz4770_mmc2_8bit_e), + INGENIC_PIN_GROUP("nemc-8bit-data", jz4770_nemc_8bit_data), + INGENIC_PIN_GROUP("nemc-16bit-data", jz4770_nemc_16bit_data), INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale), INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr), INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we), INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe), + INGENIC_PIN_GROUP("nemc-wait", jz4770_nemc_wait), INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1), INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2), INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3), @@ -455,11 +514,9 @@ static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0), INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1), INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2), - INGENIC_PIN_GROUP("i2c3-data", jz4770_i2c3), - INGENIC_PIN_GROUP("i2c4-data-e", jz4770_i2c4_e), - INGENIC_PIN_GROUP("i2c4-data-f", jz4770_i2c4_f), - INGENIC_PIN_GROUP("cim-data", jz4770_cim), - INGENIC_PIN_GROUP("lcd-32bit", jz4770_lcd_32bit), + INGENIC_PIN_GROUP("cim-data-8bit", jz4770_cim_8bit), + INGENIC_PIN_GROUP("cim-data-12bit", jz4770_cim_12bit), + INGENIC_PIN_GROUP("lcd-24bit", jz4770_lcd_24bit), { "lcd-no-pins", }, INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0), INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1), @@ -469,32 +526,41 @@ static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5), INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6), INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7), + INGENIC_PIN_GROUP("mac-rmii", jz4770_mac_rmii), + INGENIC_PIN_GROUP("mac-mii", jz4770_mac_mii), }; static const char *jz4770_uart0_groups[] = { "uart0-data", "uart0-hwflow", }; static const char *jz4770_uart1_groups[] = { "uart1-data", "uart1-hwflow", }; static const char *jz4770_uart2_groups[] = { "uart2-data", "uart2-hwflow", }; static const char *jz4770_uart3_groups[] = { "uart3-data", "uart3-hwflow", }; -static const char *jz4770_uart4_groups[] = { "uart4-data", }; static const char *jz4770_mmc0_groups[] = { - "mmc0-8bit-a", "mmc0-4bit-a", "mmc0-1bit-a", - "mmc0-1bit-e", "mmc0-4bit-e", + "mmc0-1bit-a", "mmc0-4bit-a", + "mmc0-1bit-e", "mmc0-4bit-e", "mmc0-8bit-e", }; static const char *jz4770_mmc1_groups[] = { - "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e", + "mmc1-1bit-d", "mmc1-4bit-d", + "mmc1-1bit-e", "mmc1-4bit-e", "mmc1-8bit-e", +}; +static const char *jz4770_mmc2_groups[] = { + "mmc2-1bit-b", "mmc2-4bit-b", + "mmc2-1bit-e", "mmc2-4bit-e", "mmc2-8bit-e", }; static const char *jz4770_nemc_groups[] = { - "nemc-data", "nemc-cle-ale", "nemc-addr", "nemc-rd-we", "nemc-frd-fwe", + "nemc-8bit-data", "nemc-16bit-data", "nemc-cle-ale", + "nemc-addr", "nemc-rd-we", "nemc-frd-fwe", "nemc-wait", }; static const char *jz4770_cs1_groups[] = { "nemc-cs1", }; +static const char *jz4770_cs2_groups[] = { "nemc-cs2", }; +static const char *jz4770_cs3_groups[] = { "nemc-cs3", }; +static const char *jz4770_cs4_groups[] = { "nemc-cs4", }; +static const char *jz4770_cs5_groups[] = { "nemc-cs5", }; static const char *jz4770_cs6_groups[] = { "nemc-cs6", }; static const char *jz4770_i2c0_groups[] = { "i2c0-data", }; static const char *jz4770_i2c1_groups[] = { "i2c1-data", }; static const char *jz4770_i2c2_groups[] = { "i2c2-data", }; -static const char *jz4770_i2c3_groups[] = { "i2c3-data", }; -static const char *jz4770_i2c4_groups[] = { "i2c4-data-e", "i2c4-data-f", }; -static const char *jz4770_cim_groups[] = { "cim-data", }; -static const char *jz4770_lcd_groups[] = { "lcd-32bit", "lcd-no-pins", }; +static const char *jz4770_cim_groups[] = { "cim-data-8bit", "cim-data-12bit", }; +static const char *jz4770_lcd_groups[] = { "lcd-24bit", "lcd-no-pins", }; static const char *jz4770_pwm0_groups[] = { "pwm0", }; static const char *jz4770_pwm1_groups[] = { "pwm1", }; static const char *jz4770_pwm2_groups[] = { "pwm2", }; @@ -503,23 +569,26 @@ static const char *jz4770_pwm4_groups[] = { "pwm4", }; static const char *jz4770_pwm5_groups[] = { "pwm5", }; static const char *jz4770_pwm6_groups[] = { "pwm6", }; static const char *jz4770_pwm7_groups[] = { "pwm7", }; +static const char *jz4770_mac_groups[] = { "mac-rmii", "mac-mii", }; static const struct function_desc jz4770_functions[] = { { "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), }, { "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), }, { "uart2", jz4770_uart2_groups, ARRAY_SIZE(jz4770_uart2_groups), }, { "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), }, - { "uart4", jz4770_uart4_groups, ARRAY_SIZE(jz4770_uart4_groups), }, { "mmc0", jz4770_mmc0_groups, ARRAY_SIZE(jz4770_mmc0_groups), }, { "mmc1", jz4770_mmc1_groups, ARRAY_SIZE(jz4770_mmc1_groups), }, + { "mmc2", jz4770_mmc2_groups, ARRAY_SIZE(jz4770_mmc2_groups), }, { "nemc", jz4770_nemc_groups, ARRAY_SIZE(jz4770_nemc_groups), }, { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), }, + { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), }, + { "nemc-cs3", jz4770_cs3_groups, ARRAY_SIZE(jz4770_cs3_groups), }, + { "nemc-cs4", jz4770_cs4_groups, ARRAY_SIZE(jz4770_cs4_groups), }, + { "nemc-cs5", jz4770_cs5_groups, ARRAY_SIZE(jz4770_cs5_groups), }, { "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), }, { "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), }, { "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), }, { "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), }, - { "i2c3", jz4770_i2c3_groups, ARRAY_SIZE(jz4770_i2c3_groups), }, - { "i2c4", jz4770_i2c4_groups, ARRAY_SIZE(jz4770_i2c4_groups), }, { "cim", jz4770_cim_groups, ARRAY_SIZE(jz4770_cim_groups), }, { "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), }, { "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), }, @@ -530,6 +599,7 @@ static const struct function_desc jz4770_functions[] = { { "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), }, { "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), }, { "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), }, + { "mac", jz4770_mac_groups, ARRAY_SIZE(jz4770_mac_groups), }, }; static const struct ingenic_chip_info jz4770_chip_info = { @@ -542,7 +612,140 @@ static const struct ingenic_chip_info jz4770_chip_info = { .pull_downs = jz4770_pull_downs, }; -static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) +static int jz4780_uart2_data_pins[] = { 0x66, 0x67, }; +static int jz4780_uart2_hwflow_pins[] = { 0x65, 0x64, }; +static int jz4780_uart4_data_pins[] = { 0x54, 0x4a, }; +static int jz4780_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, }; +static int jz4780_i2c3_pins[] = { 0x6a, 0x6b, }; +static int jz4780_i2c4_e_pins[] = { 0x8c, 0x8d, }; +static int jz4780_i2c4_f_pins[] = { 0xb9, 0xb8, }; + +static int jz4780_uart2_data_funcs[] = { 1, 1, }; +static int jz4780_uart2_hwflow_funcs[] = { 1, 1, }; +static int jz4780_uart4_data_funcs[] = { 2, 2, }; +static int jz4780_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, }; +static int jz4780_i2c3_funcs[] = { 1, 1, }; +static int jz4780_i2c4_e_funcs[] = { 1, 1, }; +static int jz4780_i2c4_f_funcs[] = { 1, 1, }; + +static const struct group_desc jz4780_groups[] = { + INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data), + INGENIC_PIN_GROUP("uart0-hwflow", jz4770_uart0_hwflow), + INGENIC_PIN_GROUP("uart1-data", jz4770_uart1_data), + INGENIC_PIN_GROUP("uart1-hwflow", jz4770_uart1_hwflow), + INGENIC_PIN_GROUP("uart2-data", jz4780_uart2_data), + INGENIC_PIN_GROUP("uart2-hwflow", jz4780_uart2_hwflow), + INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data), + INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow), + INGENIC_PIN_GROUP("uart4-data", jz4780_uart4_data), + INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a), + INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a), + INGENIC_PIN_GROUP("mmc0-8bit-a", jz4780_mmc0_8bit_a), + INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e), + INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e), + INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d), + INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d), + INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e), + INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), + INGENIC_PIN_GROUP("mmc2-1bit-b", jz4770_mmc2_1bit_b), + INGENIC_PIN_GROUP("mmc2-4bit-b", jz4770_mmc2_4bit_b), + INGENIC_PIN_GROUP("mmc2-1bit-e", jz4770_mmc2_1bit_e), + INGENIC_PIN_GROUP("mmc2-4bit-e", jz4770_mmc2_4bit_e), + INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_8bit_data), + INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale), + INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr), + INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we), + INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe), + INGENIC_PIN_GROUP("nemc-wait", jz4770_nemc_wait), + INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1), + INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2), + INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3), + INGENIC_PIN_GROUP("nemc-cs4", jz4770_nemc_cs4), + INGENIC_PIN_GROUP("nemc-cs5", jz4770_nemc_cs5), + INGENIC_PIN_GROUP("nemc-cs6", jz4770_nemc_cs6), + INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0), + INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1), + INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2), + INGENIC_PIN_GROUP("i2c3-data", jz4780_i2c3), + INGENIC_PIN_GROUP("i2c4-data-e", jz4780_i2c4_e), + INGENIC_PIN_GROUP("i2c4-data-f", jz4780_i2c4_f), + INGENIC_PIN_GROUP("cim-data", jz4770_cim_8bit), + INGENIC_PIN_GROUP("lcd-24bit", jz4770_lcd_24bit), + { "lcd-no-pins", }, + INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0), + INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1), + INGENIC_PIN_GROUP("pwm2", jz4770_pwm_pwm2), + INGENIC_PIN_GROUP("pwm3", jz4770_pwm_pwm3), + INGENIC_PIN_GROUP("pwm4", jz4770_pwm_pwm4), + INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5), + INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6), + INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7), +}; + +static const char *jz4780_uart2_groups[] = { "uart2-data", "uart2-hwflow", }; +static const char *jz4780_uart4_groups[] = { "uart4-data", }; +static const char *jz4780_mmc0_groups[] = { + "mmc0-1bit-a", "mmc0-4bit-a", "mmc0-8bit-a", + "mmc0-1bit-e", "mmc0-4bit-e", +}; +static const char *jz4780_mmc1_groups[] = { + "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e", +}; +static const char *jz4780_mmc2_groups[] = { + "mmc2-1bit-b", "mmc2-4bit-b", "mmc2-1bit-e", "mmc2-4bit-e", +}; +static const char *jz4780_nemc_groups[] = { + "nemc-data", "nemc-cle-ale", "nemc-addr", + "nemc-rd-we", "nemc-frd-fwe", "nemc-wait", +}; +static const char *jz4780_i2c3_groups[] = { "i2c3-data", }; +static const char *jz4780_i2c4_groups[] = { "i2c4-data-e", "i2c4-data-f", }; +static const char *jz4780_cim_groups[] = { "cim-data", }; + +static const struct function_desc jz4780_functions[] = { + { "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), }, + { "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), }, + { "uart2", jz4780_uart2_groups, ARRAY_SIZE(jz4780_uart2_groups), }, + { "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), }, + { "uart4", jz4780_uart4_groups, ARRAY_SIZE(jz4780_uart4_groups), }, + { "mmc0", jz4780_mmc0_groups, ARRAY_SIZE(jz4780_mmc0_groups), }, + { "mmc1", jz4780_mmc1_groups, ARRAY_SIZE(jz4780_mmc1_groups), }, + { "mmc2", jz4780_mmc2_groups, ARRAY_SIZE(jz4780_mmc2_groups), }, + { "nemc", jz4780_nemc_groups, ARRAY_SIZE(jz4780_nemc_groups), }, + { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), }, + { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), }, + { "nemc-cs3", jz4770_cs3_groups, ARRAY_SIZE(jz4770_cs3_groups), }, + { "nemc-cs4", jz4770_cs4_groups, ARRAY_SIZE(jz4770_cs4_groups), }, + { "nemc-cs5", jz4770_cs5_groups, ARRAY_SIZE(jz4770_cs5_groups), }, + { "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), }, + { "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), }, + { "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), }, + { "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), }, + { "i2c3", jz4780_i2c3_groups, ARRAY_SIZE(jz4780_i2c3_groups), }, + { "i2c4", jz4780_i2c4_groups, ARRAY_SIZE(jz4780_i2c4_groups), }, + { "cim", jz4780_cim_groups, ARRAY_SIZE(jz4780_cim_groups), }, + { "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), }, + { "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), }, + { "pwm1", jz4770_pwm1_groups, ARRAY_SIZE(jz4770_pwm1_groups), }, + { "pwm2", jz4770_pwm2_groups, ARRAY_SIZE(jz4770_pwm2_groups), }, + { "pwm3", jz4770_pwm3_groups, ARRAY_SIZE(jz4770_pwm3_groups), }, + { "pwm4", jz4770_pwm4_groups, ARRAY_SIZE(jz4770_pwm4_groups), }, + { "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), }, + { "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), }, + { "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), }, +}; + +static const struct ingenic_chip_info jz4780_chip_info = { + .num_chips = 6, + .groups = jz4780_groups, + .num_groups = ARRAY_SIZE(jz4780_groups), + .functions = jz4780_functions, + .num_functions = ARRAY_SIZE(jz4780_functions), + .pull_ups = jz4770_pull_ups, + .pull_downs = jz4770_pull_downs, +}; + +static u32 ingenic_gpio_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) { unsigned int val; @@ -551,7 +754,7 @@ static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) return (u32) val; } -static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc, +static void ingenic_gpio_set_bit(struct ingenic_gpio_chip *jzgc, u8 reg, u8 offset, bool set) { if (set) @@ -565,7 +768,7 @@ static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc, static inline bool ingenic_gpio_get_value(struct ingenic_gpio_chip *jzgc, u8 offset) { - unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN); + unsigned int val = ingenic_gpio_read_reg(jzgc, GPIO_PIN); return !!(val & BIT(offset)); } @@ -574,9 +777,9 @@ static void ingenic_gpio_set_value(struct ingenic_gpio_chip *jzgc, u8 offset, int value) { if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value); } static void irq_set_type(struct ingenic_gpio_chip *jzgc, @@ -594,21 +797,21 @@ static void irq_set_type(struct ingenic_gpio_chip *jzgc, switch (type) { case IRQ_TYPE_EDGE_RISING: - gpio_ingenic_set_bit(jzgc, reg2, offset, true); - gpio_ingenic_set_bit(jzgc, reg1, offset, true); + ingenic_gpio_set_bit(jzgc, reg2, offset, true); + ingenic_gpio_set_bit(jzgc, reg1, offset, true); break; case IRQ_TYPE_EDGE_FALLING: - gpio_ingenic_set_bit(jzgc, reg2, offset, false); - gpio_ingenic_set_bit(jzgc, reg1, offset, true); + ingenic_gpio_set_bit(jzgc, reg2, offset, false); + ingenic_gpio_set_bit(jzgc, reg1, offset, true); break; case IRQ_TYPE_LEVEL_HIGH: - gpio_ingenic_set_bit(jzgc, reg2, offset, true); - gpio_ingenic_set_bit(jzgc, reg1, offset, false); + ingenic_gpio_set_bit(jzgc, reg2, offset, true); + ingenic_gpio_set_bit(jzgc, reg1, offset, false); break; case IRQ_TYPE_LEVEL_LOW: default: - gpio_ingenic_set_bit(jzgc, reg2, offset, false); - gpio_ingenic_set_bit(jzgc, reg1, offset, false); + ingenic_gpio_set_bit(jzgc, reg2, offset, false); + ingenic_gpio_set_bit(jzgc, reg1, offset, false); break; } } @@ -618,7 +821,7 @@ static void ingenic_gpio_irq_mask(struct irq_data *irqd) struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true); + ingenic_gpio_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true); } static void ingenic_gpio_irq_unmask(struct irq_data *irqd) @@ -626,7 +829,7 @@ static void ingenic_gpio_irq_unmask(struct irq_data *irqd) struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false); + ingenic_gpio_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false); } static void ingenic_gpio_irq_enable(struct irq_data *irqd) @@ -636,9 +839,9 @@ static void ingenic_gpio_irq_enable(struct irq_data *irqd) int irq = irqd->hwirq; if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true); ingenic_gpio_irq_unmask(irqd); } @@ -652,9 +855,9 @@ static void ingenic_gpio_irq_disable(struct irq_data *irqd) ingenic_gpio_irq_mask(irqd); if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, false); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); } static void ingenic_gpio_irq_ack(struct irq_data *irqd) @@ -677,9 +880,9 @@ static void ingenic_gpio_irq_ack(struct irq_data *irqd) } if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true); } static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) @@ -734,9 +937,9 @@ static void ingenic_gpio_irq_handler(struct irq_desc *desc) chained_irq_enter(irq_chip, desc); if (jzgc->jzpc->version >= ID_JZ4770) - flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG); + flag = ingenic_gpio_read_reg(jzgc, JZ4770_GPIO_FLAG); else - flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG); + flag = ingenic_gpio_read_reg(jzgc, JZ4740_GPIO_FLAG); for_each_set_bit(i, &flag, 32) generic_handle_irq(irq_linear_revmap(gc->irq.domain, i)); @@ -1185,7 +1388,9 @@ static int __init ingenic_pinctrl_probe(struct platform_device *pdev) else jzpc->version = (enum jz_version)id->driver_data; - if (jzpc->version >= ID_JZ4770) + if (jzpc->version >= ID_JZ4780) + chip_info = &jz4780_chip_info; + else if (jzpc->version >= ID_JZ4770) chip_info = &jz4770_chip_info; else if (jzpc->version >= ID_JZ4725B) chip_info = &jz4725b_chip_info; diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c index 98905d4a79ca..5d7a8514def9 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08.c +++ b/drivers/pinctrl/pinctrl-mcp23s08.c @@ -68,6 +68,7 @@ struct mcp23s08 { struct mutex lock; struct gpio_chip chip; + struct irq_chip irq_chip; struct regmap *regmap; struct device *dev; @@ -607,15 +608,6 @@ static void mcp23s08_irq_bus_unlock(struct irq_data *data) mutex_unlock(&mcp->lock); } -static struct irq_chip mcp23s08_irq_chip = { - .name = "gpio-mcp23xxx", - .irq_mask = mcp23s08_irq_mask, - .irq_unmask = mcp23s08_irq_unmask, - .irq_set_type = mcp23s08_irq_set_type, - .irq_bus_lock = mcp23s08_irq_bus_lock, - .irq_bus_sync_unlock = mcp23s08_irq_bus_unlock, -}; - static int mcp23s08_irq_setup(struct mcp23s08 *mcp) { struct gpio_chip *chip = &mcp->chip; @@ -645,7 +637,7 @@ static int mcp23s08_irqchip_setup(struct mcp23s08 *mcp) int err; err = gpiochip_irqchip_add_nested(chip, - &mcp23s08_irq_chip, + &mcp->irq_chip, 0, handle_simple_irq, IRQ_TYPE_NONE); @@ -656,7 +648,7 @@ static int mcp23s08_irqchip_setup(struct mcp23s08 *mcp) } gpiochip_set_nested_irqchip(chip, - &mcp23s08_irq_chip, + &mcp->irq_chip, mcp->irq); return 0; @@ -1047,6 +1039,13 @@ static int mcp230xx_probe(struct i2c_client *client, return -ENOMEM; mcp->irq = client->irq; + mcp->irq_chip.name = dev_name(&client->dev); + mcp->irq_chip.irq_mask = mcp23s08_irq_mask; + mcp->irq_chip.irq_unmask = mcp23s08_irq_unmask; + mcp->irq_chip.irq_set_type = mcp23s08_irq_set_type; + mcp->irq_chip.irq_bus_lock = mcp23s08_irq_bus_lock; + mcp->irq_chip.irq_bus_sync_unlock = mcp23s08_irq_bus_unlock; + status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, id->driver_data, pdata->base, 0); if (status) @@ -1144,8 +1143,7 @@ static int mcp23s08_probe(struct spi_device *spi) return -ENODEV; data = devm_kzalloc(&spi->dev, - sizeof(*data) + chips * sizeof(struct mcp23s08), - GFP_KERNEL); + struct_size(data, chip, chips), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1157,6 +1155,13 @@ static int mcp23s08_probe(struct spi_device *spi) chips--; data->mcp[addr] = &data->chip[chips]; data->mcp[addr]->irq = spi->irq; + data->mcp[addr]->irq_chip.name = dev_name(&spi->dev); + data->mcp[addr]->irq_chip.irq_mask = mcp23s08_irq_mask; + data->mcp[addr]->irq_chip.irq_unmask = mcp23s08_irq_unmask; + data->mcp[addr]->irq_chip.irq_set_type = mcp23s08_irq_set_type; + data->mcp[addr]->irq_chip.irq_bus_lock = mcp23s08_irq_bus_lock; + data->mcp[addr]->irq_chip.irq_bus_sync_unlock = + mcp23s08_irq_bus_unlock; status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, 0x40 | (addr << 1), type, pdata->base, addr); diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c index 4ffd56ff809e..1c6ba978c69f 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs404.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c @@ -95,31 +95,6 @@ enum { .intr_detection_width = -1, \ } -#define UFS_RESET(pg_name, offset) \ - { \ - .name = #pg_name, \ - .pins = pg_name##_pins, \ - .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ - .ctl_reg = offset, \ - .io_reg = offset + 0x4, \ - .intr_cfg_reg = 0, \ - .intr_status_reg = 0, \ - .intr_target_reg = 0, \ - .tile = NORTH, \ - .mux_bit = -1, \ - .pull_bit = 3, \ - .drv_bit = 0, \ - .oe_bit = -1, \ - .in_bit = -1, \ - .out_bit = 0, \ - .intr_enable_bit = -1, \ - .intr_status_bit = -1, \ - .intr_target_bit = -1, \ - .intr_raw_status_bit = -1, \ - .intr_polarity_bit = -1, \ - .intr_detection_bit = -1, \ - .intr_detection_width = -1, \ - } static const struct pinctrl_pin_desc qcs404_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index cb512c7a5251..76e57ae2f6e8 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -675,11 +675,11 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, else seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in"); + seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); seq_printf(s, " %-7s", pmic_gpio_functions[function]); seq_printf(s, " vin-%d", pad->power_source); seq_printf(s, " %-27s", biases[pad->pullup]); seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); - seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); seq_printf(s, " %-7s", strengths[pad->strength]); seq_printf(s, " atest-%d", pad->atest); seq_printf(s, " dtest-%d", pad->dtest_buffer); diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index f49ea3d92aa1..ebc27b06718c 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -325,13 +325,6 @@ err_domains: return ret; } -static u32 exynos_eint_wake_mask = 0xffffffff; - -u32 exynos_get_eint_wake_mask(void) -{ - return exynos_eint_wake_mask; -} - static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) { struct irq_chip *chip = irq_data_get_irq_chip(irqd); @@ -342,10 +335,9 @@ static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) pr_info("wake %s for irq %d\n", on ? "enabled" : "disabled", irqd->irq); if (!on) - exynos_eint_wake_mask |= bit; + our_chip->eint_wake_mask_value |= bit; else - exynos_eint_wake_mask &= ~bit; - our_chip->eint_wake_mask_value = exynos_eint_wake_mask; + our_chip->eint_wake_mask_value &= ~bit; return 0; } diff --git a/drivers/pinctrl/sh-pfc/pfc-emev2.c b/drivers/pinctrl/sh-pfc/pfc-emev2.c index dc271c3243df..310c6f3ee7cc 100644 --- a/drivers/pinctrl/sh-pfc/pfc-emev2.c +++ b/drivers/pinctrl/sh-pfc/pfc-emev2.c @@ -1260,6 +1260,14 @@ static const char * const dtv_groups[] = { "dtv_b", }; +static const char * const err_rst_reqb_groups[] = { + "err_rst_reqb", +}; + +static const char * const ext_clki_groups[] = { + "ext_clki", +}; + static const char * const iic0_groups[] = { "iic0", }; @@ -1282,6 +1290,10 @@ static const char * const lcd_groups[] = { "yuv3", }; +static const char * const lowpwr_groups[] = { + "lowpwr", +}; + static const char * const ntsc_groups[] = { "ntsc_clk", "ntsc_data", @@ -1295,6 +1307,10 @@ static const char * const pwm1_groups[] = { "pwm1", }; +static const char * const ref_clko_groups[] = { + "ref_clko", +}; + static const char * const sd_groups[] = { "sd_cki", }; @@ -1388,13 +1404,17 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(cam), SH_PFC_FUNCTION(cf), SH_PFC_FUNCTION(dtv), + SH_PFC_FUNCTION(err_rst_reqb), + SH_PFC_FUNCTION(ext_clki), SH_PFC_FUNCTION(iic0), SH_PFC_FUNCTION(iic1), SH_PFC_FUNCTION(jtag), SH_PFC_FUNCTION(lcd), + SH_PFC_FUNCTION(lowpwr), SH_PFC_FUNCTION(ntsc), SH_PFC_FUNCTION(pwm0), SH_PFC_FUNCTION(pwm1), + SH_PFC_FUNCTION(ref_clko), SH_PFC_FUNCTION(sd), SH_PFC_FUNCTION(sdi0), SH_PFC_FUNCTION(sdi1), diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c index 6bcdb4b5e69e..068b5e6334d1 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c @@ -1264,8 +1264,8 @@ static const struct sh_pfc_pin pinmux_pins[] = { /* Pins not associated with a GPIO port */ SH_PFC_PIN_NAMED(3, 20, C20), - SH_PFC_PIN_NAMED(20, 1, T1), - SH_PFC_PIN_NAMED(25, 2, Y2), + SH_PFC_PIN_NAMED(1, 20, A20), + SH_PFC_PIN_NAMED(2, 25, B25), }; /* - macro */ @@ -1400,7 +1400,7 @@ HSPI_PFC_DAT(hspi1_a, HSPI_CLK1_A, HSPI_CS1_A, HSPI_RX1_A, HSPI_TX1_A); HSPI_PFC_PIN(hspi1_b, RCAR_GP_PIN(0, 27), RCAR_GP_PIN(0, 26), - PIN_NUMBER(20, 1), PIN_NUMBER(25, 2)); + PIN_NUMBER(1, 20), PIN_NUMBER(2, 25)); HSPI_PFC_DAT(hspi1_b, HSPI_CLK1_B, HSPI_CS1_B, HSPI_RX1_B, HSPI_TX1_B); diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c index ab7a35392cd8..a84229cb8cd4 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c @@ -10,7 +10,9 @@ #include <linux/io.h> #include <linux/kernel.h> +#include <linux/sys_soc.h> +#include "core.h" #include "sh_pfc.h" /* @@ -5691,7 +5693,22 @@ static int r8a7790_pin_to_pocctrl(struct sh_pfc *pfc, unsigned int pin, u32 *poc return 31 - (pin & 0x1f); } +static const struct soc_device_attribute r8a7790_tdsel[] = { + { .soc_id = "r8a7790", .revision = "ES1.0" }, + { /* sentinel */ } +}; + +static int r8a7790_pinmux_soc_init(struct sh_pfc *pfc) +{ + /* Initialize TDSEL on old revisions */ + if (soc_device_match(r8a7790_tdsel)) + sh_pfc_write(pfc, 0xe6060088, 0x00155554); + + return 0; +} + static const struct sh_pfc_soc_operations r8a7790_pinmux_ops = { + .init = r8a7790_pinmux_soc_init, .pin_to_pocctrl = r8a7790_pin_to_pocctrl, }; diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c index 2859231aaffc..d8b13d4e9bbf 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c @@ -4317,7 +4317,7 @@ static const unsigned int vin1_clk_pins[] = { static const unsigned int vin1_clk_mux[] = { VI1_CLK_MARK, }; -static const union vin_data vin1_b_data_pins = { +static const union vin_data vin1_data_b_pins = { .data24 = { /* B */ RCAR_GP_PIN(3, 0), RCAR_GP_PIN(3, 1), @@ -4336,7 +4336,7 @@ static const union vin_data vin1_b_data_pins = { RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20), }, }; -static const union vin_data vin1_b_data_mux = { +static const union vin_data vin1_data_b_mux = { .data24 = { /* B */ VI1_DATA0_B_MARK, VI1_DATA1_B_MARK, @@ -4355,7 +4355,7 @@ static const union vin_data vin1_b_data_mux = { VI1_R6_B_MARK, VI1_R7_B_MARK, }, }; -static const unsigned int vin1_b_data18_pins[] = { +static const unsigned int vin1_data18_b_pins[] = { /* B */ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), @@ -4369,7 +4369,7 @@ static const unsigned int vin1_b_data18_pins[] = { RCAR_GP_PIN(2, 17), RCAR_GP_PIN(2, 18), RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20), }; -static const unsigned int vin1_b_data18_mux[] = { +static const unsigned int vin1_data18_b_mux[] = { /* B */ VI1_DATA2_B_MARK, VI1_DATA3_B_MARK, VI1_DATA4_B_MARK, VI1_DATA5_B_MARK, @@ -4383,30 +4383,30 @@ static const unsigned int vin1_b_data18_mux[] = { VI1_R4_B_MARK, VI1_R5_B_MARK, VI1_R6_B_MARK, VI1_R7_B_MARK, }; -static const unsigned int vin1_b_sync_pins[] = { +static const unsigned int vin1_sync_b_pins[] = { RCAR_GP_PIN(3, 17), /* HSYNC */ RCAR_GP_PIN(3, 18), /* VSYNC */ }; -static const unsigned int vin1_b_sync_mux[] = { +static const unsigned int vin1_sync_b_mux[] = { VI1_HSYNC_N_B_MARK, VI1_VSYNC_N_B_MARK, }; -static const unsigned int vin1_b_field_pins[] = { +static const unsigned int vin1_field_b_pins[] = { RCAR_GP_PIN(3, 20), }; -static const unsigned int vin1_b_field_mux[] = { +static const unsigned int vin1_field_b_mux[] = { VI1_FIELD_B_MARK, }; -static const unsigned int vin1_b_clkenb_pins[] = { +static const unsigned int vin1_clkenb_b_pins[] = { RCAR_GP_PIN(3, 19), }; -static const unsigned int vin1_b_clkenb_mux[] = { +static const unsigned int vin1_clkenb_b_mux[] = { VI1_CLKENB_B_MARK, }; -static const unsigned int vin1_b_clk_pins[] = { +static const unsigned int vin1_clk_b_pins[] = { RCAR_GP_PIN(3, 16), }; -static const unsigned int vin1_b_clk_mux[] = { +static const unsigned int vin1_clk_b_mux[] = { VI1_CLK_B_MARK, }; /* - VIN2 ----------------------------------------------------------------- */ @@ -4784,17 +4784,17 @@ static const struct { SH_PFC_PIN_GROUP(vin1_field), SH_PFC_PIN_GROUP(vin1_clkenb), SH_PFC_PIN_GROUP(vin1_clk), - VIN_DATA_PIN_GROUP(vin1_b_data, 24), - VIN_DATA_PIN_GROUP(vin1_b_data, 20), - SH_PFC_PIN_GROUP(vin1_b_data18), - VIN_DATA_PIN_GROUP(vin1_b_data, 16), - VIN_DATA_PIN_GROUP(vin1_b_data, 12), - VIN_DATA_PIN_GROUP(vin1_b_data, 10), - VIN_DATA_PIN_GROUP(vin1_b_data, 8), - SH_PFC_PIN_GROUP(vin1_b_sync), - SH_PFC_PIN_GROUP(vin1_b_field), - SH_PFC_PIN_GROUP(vin1_b_clkenb), - SH_PFC_PIN_GROUP(vin1_b_clk), + VIN_DATA_PIN_GROUP(vin1_data, 24, _b), + VIN_DATA_PIN_GROUP(vin1_data, 20, _b), + SH_PFC_PIN_GROUP(vin1_data18_b), + VIN_DATA_PIN_GROUP(vin1_data, 16, _b), + VIN_DATA_PIN_GROUP(vin1_data, 12, _b), + VIN_DATA_PIN_GROUP(vin1_data, 10, _b), + VIN_DATA_PIN_GROUP(vin1_data, 8, _b), + SH_PFC_PIN_GROUP(vin1_sync_b), + SH_PFC_PIN_GROUP(vin1_field_b), + SH_PFC_PIN_GROUP(vin1_clkenb_b), + SH_PFC_PIN_GROUP(vin1_clk_b), SH_PFC_PIN_GROUP(vin2_data8), SH_PFC_PIN_GROUP(vin2_sync), SH_PFC_PIN_GROUP(vin2_field), @@ -5236,7 +5236,7 @@ static const char * const scifb2_groups[] = { "scifb2_data_b", "scifb2_clk_b", "scifb2_ctrl_b", - "scifb0_data_c", + "scifb2_data_c", "scifb2_clk_c", "scifb2_data_d", }; @@ -5335,17 +5335,17 @@ static const char * const vin1_groups[] = { "vin1_field", "vin1_clkenb", "vin1_clk", - "vin1_b_data24", - "vin1_b_data20", - "vin1_b_data18", - "vin1_b_data16", - "vin1_b_data12", - "vin1_b_data10", - "vin1_b_data8", - "vin1_b_sync", - "vin1_b_field", - "vin1_b_clkenb", - "vin1_b_clk", + "vin1_data24_b", + "vin1_data20_b", + "vin1_data18_b", + "vin1_data16_b", + "vin1_data12_b", + "vin1_data10_b", + "vin1_data8_b", + "vin1_sync_b", + "vin1_field_b", + "vin1_clkenb_b", + "vin1_clk_b", }; static const char * const vin2_groups[] = { diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c index a623459b234e..d36da5652de6 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c @@ -1913,6 +1913,7 @@ static const char * const vin1_groups[] = { "vin1_data8", "vin1_data24_b", "vin1_data20_b", + "vin1_data18_b", "vin1_data16_b", "vin1_sync", "vin1_field", diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c index fcf1339c4058..958a5f714c93 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c @@ -8,6 +8,7 @@ */ #include <linux/kernel.h> +#include <linux/sys_soc.h> #include "core.h" #include "sh_pfc.h" @@ -5560,7 +5561,22 @@ static int r8a7794_pin_to_pocctrl(struct sh_pfc *pfc, unsigned int pin, u32 *poc return -EINVAL; } +static const struct soc_device_attribute r8a7794_tdsel[] = { + { .soc_id = "r8a7794", .revision = "ES1.0" }, + { /* sentinel */ } +}; + +static int r8a7794_pinmux_soc_init(struct sh_pfc *pfc) +{ + /* Initialize TDSEL on old revisions */ + if (soc_device_match(r8a7794_tdsel)) + sh_pfc_write(pfc, 0xe6060068, 0x55555500); + + return 0; +} + static const struct sh_pfc_soc_operations r8a7794_pinmux_ops = { + .init = r8a7794_pinmux_soc_init, .pin_to_pocctrl = r8a7794_pin_to_pocctrl, }; diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c index 01105bb83598..db9add1405c5 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c @@ -4098,67 +4098,29 @@ static const unsigned int vin4_clk_mux[] = { }; /* - VIN5 ------------------------------------------------------------------- */ -static const unsigned int vin5_data8_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), -}; -static const unsigned int vin5_data8_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, -}; -static const unsigned int vin5_data10_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), -}; -static const unsigned int vin5_data10_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, -}; -static const unsigned int vin5_data12_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), -}; -static const unsigned int vin5_data12_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, -}; -static const unsigned int vin5_data16_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), - RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), - RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), +static const union vin_data16 vin5_data_pins = { + .data16 = { + RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), + RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), + RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), + RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), + RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), + RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), + RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), + }, }; -static const unsigned int vin5_data16_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, - VI5_DATA12_MARK, VI5_DATA13_MARK, - VI5_DATA14_MARK, VI5_DATA15_MARK, +static const union vin_data16 vin5_data_mux = { + .data16 = { + VI5_DATA0_MARK, VI5_DATA1_MARK, + VI5_DATA2_MARK, VI5_DATA3_MARK, + VI5_DATA4_MARK, VI5_DATA5_MARK, + VI5_DATA6_MARK, VI5_DATA7_MARK, + VI5_DATA8_MARK, VI5_DATA9_MARK, + VI5_DATA10_MARK, VI5_DATA11_MARK, + VI5_DATA12_MARK, VI5_DATA13_MARK, + VI5_DATA14_MARK, VI5_DATA15_MARK, + }, }; static const unsigned int vin5_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -4530,10 +4492,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(vin4_field), SH_PFC_PIN_GROUP(vin4_clkenb), SH_PFC_PIN_GROUP(vin4_clk), - SH_PFC_PIN_GROUP(vin5_data8), - SH_PFC_PIN_GROUP(vin5_data10), - SH_PFC_PIN_GROUP(vin5_data12), - SH_PFC_PIN_GROUP(vin5_data16), + VIN_DATA_PIN_GROUP(vin5_data, 8), + VIN_DATA_PIN_GROUP(vin5_data, 10), + VIN_DATA_PIN_GROUP(vin5_data, 12), + VIN_DATA_PIN_GROUP(vin5_data, 16), SH_PFC_PIN_GROUP(vin5_sync), SH_PFC_PIN_GROUP(vin5_field), SH_PFC_PIN_GROUP(vin5_clkenb), diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c index 4b98ad8d93d9..72348a4f2ece 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c @@ -4070,67 +4070,29 @@ static const unsigned int vin4_clk_mux[] = { }; /* - VIN5 ------------------------------------------------------------------- */ -static const unsigned int vin5_data8_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), -}; -static const unsigned int vin5_data8_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, -}; -static const unsigned int vin5_data10_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), -}; -static const unsigned int vin5_data10_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, -}; -static const unsigned int vin5_data12_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), -}; -static const unsigned int vin5_data12_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, -}; -static const unsigned int vin5_data16_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), - RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), - RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), +static const union vin_data16 vin5_data_pins = { + .data16 = { + RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), + RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), + RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), + RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), + RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), + RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), + RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), + }, }; -static const unsigned int vin5_data16_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, - VI5_DATA12_MARK, VI5_DATA13_MARK, - VI5_DATA14_MARK, VI5_DATA15_MARK, +static const union vin_data16 vin5_data_mux = { + .data16 = { + VI5_DATA0_MARK, VI5_DATA1_MARK, + VI5_DATA2_MARK, VI5_DATA3_MARK, + VI5_DATA4_MARK, VI5_DATA5_MARK, + VI5_DATA6_MARK, VI5_DATA7_MARK, + VI5_DATA8_MARK, VI5_DATA9_MARK, + VI5_DATA10_MARK, VI5_DATA11_MARK, + VI5_DATA12_MARK, VI5_DATA13_MARK, + VI5_DATA14_MARK, VI5_DATA15_MARK, + }, }; static const unsigned int vin5_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -4468,10 +4430,10 @@ static const struct { SH_PFC_PIN_GROUP(vin4_field), SH_PFC_PIN_GROUP(vin4_clkenb), SH_PFC_PIN_GROUP(vin4_clk), - SH_PFC_PIN_GROUP(vin5_data8), - SH_PFC_PIN_GROUP(vin5_data10), - SH_PFC_PIN_GROUP(vin5_data12), - SH_PFC_PIN_GROUP(vin5_data16), + VIN_DATA_PIN_GROUP(vin5_data, 8), + VIN_DATA_PIN_GROUP(vin5_data, 10), + VIN_DATA_PIN_GROUP(vin5_data, 12), + VIN_DATA_PIN_GROUP(vin5_data, 16), SH_PFC_PIN_GROUP(vin5_sync), SH_PFC_PIN_GROUP(vin5_field), SH_PFC_PIN_GROUP(vin5_clkenb), diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c index a7c4882de09e..14c4b671cddf 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c @@ -554,7 +554,7 @@ MOD_SEL0_4_3 MOD_SEL1_4 \ FM(AVB_RX_CTL) FM(AVB_RXC) FM(AVB_RD0) FM(AVB_RD1) FM(AVB_RD2) FM(AVB_RD3) \ FM(AVB_TXCREFCLK) FM(AVB_MDIO) \ FM(PRESETOUT) \ - FM(DU_DOTCLKIN0) FM(DU_DOTCLKIN1) FM(DU_DOTCLKIN2) \ + FM(DU_DOTCLKIN0) FM(DU_DOTCLKIN1) FM(DU_DOTCLKIN3) \ FM(TMS) FM(TDO) FM(ASEBRK) FM(MLB_REF) FM(TDI) FM(TCK) FM(TRST) FM(EXTALR) enum { @@ -1566,7 +1566,7 @@ static const struct sh_pfc_pin pinmux_pins[] = { SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('E'), 5, QSPI1_MISO_IO1, CFG_FLAGS), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 7, DU_DOTCLKIN0, CFG_FLAGS), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 8, DU_DOTCLKIN1, CFG_FLAGS), - SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 8, DU_DOTCLKIN2, CFG_FLAGS), + SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 8, DU_DOTCLKIN3, CFG_FLAGS), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 26, TRST#, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 29, TDI, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 30, TMS, CFG_FLAGS), @@ -1850,6 +1850,280 @@ static const unsigned int canfd1_data_mux[] = { CANFD1_TX_MARK, CANFD1_RX_MARK, }; +/* - DRIF0 --------------------------------------------------------------- */ +static const unsigned int drif0_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9), +}; + +static const unsigned int drif0_ctrl_a_mux[] = { + RIF0_CLK_A_MARK, RIF0_SYNC_A_MARK, +}; + +static const unsigned int drif0_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 10), +}; + +static const unsigned int drif0_data0_a_mux[] = { + RIF0_D0_A_MARK, +}; + +static const unsigned int drif0_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 7), +}; + +static const unsigned int drif0_data1_a_mux[] = { + RIF0_D1_A_MARK, +}; + +static const unsigned int drif0_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 0), RCAR_GP_PIN(5, 4), +}; + +static const unsigned int drif0_ctrl_b_mux[] = { + RIF0_CLK_B_MARK, RIF0_SYNC_B_MARK, +}; + +static const unsigned int drif0_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 1), +}; + +static const unsigned int drif0_data0_b_mux[] = { + RIF0_D0_B_MARK, +}; + +static const unsigned int drif0_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 2), +}; + +static const unsigned int drif0_data1_b_mux[] = { + RIF0_D1_B_MARK, +}; + +static const unsigned int drif0_ctrl_c_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 12), RCAR_GP_PIN(5, 15), +}; + +static const unsigned int drif0_ctrl_c_mux[] = { + RIF0_CLK_C_MARK, RIF0_SYNC_C_MARK, +}; + +static const unsigned int drif0_data0_c_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 13), +}; + +static const unsigned int drif0_data0_c_mux[] = { + RIF0_D0_C_MARK, +}; + +static const unsigned int drif0_data1_c_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 14), +}; + +static const unsigned int drif0_data1_c_mux[] = { + RIF0_D1_C_MARK, +}; + +/* - DRIF1 --------------------------------------------------------------- */ +static const unsigned int drif1_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18), +}; + +static const unsigned int drif1_ctrl_a_mux[] = { + RIF1_CLK_A_MARK, RIF1_SYNC_A_MARK, +}; + +static const unsigned int drif1_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 19), +}; + +static const unsigned int drif1_data0_a_mux[] = { + RIF1_D0_A_MARK, +}; + +static const unsigned int drif1_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 20), +}; + +static const unsigned int drif1_data1_a_mux[] = { + RIF1_D1_A_MARK, +}; + +static const unsigned int drif1_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 9), RCAR_GP_PIN(5, 3), +}; + +static const unsigned int drif1_ctrl_b_mux[] = { + RIF1_CLK_B_MARK, RIF1_SYNC_B_MARK, +}; + +static const unsigned int drif1_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 7), +}; + +static const unsigned int drif1_data0_b_mux[] = { + RIF1_D0_B_MARK, +}; + +static const unsigned int drif1_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 8), +}; + +static const unsigned int drif1_data1_b_mux[] = { + RIF1_D1_B_MARK, +}; + +static const unsigned int drif1_ctrl_c_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 11), +}; + +static const unsigned int drif1_ctrl_c_mux[] = { + RIF1_CLK_C_MARK, RIF1_SYNC_C_MARK, +}; + +static const unsigned int drif1_data0_c_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 6), +}; + +static const unsigned int drif1_data0_c_mux[] = { + RIF1_D0_C_MARK, +}; + +static const unsigned int drif1_data1_c_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 10), +}; + +static const unsigned int drif1_data1_c_mux[] = { + RIF1_D1_C_MARK, +}; + +/* - DRIF2 --------------------------------------------------------------- */ +static const unsigned int drif2_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9), +}; + +static const unsigned int drif2_ctrl_a_mux[] = { + RIF2_CLK_A_MARK, RIF2_SYNC_A_MARK, +}; + +static const unsigned int drif2_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 7), +}; + +static const unsigned int drif2_data0_a_mux[] = { + RIF2_D0_A_MARK, +}; + +static const unsigned int drif2_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 10), +}; + +static const unsigned int drif2_data1_a_mux[] = { + RIF2_D1_A_MARK, +}; + +static const unsigned int drif2_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 26), RCAR_GP_PIN(6, 27), +}; + +static const unsigned int drif2_ctrl_b_mux[] = { + RIF2_CLK_B_MARK, RIF2_SYNC_B_MARK, +}; + +static const unsigned int drif2_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 30), +}; + +static const unsigned int drif2_data0_b_mux[] = { + RIF2_D0_B_MARK, +}; + +static const unsigned int drif2_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 31), +}; + +static const unsigned int drif2_data1_b_mux[] = { + RIF2_D1_B_MARK, +}; + +/* - DRIF3 --------------------------------------------------------------- */ +static const unsigned int drif3_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18), +}; + +static const unsigned int drif3_ctrl_a_mux[] = { + RIF3_CLK_A_MARK, RIF3_SYNC_A_MARK, +}; + +static const unsigned int drif3_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 19), +}; + +static const unsigned int drif3_data0_a_mux[] = { + RIF3_D0_A_MARK, +}; + +static const unsigned int drif3_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 20), +}; + +static const unsigned int drif3_data1_a_mux[] = { + RIF3_D1_A_MARK, +}; + +static const unsigned int drif3_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 24), RCAR_GP_PIN(6, 25), +}; + +static const unsigned int drif3_ctrl_b_mux[] = { + RIF3_CLK_B_MARK, RIF3_SYNC_B_MARK, +}; + +static const unsigned int drif3_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 28), +}; + +static const unsigned int drif3_data0_b_mux[] = { + RIF3_D0_B_MARK, +}; + +static const unsigned int drif3_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 29), +}; + +static const unsigned int drif3_data1_b_mux[] = { + RIF3_D1_B_MARK, +}; + /* - DU --------------------------------------------------------------------- */ static const unsigned int du_rgb666_pins[] = { /* R[7:2], G[7:2], B[7:2] */ @@ -3760,6 +4034,42 @@ static const unsigned int ssi9_ctrl_b_mux[] = { SSI_SCK9_B_MARK, SSI_WS9_B_MARK, }; +/* - TMU -------------------------------------------------------------------- */ +static const unsigned int tmu_tclk1_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(6, 23), +}; + +static const unsigned int tmu_tclk1_a_mux[] = { + TCLK1_A_MARK, +}; + +static const unsigned int tmu_tclk1_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(5, 19), +}; + +static const unsigned int tmu_tclk1_b_mux[] = { + TCLK1_B_MARK, +}; + +static const unsigned int tmu_tclk2_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(6, 19), +}; + +static const unsigned int tmu_tclk2_a_mux[] = { + TCLK2_A_MARK, +}; + +static const unsigned int tmu_tclk2_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(6, 28), +}; + +static const unsigned int tmu_tclk2_b_mux[] = { + TCLK2_B_MARK, +}; /* - USB0 ------------------------------------------------------------------- */ static const unsigned int usb0_pins[] = { @@ -4037,6 +4347,36 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(canfd0_data_a), SH_PFC_PIN_GROUP(canfd0_data_b), SH_PFC_PIN_GROUP(canfd1_data), + SH_PFC_PIN_GROUP(drif0_ctrl_a), + SH_PFC_PIN_GROUP(drif0_data0_a), + SH_PFC_PIN_GROUP(drif0_data1_a), + SH_PFC_PIN_GROUP(drif0_ctrl_b), + SH_PFC_PIN_GROUP(drif0_data0_b), + SH_PFC_PIN_GROUP(drif0_data1_b), + SH_PFC_PIN_GROUP(drif0_ctrl_c), + SH_PFC_PIN_GROUP(drif0_data0_c), + SH_PFC_PIN_GROUP(drif0_data1_c), + SH_PFC_PIN_GROUP(drif1_ctrl_a), + SH_PFC_PIN_GROUP(drif1_data0_a), + SH_PFC_PIN_GROUP(drif1_data1_a), + SH_PFC_PIN_GROUP(drif1_ctrl_b), + SH_PFC_PIN_GROUP(drif1_data0_b), + SH_PFC_PIN_GROUP(drif1_data1_b), + SH_PFC_PIN_GROUP(drif1_ctrl_c), + SH_PFC_PIN_GROUP(drif1_data0_c), + SH_PFC_PIN_GROUP(drif1_data1_c), + SH_PFC_PIN_GROUP(drif2_ctrl_a), + SH_PFC_PIN_GROUP(drif2_data0_a), + SH_PFC_PIN_GROUP(drif2_data1_a), + SH_PFC_PIN_GROUP(drif2_ctrl_b), + SH_PFC_PIN_GROUP(drif2_data0_b), + SH_PFC_PIN_GROUP(drif2_data1_b), + SH_PFC_PIN_GROUP(drif3_ctrl_a), + SH_PFC_PIN_GROUP(drif3_data0_a), + SH_PFC_PIN_GROUP(drif3_data1_a), + SH_PFC_PIN_GROUP(drif3_ctrl_b), + SH_PFC_PIN_GROUP(drif3_data0_b), + SH_PFC_PIN_GROUP(drif3_data1_b), SH_PFC_PIN_GROUP(du_rgb666), SH_PFC_PIN_GROUP(du_rgb888), SH_PFC_PIN_GROUP(du_clk_out_0), @@ -4280,6 +4620,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(ssi9_data_b), SH_PFC_PIN_GROUP(ssi9_ctrl_a), SH_PFC_PIN_GROUP(ssi9_ctrl_b), + SH_PFC_PIN_GROUP(tmu_tclk1_a), + SH_PFC_PIN_GROUP(tmu_tclk1_b), + SH_PFC_PIN_GROUP(tmu_tclk2_a), + SH_PFC_PIN_GROUP(tmu_tclk2_b), SH_PFC_PIN_GROUP(usb0), SH_PFC_PIN_GROUP(usb1), SH_PFC_PIN_GROUP(usb30), @@ -4367,6 +4711,48 @@ static const char * const canfd1_groups[] = { "canfd1_data", }; +static const char * const drif0_groups[] = { + "drif0_ctrl_a", + "drif0_data0_a", + "drif0_data1_a", + "drif0_ctrl_b", + "drif0_data0_b", + "drif0_data1_b", + "drif0_ctrl_c", + "drif0_data0_c", + "drif0_data1_c", +}; + +static const char * const drif1_groups[] = { + "drif1_ctrl_a", + "drif1_data0_a", + "drif1_data1_a", + "drif1_ctrl_b", + "drif1_data0_b", + "drif1_data1_b", + "drif1_ctrl_c", + "drif1_data0_c", + "drif1_data1_c", +}; + +static const char * const drif2_groups[] = { + "drif2_ctrl_a", + "drif2_data0_a", + "drif2_data1_a", + "drif2_ctrl_b", + "drif2_data0_b", + "drif2_data1_b", +}; + +static const char * const drif3_groups[] = { + "drif3_ctrl_a", + "drif3_data0_a", + "drif3_data1_a", + "drif3_ctrl_b", + "drif3_data0_b", + "drif3_data1_b", +}; + static const char * const du_groups[] = { "du_rgb666", "du_rgb888", @@ -4711,6 +5097,13 @@ static const char * const ssi_groups[] = { "ssi9_ctrl_b", }; +static const char * const tmu_groups[] = { + "tmu_tclk1_a", + "tmu_tclk1_b", + "tmu_tclk2_a", + "tmu_tclk2_b", +}; + static const char * const usb0_groups[] = { "usb0", }; @@ -4763,6 +5156,10 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(can_clk), SH_PFC_FUNCTION(canfd0), SH_PFC_FUNCTION(canfd1), + SH_PFC_FUNCTION(drif0), + SH_PFC_FUNCTION(drif1), + SH_PFC_FUNCTION(drif2), + SH_PFC_FUNCTION(drif3), SH_PFC_FUNCTION(du), SH_PFC_FUNCTION(hscif0), SH_PFC_FUNCTION(hscif1), @@ -4797,6 +5194,7 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(sdhi2), SH_PFC_FUNCTION(sdhi3), SH_PFC_FUNCTION(ssi), + SH_PFC_FUNCTION(tmu), SH_PFC_FUNCTION(usb0), SH_PFC_FUNCTION(usb1), SH_PFC_FUNCTION(usb30), @@ -5740,7 +6138,7 @@ static const struct pinmux_bias_reg pinmux_bias_regs[] = { [31] = PIN_A_NUMBER('P', 8), /* DU_DOTCLKIN1 */ } }, { PINMUX_BIAS_REG("PUEN3", 0xe606040c, "PUD3", 0xe606044c) { - [ 0] = PIN_A_NUMBER('R', 8), /* DU_DOTCLKIN2 */ + [ 0] = PIN_A_NUMBER('R', 8), /* DU_DOTCLKIN3 */ [ 1] = PIN_NONE, [ 2] = PIN_A_NUMBER('D', 38), /* FSCLKST */ [ 3] = PIN_A_NUMBER('D', 39), /* EXTALR*/ diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c index 4a7ab84b366b..c5e67ba29f7c 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c @@ -1578,47 +1578,25 @@ static const unsigned int tmu_tclk2_b_mux[] = { }; /* - VIN0 ------------------------------------------------------------------- */ -static const unsigned int vin0_data8_pins[] = { - RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), - RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), - RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), - RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), -}; -static const unsigned int vin0_data8_mux[] = { - VI0_DATA0_MARK, VI0_DATA1_MARK, - VI0_DATA2_MARK, VI0_DATA3_MARK, - VI0_DATA4_MARK, VI0_DATA5_MARK, - VI0_DATA6_MARK, VI0_DATA7_MARK, +static const union vin_data12 vin0_data_pins = { + .data12 = { + RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), + RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), + RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), + RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), + RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13), + RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15), + }, }; -static const unsigned int vin0_data10_pins[] = { - RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), - RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), - RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), - RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), - RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13), -}; -static const unsigned int vin0_data10_mux[] = { - VI0_DATA0_MARK, VI0_DATA1_MARK, - VI0_DATA2_MARK, VI0_DATA3_MARK, - VI0_DATA4_MARK, VI0_DATA5_MARK, - VI0_DATA6_MARK, VI0_DATA7_MARK, - VI0_DATA8_MARK, VI0_DATA9_MARK, -}; -static const unsigned int vin0_data12_pins[] = { - RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), - RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), - RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), - RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), - RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13), - RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15), -}; -static const unsigned int vin0_data12_mux[] = { - VI0_DATA0_MARK, VI0_DATA1_MARK, - VI0_DATA2_MARK, VI0_DATA3_MARK, - VI0_DATA4_MARK, VI0_DATA5_MARK, - VI0_DATA6_MARK, VI0_DATA7_MARK, - VI0_DATA8_MARK, VI0_DATA9_MARK, - VI0_DATA10_MARK, VI0_DATA11_MARK, +static const union vin_data12 vin0_data_mux = { + .data12 = { + VI0_DATA0_MARK, VI0_DATA1_MARK, + VI0_DATA2_MARK, VI0_DATA3_MARK, + VI0_DATA4_MARK, VI0_DATA5_MARK, + VI0_DATA6_MARK, VI0_DATA7_MARK, + VI0_DATA8_MARK, VI0_DATA9_MARK, + VI0_DATA10_MARK, VI0_DATA11_MARK, + }, }; static const unsigned int vin0_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -1650,47 +1628,25 @@ static const unsigned int vin0_clk_mux[] = { }; /* - VIN1 ------------------------------------------------------------------- */ -static const unsigned int vin1_data8_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), -}; -static const unsigned int vin1_data8_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, -}; -static const unsigned int vin1_data10_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), -}; -static const unsigned int vin1_data10_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, -}; -static const unsigned int vin1_data12_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), - RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), +static const union vin_data12 vin1_data_pins = { + .data12 = { + RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), + RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), + RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), + RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), + RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), + }, }; -static const unsigned int vin1_data12_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, - VI1_DATA10_MARK, VI1_DATA11_MARK, +static const union vin_data12 vin1_data_mux = { + .data12 = { + VI1_DATA0_MARK, VI1_DATA1_MARK, + VI1_DATA2_MARK, VI1_DATA3_MARK, + VI1_DATA4_MARK, VI1_DATA5_MARK, + VI1_DATA6_MARK, VI1_DATA7_MARK, + VI1_DATA8_MARK, VI1_DATA9_MARK, + VI1_DATA10_MARK, VI1_DATA11_MARK, + }, }; static const unsigned int vin1_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -1831,16 +1787,16 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(tmu_tclk1_b), SH_PFC_PIN_GROUP(tmu_tclk2_a), SH_PFC_PIN_GROUP(tmu_tclk2_b), - SH_PFC_PIN_GROUP(vin0_data8), - SH_PFC_PIN_GROUP(vin0_data10), - SH_PFC_PIN_GROUP(vin0_data12), + VIN_DATA_PIN_GROUP(vin0_data, 8), + VIN_DATA_PIN_GROUP(vin0_data, 10), + VIN_DATA_PIN_GROUP(vin0_data, 12), SH_PFC_PIN_GROUP(vin0_sync), SH_PFC_PIN_GROUP(vin0_field), SH_PFC_PIN_GROUP(vin0_clkenb), SH_PFC_PIN_GROUP(vin0_clk), - SH_PFC_PIN_GROUP(vin1_data8), - SH_PFC_PIN_GROUP(vin1_data10), - SH_PFC_PIN_GROUP(vin1_data12), + VIN_DATA_PIN_GROUP(vin1_data, 8), + VIN_DATA_PIN_GROUP(vin1_data, 10), + VIN_DATA_PIN_GROUP(vin1_data, 12), SH_PFC_PIN_GROUP(vin1_sync), SH_PFC_PIN_GROUP(vin1_field), SH_PFC_PIN_GROUP(vin1_clkenb), diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c index 8bef24502f0c..b807b67ae143 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c @@ -1970,47 +1970,25 @@ static const unsigned int vin0_clk_mux[] = { }; /* - VIN1 ------------------------------------------------------------------- */ -static const unsigned int vin1_data8_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), -}; -static const unsigned int vin1_data8_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, -}; -static const unsigned int vin1_data10_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), -}; -static const unsigned int vin1_data10_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, -}; -static const unsigned int vin1_data12_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), - RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), +static const union vin_data12 vin1_data_pins = { + .data12 = { + RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), + RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), + RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), + RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), + RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), + }, }; -static const unsigned int vin1_data12_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, - VI1_DATA10_MARK, VI1_DATA11_MARK, +static const union vin_data12 vin1_data_mux = { + .data12 = { + VI1_DATA0_MARK, VI1_DATA1_MARK, + VI1_DATA2_MARK, VI1_DATA3_MARK, + VI1_DATA4_MARK, VI1_DATA5_MARK, + VI1_DATA6_MARK, VI1_DATA7_MARK, + VI1_DATA8_MARK, VI1_DATA9_MARK, + VI1_DATA10_MARK, VI1_DATA11_MARK, + }, }; static const unsigned int vin1_sync_pins[] = { /* VI1_VSYNC#, VI1_HSYNC# */ @@ -2182,9 +2160,9 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(vin0_field), SH_PFC_PIN_GROUP(vin0_clkenb), SH_PFC_PIN_GROUP(vin0_clk), - SH_PFC_PIN_GROUP(vin1_data8), - SH_PFC_PIN_GROUP(vin1_data10), - SH_PFC_PIN_GROUP(vin1_data12), + VIN_DATA_PIN_GROUP(vin1_data, 8), + VIN_DATA_PIN_GROUP(vin1_data, 10), + VIN_DATA_PIN_GROUP(vin1_data, 12), SH_PFC_PIN_GROUP(vin1_sync), SH_PFC_PIN_GROUP(vin1_field), SH_PFC_PIN_GROUP(vin1_clkenb), diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index e40908dc37e0..151640c30e9d 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -30,7 +30,16 @@ PORT_GP_CFG_1(3, 15, fn, sfx, CFG_FLAGS), \ PORT_GP_CFG_11(4, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE), \ PORT_GP_CFG_20(5, fn, sfx, CFG_FLAGS), \ - PORT_GP_CFG_18(6, fn, sfx, CFG_FLAGS) + PORT_GP_CFG_9(6, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 9, fn, sfx, SH_PFC_PIN_CFG_PULL_UP), \ + PORT_GP_CFG_1(6, 10, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 11, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 12, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 13, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 14, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 15, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 16, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 17, fn, sfx, CFG_FLAGS) /* * F_() : just information * FM() : macro for FN_xxx / xxx_MARK @@ -391,29 +400,33 @@ FM(IP12_23_20) IP12_23_20 FM(IP13_23_20) IP13_23_20 FM(IP14_23_20) IP14_23_20 FM FM(IP12_27_24) IP12_27_24 FM(IP13_27_24) IP13_27_24 FM(IP14_27_24) IP14_27_24 FM(IP15_27_24) IP15_27_24 \ FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM(IP15_31_28) IP15_31_28 +/* The bit numbering in MOD_SEL fields is reversed */ +#define REV4(f0, f1, f2, f3) f0 f2 f1 f3 +#define REV8(f0, f1, f2, f3, f4, f5, f6, f7) f0 f4 f2 f6 f1 f5 f3 f7 + /* MOD_SEL0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ -#define MOD_SEL0_30_29 FM(SEL_ADGB_0) FM(SEL_ADGB_1) FM(SEL_ADGB_2) F_(0, 0) +#define MOD_SEL0_30_29 REV4(FM(SEL_ADGB_0), FM(SEL_ADGB_1), FM(SEL_ADGB_2), F_(0, 0)) #define MOD_SEL0_28 FM(SEL_DRIF0_0) FM(SEL_DRIF0_1) -#define MOD_SEL0_27_26 FM(SEL_FM_0) FM(SEL_FM_1) FM(SEL_FM_2) F_(0, 0) +#define MOD_SEL0_27_26 REV4(FM(SEL_FM_0), FM(SEL_FM_1), FM(SEL_FM_2), F_(0, 0)) #define MOD_SEL0_25 FM(SEL_FSO_0) FM(SEL_FSO_1) #define MOD_SEL0_24 FM(SEL_HSCIF0_0) FM(SEL_HSCIF0_1) #define MOD_SEL0_23 FM(SEL_HSCIF1_0) FM(SEL_HSCIF1_1) #define MOD_SEL0_22 FM(SEL_HSCIF2_0) FM(SEL_HSCIF2_1) -#define MOD_SEL0_21_20 FM(SEL_I2C1_0) FM(SEL_I2C1_1) FM(SEL_I2C1_2) FM(SEL_I2C1_3) -#define MOD_SEL0_19_18_17 FM(SEL_I2C2_0) FM(SEL_I2C2_1) FM(SEL_I2C2_2) FM(SEL_I2C2_3) FM(SEL_I2C2_4) F_(0, 0) F_(0, 0) F_(0, 0) +#define MOD_SEL0_21_20 REV4(FM(SEL_I2C1_0), FM(SEL_I2C1_1), FM(SEL_I2C1_2), FM(SEL_I2C1_3)) +#define MOD_SEL0_19_18_17 REV8(FM(SEL_I2C2_0), FM(SEL_I2C2_1), FM(SEL_I2C2_2), FM(SEL_I2C2_3), FM(SEL_I2C2_4), F_(0, 0), F_(0, 0), F_(0, 0)) #define MOD_SEL0_16 FM(SEL_NDFC_0) FM(SEL_NDFC_1) #define MOD_SEL0_15 FM(SEL_PWM0_0) FM(SEL_PWM0_1) #define MOD_SEL0_14 FM(SEL_PWM1_0) FM(SEL_PWM1_1) -#define MOD_SEL0_13_12 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) F_(0, 0) -#define MOD_SEL0_11_10 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) F_(0, 0) +#define MOD_SEL0_13_12 REV4(FM(SEL_PWM2_0), FM(SEL_PWM2_1), FM(SEL_PWM2_2), F_(0, 0)) +#define MOD_SEL0_11_10 REV4(FM(SEL_PWM3_0), FM(SEL_PWM3_1), FM(SEL_PWM3_2), F_(0, 0)) #define MOD_SEL0_9 FM(SEL_PWM4_0) FM(SEL_PWM4_1) #define MOD_SEL0_8 FM(SEL_PWM5_0) FM(SEL_PWM5_1) #define MOD_SEL0_7 FM(SEL_PWM6_0) FM(SEL_PWM6_1) -#define MOD_SEL0_6_5 FM(SEL_REMOCON_0) FM(SEL_REMOCON_1) FM(SEL_REMOCON_2) F_(0, 0) +#define MOD_SEL0_6_5 REV4(FM(SEL_REMOCON_0), FM(SEL_REMOCON_1), FM(SEL_REMOCON_2), F_(0, 0)) #define MOD_SEL0_4 FM(SEL_SCIF_0) FM(SEL_SCIF_1) #define MOD_SEL0_3 FM(SEL_SCIF0_0) FM(SEL_SCIF0_1) #define MOD_SEL0_2 FM(SEL_SCIF2_0) FM(SEL_SCIF2_1) -#define MOD_SEL0_1_0 FM(SEL_SPEED_PULSE_IF_0) FM(SEL_SPEED_PULSE_IF_1) FM(SEL_SPEED_PULSE_IF_2) F_(0, 0) +#define MOD_SEL0_1_0 REV4(FM(SEL_SPEED_PULSE_IF_0), FM(SEL_SPEED_PULSE_IF_1), FM(SEL_SPEED_PULSE_IF_2), F_(0, 0)) /* MOD_SEL1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ #define MOD_SEL1_31 FM(SEL_SIMCARD_0) FM(SEL_SIMCARD_1) @@ -422,18 +435,18 @@ FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM #define MOD_SEL1_28 FM(SEL_USB_20_CH0_0) FM(SEL_USB_20_CH0_1) #define MOD_SEL1_26 FM(SEL_DRIF2_0) FM(SEL_DRIF2_1) #define MOD_SEL1_25 FM(SEL_DRIF3_0) FM(SEL_DRIF3_1) -#define MOD_SEL1_24_23_22 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1) FM(SEL_HSCIF3_2) FM(SEL_HSCIF3_3) FM(SEL_HSCIF3_4) F_(0, 0) F_(0, 0) F_(0, 0) -#define MOD_SEL1_21_20_19 FM(SEL_HSCIF4_0) FM(SEL_HSCIF4_1) FM(SEL_HSCIF4_2) FM(SEL_HSCIF4_3) FM(SEL_HSCIF4_4) F_(0, 0) F_(0, 0) F_(0, 0) +#define MOD_SEL1_24_23_22 REV8(FM(SEL_HSCIF3_0), FM(SEL_HSCIF3_1), FM(SEL_HSCIF3_2), FM(SEL_HSCIF3_3), FM(SEL_HSCIF3_4), F_(0, 0), F_(0, 0), F_(0, 0)) +#define MOD_SEL1_21_20_19 REV8(FM(SEL_HSCIF4_0), FM(SEL_HSCIF4_1), FM(SEL_HSCIF4_2), FM(SEL_HSCIF4_3), FM(SEL_HSCIF4_4), F_(0, 0), F_(0, 0), F_(0, 0)) #define MOD_SEL1_18 FM(SEL_I2C6_0) FM(SEL_I2C6_1) #define MOD_SEL1_17 FM(SEL_I2C7_0) FM(SEL_I2C7_1) #define MOD_SEL1_16 FM(SEL_MSIOF2_0) FM(SEL_MSIOF2_1) #define MOD_SEL1_15 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1) -#define MOD_SEL1_14_13 FM(SEL_SCIF3_0) FM(SEL_SCIF3_1) FM(SEL_SCIF3_2) F_(0, 0) -#define MOD_SEL1_12_11 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1) FM(SEL_SCIF4_2) F_(0, 0) -#define MOD_SEL1_10_9 FM(SEL_SCIF5_0) FM(SEL_SCIF5_1) FM(SEL_SCIF5_2) F_(0, 0) +#define MOD_SEL1_14_13 REV4(FM(SEL_SCIF3_0), FM(SEL_SCIF3_1), FM(SEL_SCIF3_2), F_(0, 0)) +#define MOD_SEL1_12_11 REV4(FM(SEL_SCIF4_0), FM(SEL_SCIF4_1), FM(SEL_SCIF4_2), F_(0, 0)) +#define MOD_SEL1_10_9 REV4(FM(SEL_SCIF5_0), FM(SEL_SCIF5_1), FM(SEL_SCIF5_2), F_(0, 0)) #define MOD_SEL1_8 FM(SEL_VIN4_0) FM(SEL_VIN4_1) #define MOD_SEL1_7 FM(SEL_VIN5_0) FM(SEL_VIN5_1) -#define MOD_SEL1_6_5 FM(SEL_ADGC_0) FM(SEL_ADGC_1) FM(SEL_ADGC_2) F_(0, 0) +#define MOD_SEL1_6_5 REV4(FM(SEL_ADGC_0), FM(SEL_ADGC_1), FM(SEL_ADGC_2), F_(0, 0)) #define MOD_SEL1_4 FM(SEL_SSI9_0) FM(SEL_SSI9_1) #define PINMUX_MOD_SELS \ @@ -1060,7 +1073,7 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_GPSR(IP11_11_8, RIF1_SYNC), PINMUX_IPSR_GPSR(IP11_11_8, TS_SCK1), - PINMUX_IPSR_GPSR(IP11_15_12, TX0_A), + PINMUX_IPSR_MSEL(IP11_15_12, TX0_A, SEL_SCIF0_0), PINMUX_IPSR_GPSR(IP11_15_12, HTX1_A), PINMUX_IPSR_MSEL(IP11_15_12, SSI_WS2_A, SEL_SSI2_0), PINMUX_IPSR_GPSR(IP11_15_12, RIF1_D0), @@ -1099,7 +1112,7 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_MSEL(IP12_3_0, SSI_WS9_B, SEL_SSI9_1), PINMUX_IPSR_GPSR(IP12_3_0, AUDIO_CLKOUT3_B), - PINMUX_IPSR_GPSR(IP12_7_4, SCK2_A), + PINMUX_IPSR_MSEL(IP12_7_4, SCK2_A, SEL_SCIF2_0), PINMUX_IPSR_MSEL(IP12_7_4, HSCK0_A, SEL_HSCIF0_0), PINMUX_IPSR_MSEL(IP12_7_4, AUDIO_CLKB_A, SEL_ADGB_0), PINMUX_IPSR_GPSR(IP12_7_4, CTS1_N), @@ -1107,14 +1120,14 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_MSEL(IP12_7_4, REMOCON_A, SEL_REMOCON_0), PINMUX_IPSR_MSEL(IP12_7_4, SCIF_CLK_B, SEL_SCIF_1), - PINMUX_IPSR_GPSR(IP12_11_8, TX2_A), + PINMUX_IPSR_MSEL(IP12_11_8, TX2_A, SEL_SCIF2_0), PINMUX_IPSR_MSEL(IP12_11_8, HRX0_A, SEL_HSCIF0_0), PINMUX_IPSR_GPSR(IP12_11_8, AUDIO_CLKOUT2_A), PINMUX_IPSR_MSEL(IP12_11_8, SCL1_A, SEL_I2C1_0), PINMUX_IPSR_MSEL(IP12_11_8, FSO_CFE_0_N_A, SEL_FSO_0), PINMUX_IPSR_GPSR(IP12_11_8, TS_SDEN1), - PINMUX_IPSR_GPSR(IP12_15_12, RX2_A), + PINMUX_IPSR_MSEL(IP12_15_12, RX2_A, SEL_SCIF2_0), PINMUX_IPSR_GPSR(IP12_15_12, HTX0_A), PINMUX_IPSR_GPSR(IP12_15_12, AUDIO_CLKOUT3_A), PINMUX_IPSR_MSEL(IP12_15_12, SDA1_A, SEL_I2C1_0), @@ -1126,11 +1139,11 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_GPSR(IP12_23_20, MSIOF0_RXD), PINMUX_IPSR_GPSR(IP12_23_20, SSI_WS78), - PINMUX_IPSR_GPSR(IP12_23_20, TX2_B), + PINMUX_IPSR_MSEL(IP12_23_20, TX2_B, SEL_SCIF2_1), PINMUX_IPSR_GPSR(IP12_27_24, MSIOF0_TXD), PINMUX_IPSR_GPSR(IP12_27_24, SSI_SDATA7), - PINMUX_IPSR_GPSR(IP12_27_24, RX2_B), + PINMUX_IPSR_MSEL(IP12_27_24, RX2_B, SEL_SCIF2_1), PINMUX_IPSR_GPSR(IP12_31_28, MSIOF0_SYNC), PINMUX_IPSR_GPSR(IP12_31_28, AUDIO_CLKOUT_B), @@ -1170,7 +1183,7 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_MSEL(IP13_19_16, SIM0_D_A, SEL_SIMCARD_0), PINMUX_IPSR_GPSR(IP13_23_20, MLB_DAT), - PINMUX_IPSR_GPSR(IP13_23_20, TX0_B), + PINMUX_IPSR_MSEL(IP13_23_20, TX0_B, SEL_SCIF0_1), PINMUX_IPSR_MSEL(IP13_23_20, RIF0_SYNC_A, SEL_DRIF0_0), PINMUX_IPSR_GPSR(IP13_23_20, SIM0_CLK_A), @@ -1586,6 +1599,199 @@ static const unsigned int canfd1_data_mux[] = { CANFD1_TX_MARK, CANFD1_RX_MARK, }; +/* - DRIF0 --------------------------------------------------------------- */ +static const unsigned int drif0_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 19), +}; + +static const unsigned int drif0_ctrl_a_mux[] = { + RIF0_CLK_A_MARK, RIF0_SYNC_A_MARK, +}; + +static const unsigned int drif0_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 17), +}; + +static const unsigned int drif0_data0_a_mux[] = { + RIF0_D0_A_MARK, +}; + +static const unsigned int drif0_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 18), +}; + +static const unsigned int drif0_data1_a_mux[] = { + RIF0_D1_A_MARK, +}; + +static const unsigned int drif0_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 15), +}; + +static const unsigned int drif0_ctrl_b_mux[] = { + RIF0_CLK_B_MARK, RIF0_SYNC_B_MARK, +}; + +static const unsigned int drif0_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(3, 13), +}; + +static const unsigned int drif0_data0_b_mux[] = { + RIF0_D0_B_MARK, +}; + +static const unsigned int drif0_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(3, 14), +}; + +static const unsigned int drif0_data1_b_mux[] = { + RIF0_D1_B_MARK, +}; + +/* - DRIF1 --------------------------------------------------------------- */ +static const unsigned int drif1_ctrl_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 4), RCAR_GP_PIN(5, 1), +}; + +static const unsigned int drif1_ctrl_mux[] = { + RIF1_CLK_MARK, RIF1_SYNC_MARK, +}; + +static const unsigned int drif1_data0_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 2), +}; + +static const unsigned int drif1_data0_mux[] = { + RIF1_D0_MARK, +}; + +static const unsigned int drif1_data1_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 3), +}; + +static const unsigned int drif1_data1_mux[] = { + RIF1_D1_MARK, +}; + +/* - DRIF2 --------------------------------------------------------------- */ +static const unsigned int drif2_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), +}; + +static const unsigned int drif2_ctrl_a_mux[] = { + RIF2_CLK_A_MARK, RIF2_SYNC_A_MARK, +}; + +static const unsigned int drif2_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(2, 8), +}; + +static const unsigned int drif2_data0_a_mux[] = { + RIF2_D0_A_MARK, +}; + +static const unsigned int drif2_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(2, 9), +}; + +static const unsigned int drif2_data1_a_mux[] = { + RIF2_D1_A_MARK, +}; + +static const unsigned int drif2_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), +}; + +static const unsigned int drif2_ctrl_b_mux[] = { + RIF2_CLK_B_MARK, RIF2_SYNC_B_MARK, +}; + +static const unsigned int drif2_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(1, 6), +}; + +static const unsigned int drif2_data0_b_mux[] = { + RIF2_D0_B_MARK, +}; + +static const unsigned int drif2_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(1, 7), +}; + +static const unsigned int drif2_data1_b_mux[] = { + RIF2_D1_B_MARK, +}; + +/* - DRIF3 --------------------------------------------------------------- */ +static const unsigned int drif3_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), +}; + +static const unsigned int drif3_ctrl_a_mux[] = { + RIF3_CLK_A_MARK, RIF3_SYNC_A_MARK, +}; + +static const unsigned int drif3_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(2, 12), +}; + +static const unsigned int drif3_data0_a_mux[] = { + RIF3_D0_A_MARK, +}; + +static const unsigned int drif3_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(2, 13), +}; + +static const unsigned int drif3_data1_a_mux[] = { + RIF3_D1_A_MARK, +}; + +static const unsigned int drif3_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9), +}; + +static const unsigned int drif3_ctrl_b_mux[] = { + RIF3_CLK_B_MARK, RIF3_SYNC_B_MARK, +}; + +static const unsigned int drif3_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(0, 10), +}; + +static const unsigned int drif3_data0_b_mux[] = { + RIF3_D0_B_MARK, +}; + +static const unsigned int drif3_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(0, 11), +}; + +static const unsigned int drif3_data1_b_mux[] = { + RIF3_D1_B_MARK, +}; + /* - DU --------------------------------------------------------------------- */ static const unsigned int du_rgb666_pins[] = { /* R[7:2], G[7:2], B[7:2] */ @@ -3243,6 +3449,43 @@ static const unsigned int ssi9_ctrl_b_mux[] = { SSI_SCK9_B_MARK, SSI_WS9_B_MARK, }; +/* - TMU -------------------------------------------------------------------- */ +static const unsigned int tmu_tclk1_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(3, 12), +}; + +static const unsigned int tmu_tclk1_a_mux[] = { + TCLK1_A_MARK, +}; + +static const unsigned int tmu_tclk1_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(5, 17), +}; + +static const unsigned int tmu_tclk1_b_mux[] = { + TCLK1_B_MARK, +}; + +static const unsigned int tmu_tclk2_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(3, 13), +}; + +static const unsigned int tmu_tclk2_a_mux[] = { + TCLK2_A_MARK, +}; + +static const unsigned int tmu_tclk2_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(5, 18), +}; + +static const unsigned int tmu_tclk2_b_mux[] = { + TCLK2_B_MARK, +}; + /* - USB0 ------------------------------------------------------------------- */ static const unsigned int usb0_a_pins[] = { /* PWEN, OVC */ @@ -3523,8 +3766,8 @@ static const unsigned int vin5_clk_b_mux[] = { }; static const struct { - struct sh_pfc_pin_group common[241]; - struct sh_pfc_pin_group automotive[2]; + struct sh_pfc_pin_group common[245]; + struct sh_pfc_pin_group automotive[23]; } pinmux_groups = { .common = { SH_PFC_PIN_GROUP(audio_clk_a), @@ -3735,6 +3978,10 @@ static const struct { SH_PFC_PIN_GROUP(ssi9_data), SH_PFC_PIN_GROUP(ssi9_ctrl_a), SH_PFC_PIN_GROUP(ssi9_ctrl_b), + SH_PFC_PIN_GROUP(tmu_tclk1_a), + SH_PFC_PIN_GROUP(tmu_tclk1_b), + SH_PFC_PIN_GROUP(tmu_tclk2_a), + SH_PFC_PIN_GROUP(tmu_tclk2_b), SH_PFC_PIN_GROUP(usb0_a), SH_PFC_PIN_GROUP(usb0_b), SH_PFC_PIN_GROUP(usb0_id), @@ -3772,6 +4019,27 @@ static const struct { .automotive = { SH_PFC_PIN_GROUP(canfd0_data), SH_PFC_PIN_GROUP(canfd1_data), + SH_PFC_PIN_GROUP(drif0_ctrl_a), + SH_PFC_PIN_GROUP(drif0_data0_a), + SH_PFC_PIN_GROUP(drif0_data1_a), + SH_PFC_PIN_GROUP(drif0_ctrl_b), + SH_PFC_PIN_GROUP(drif0_data0_b), + SH_PFC_PIN_GROUP(drif0_data1_b), + SH_PFC_PIN_GROUP(drif1_ctrl), + SH_PFC_PIN_GROUP(drif1_data0), + SH_PFC_PIN_GROUP(drif1_data1), + SH_PFC_PIN_GROUP(drif2_ctrl_a), + SH_PFC_PIN_GROUP(drif2_data0_a), + SH_PFC_PIN_GROUP(drif2_data1_a), + SH_PFC_PIN_GROUP(drif2_ctrl_b), + SH_PFC_PIN_GROUP(drif2_data0_b), + SH_PFC_PIN_GROUP(drif2_data1_b), + SH_PFC_PIN_GROUP(drif3_ctrl_a), + SH_PFC_PIN_GROUP(drif3_data0_a), + SH_PFC_PIN_GROUP(drif3_data1_a), + SH_PFC_PIN_GROUP(drif3_ctrl_b), + SH_PFC_PIN_GROUP(drif3_data0_b), + SH_PFC_PIN_GROUP(drif3_data1_b), } }; @@ -3826,6 +4094,39 @@ static const char * const canfd1_groups[] = { "canfd1_data", }; +static const char * const drif0_groups[] = { + "drif0_ctrl_a", + "drif0_data0_a", + "drif0_data1_a", + "drif0_ctrl_b", + "drif0_data0_b", + "drif0_data1_b", +}; + +static const char * const drif1_groups[] = { + "drif1_ctrl", + "drif1_data0", + "drif1_data1", +}; + +static const char * const drif2_groups[] = { + "drif2_ctrl_a", + "drif2_data0_a", + "drif2_data1_a", + "drif2_ctrl_b", + "drif2_data0_b", + "drif2_data1_b", +}; + +static const char * const drif3_groups[] = { + "drif3_ctrl_a", + "drif3_data0_a", + "drif3_data1_a", + "drif3_ctrl_b", + "drif3_data0_b", + "drif3_data1_b", +}; + static const char * const du_groups[] = { "du_rgb666", "du_rgb888", @@ -4111,6 +4412,13 @@ static const char * const ssi_groups[] = { "ssi9_ctrl_b", }; +static const char * const tmu_groups[] = { + "tmu_tclk1_a", + "tmu_tclk1_b", + "tmu_tclk2_a", + "tmu_tclk2_b", +}; + static const char * const usb0_groups[] = { "usb0_a", "usb0_b", @@ -4157,8 +4465,8 @@ static const char * const vin5_groups[] = { }; static const struct { - struct sh_pfc_function common[44]; - struct sh_pfc_function automotive[2]; + struct sh_pfc_function common[45]; + struct sh_pfc_function automotive[6]; } pinmux_functions = { .common = { SH_PFC_FUNCTION(audio_clk), @@ -4201,6 +4509,7 @@ static const struct { SH_PFC_FUNCTION(sdhi1), SH_PFC_FUNCTION(sdhi3), SH_PFC_FUNCTION(ssi), + SH_PFC_FUNCTION(tmu), SH_PFC_FUNCTION(usb0), SH_PFC_FUNCTION(usb30), SH_PFC_FUNCTION(vin4), @@ -4209,6 +4518,10 @@ static const struct { .automotive = { SH_PFC_FUNCTION(canfd0), SH_PFC_FUNCTION(canfd1), + SH_PFC_FUNCTION(drif0), + SH_PFC_FUNCTION(drif1), + SH_PFC_FUNCTION(drif2), + SH_PFC_FUNCTION(drif3), } }; @@ -4914,17 +5227,6 @@ static const struct pinmux_bias_reg pinmux_bias_regs[] = { { /* sentinel */ }, }; -static bool pin_has_pud(unsigned int pin) -{ - /* Some pins are pull-up only */ - switch (pin) { - case RCAR_GP_PIN(6, 9): /* USB30_OVC */ - return false; - } - - return true; -} - static unsigned int r8a77990_pinmux_get_bias(struct sh_pfc *pfc, unsigned int pin) { @@ -4937,7 +5239,7 @@ static unsigned int r8a77990_pinmux_get_bias(struct sh_pfc *pfc, if (!(sh_pfc_read(pfc, reg->puen) & BIT(bit))) return PIN_CONFIG_BIAS_DISABLE; - else if (!pin_has_pud(pin) || (sh_pfc_read(pfc, reg->pud) & BIT(bit))) + else if (sh_pfc_read(pfc, reg->pud) & BIT(bit)) return PIN_CONFIG_BIAS_PULL_UP; else return PIN_CONFIG_BIAS_PULL_DOWN; @@ -4958,13 +5260,11 @@ static void r8a77990_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin, if (bias != PIN_CONFIG_BIAS_DISABLE) enable |= BIT(bit); - if (pin_has_pud(pin)) { - updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit); - if (bias == PIN_CONFIG_BIAS_PULL_UP) - updown |= BIT(bit); + updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit); + if (bias == PIN_CONFIG_BIAS_PULL_UP) + updown |= BIT(bit); - sh_pfc_write(pfc, reg->pud, updown); - } + sh_pfc_write(pfc, reg->pud, updown); sh_pfc_write(pfc, reg->puen, enable); } diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c index 84d78db381e3..9e377e3b9cb3 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c @@ -381,6 +381,9 @@ FM(IP12_23_20) IP12_23_20 \ FM(IP12_27_24) IP12_27_24 \ FM(IP12_31_28) IP12_31_28 \ +/* The bit numbering in MOD_SEL fields is reversed */ +#define REV4(f0, f1, f2, f3) f0 f2 f1 f3 + /* MOD_SEL0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ #define MOD_SEL0_30 FM(SEL_MSIOF2_0) FM(SEL_MSIOF2_1) #define MOD_SEL0_29 FM(SEL_I2C3_0) FM(SEL_I2C3_1) @@ -388,10 +391,10 @@ FM(IP12_31_28) IP12_31_28 \ #define MOD_SEL0_27 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1) #define MOD_SEL0_26 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1) #define MOD_SEL0_25 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1) -#define MOD_SEL0_24_23 FM(SEL_PWM0_0) FM(SEL_PWM0_1) FM(SEL_PWM0_2) F_(0, 0) -#define MOD_SEL0_22_21 FM(SEL_PWM1_0) FM(SEL_PWM1_1) FM(SEL_PWM1_2) F_(0, 0) -#define MOD_SEL0_20_19 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) F_(0, 0) -#define MOD_SEL0_18_17 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) F_(0, 0) +#define MOD_SEL0_24_23 REV4(FM(SEL_PWM0_0), FM(SEL_PWM0_1), FM(SEL_PWM0_2), F_(0, 0)) +#define MOD_SEL0_22_21 REV4(FM(SEL_PWM1_0), FM(SEL_PWM1_1), FM(SEL_PWM1_2), F_(0, 0)) +#define MOD_SEL0_20_19 REV4(FM(SEL_PWM2_0), FM(SEL_PWM2_1), FM(SEL_PWM2_2), F_(0, 0)) +#define MOD_SEL0_18_17 REV4(FM(SEL_PWM3_0), FM(SEL_PWM3_1), FM(SEL_PWM3_2), F_(0, 0)) #define MOD_SEL0_15 FM(SEL_IRQ_0_0) FM(SEL_IRQ_0_1) #define MOD_SEL0_14 FM(SEL_IRQ_1_0) FM(SEL_IRQ_1_1) #define MOD_SEL0_13 FM(SEL_IRQ_2_0) FM(SEL_IRQ_2_1) diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c index 085770e66895..ef3da8bf1d87 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c @@ -3354,7 +3354,8 @@ static const char * const fsic_groups[] = { "fsic_sclk_out", "fsic_data_in", "fsic_data_out", - "fsic_spdif", + "fsic_spdif_0", + "fsic_spdif_1", }; static const char * const fsid_groups[] = { diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 274d5ff87078..c97d2ba7677c 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -347,6 +347,8 @@ static int sh_pfc_func_set_mux(struct pinctrl_dev *pctldev, unsigned selector, unsigned int i; int ret = 0; + dev_dbg(pctldev->dev, "Configuring pin group %s\n", grp->name); + spin_lock_irqsave(&pfc->lock, flags); for (i = 0; i < grp->nr_pins; ++i) { diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h index 46d477ff5109..56016cb76769 100644 --- a/drivers/pinctrl/sh-pfc/sh_pfc.h +++ b/drivers/pinctrl/sh-pfc/sh_pfc.h @@ -126,7 +126,8 @@ struct pinmux_cfg_reg { * one for each possible combination of the register field bit values. */ #define PINMUX_CFG_REG(name, r, r_width, f_width) \ - .reg = r, .reg_width = r_width, .field_width = f_width, \ + .reg = r, .reg_width = r_width, \ + .field_width = f_width + BUILD_BUG_ON_ZERO(r_width % f_width), \ .enum_ids = (const u16 [(r_width / f_width) * (1 << f_width)]) /* diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c index 4ba171827428..8a0eee044264 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas7.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c @@ -6007,8 +6007,8 @@ static int atlas7_gpio_probe(struct platform_device *pdev) } /* retrieve gpio descriptor data */ - a7gc = devm_kzalloc(&pdev->dev, sizeof(*a7gc) + - sizeof(struct atlas7_gpio_bank) * nbank, GFP_KERNEL); + a7gc = devm_kzalloc(&pdev->dev, struct_size(a7gc, banks, nbank), + GFP_KERNEL); if (!a7gc) return -ENOMEM; diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index 2e42d738b589..2b3bd1a41f21 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -782,7 +782,7 @@ static void sirfsoc_gpio_set_pulldown(struct sirfsoc_gpio_chip *sgpio, static int sirfsoc_gpio_probe(struct device_node *np) { int i, err = 0; - static struct sirfsoc_gpio_chip *sgpio; + struct sirfsoc_gpio_chip *sgpio; struct sirfsoc_gpio_bank *bank; void __iomem *regs; struct platform_device *pdev; diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 813eccbb9aaf..0b9ff5aa6bb5 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -414,7 +414,7 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, unsigned int num_configs; bool has_config = 0; unsigned reserve = 0; - int num_pins, num_funcs, maps_per_pin, i, err; + int num_pins, num_funcs, maps_per_pin, i, err = 0; pctl = pinctrl_dev_get_drvdata(pctldev); @@ -441,41 +441,45 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, if (has_config && num_pins >= 1) maps_per_pin++; - if (!num_pins || !maps_per_pin) - return -EINVAL; + if (!num_pins || !maps_per_pin) { + err = -EINVAL; + goto exit; + } reserve = num_pins * maps_per_pin; err = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps, reserve); if (err) - return err; + goto exit; for (i = 0; i < num_pins; i++) { err = of_property_read_u32_index(node, "pinmux", i, &pinfunc); if (err) - return err; + goto exit; pin = STM32_GET_PIN_NO(pinfunc); func = STM32_GET_PIN_FUNC(pinfunc); if (!stm32_pctrl_is_function_valid(pctl, pin, func)) { dev_err(pctl->dev, "invalid function.\n"); - return -EINVAL; + err = -EINVAL; + goto exit; } grp = stm32_pctrl_find_group_by_pin(pctl, pin); if (!grp) { dev_err(pctl->dev, "unable to match pin %d to group\n", pin); - return -EINVAL; + err = -EINVAL; + goto exit; } err = stm32_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map, reserved_maps, num_maps); if (err) - return err; + goto exit; if (has_config) { err = pinctrl_utils_add_map_configs(pctldev, map, @@ -483,11 +487,13 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, configs, num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); if (err) - return err; + goto exit; } } - return 0; +exit: + kfree(configs); + return err; } static int stm32_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, @@ -577,8 +583,8 @@ static int stm32_pmx_get_func_groups(struct pinctrl_dev *pctldev, return 0; } -static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, - int pin, u32 mode, u32 alt) +static int stm32_pmx_set_mode(struct stm32_gpio_bank *bank, + int pin, u32 mode, u32 alt) { struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); u32 val; @@ -614,6 +620,8 @@ static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, unlock: spin_unlock_irqrestore(&bank->lock, flags); clk_disable(bank->clk); + + return err; } void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode, @@ -670,9 +678,7 @@ static int stm32_pmx_set_mux(struct pinctrl_dev *pctldev, mode = stm32_gpio_get_mode(function); alt = stm32_gpio_get_alt(function); - stm32_pmx_set_mode(bank, pin, mode, alt); - - return 0; + return stm32_pmx_set_mode(bank, pin, mode, alt); } static int stm32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, @@ -682,9 +688,7 @@ static int stm32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, struct stm32_gpio_bank *bank = gpiochip_get_data(range->gc); int pin = stm32_gpio_pin(gpio); - stm32_pmx_set_mode(bank, pin, !input, 0); - - return 0; + return stm32_pmx_set_mode(bank, pin, !input, 0); } static const struct pinmux_ops stm32_pmx_ops = { @@ -698,8 +702,8 @@ static const struct pinmux_ops stm32_pmx_ops = { /* Pinconf functions */ -static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, - unsigned offset, u32 drive) +static int stm32_pconf_set_driving(struct stm32_gpio_bank *bank, + unsigned offset, u32 drive) { struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); unsigned long flags; @@ -728,6 +732,8 @@ static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, unlock: spin_unlock_irqrestore(&bank->lock, flags); clk_disable(bank->clk); + + return err; } static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank, @@ -748,8 +754,8 @@ static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank, return (val >> offset); } -static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, - unsigned offset, u32 speed) +static int stm32_pconf_set_speed(struct stm32_gpio_bank *bank, + unsigned offset, u32 speed) { struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); unsigned long flags; @@ -778,6 +784,8 @@ static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, unlock: spin_unlock_irqrestore(&bank->lock, flags); clk_disable(bank->clk); + + return err; } static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank, @@ -798,8 +806,8 @@ static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank, return (val >> (offset * 2)); } -static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, - unsigned offset, u32 bias) +static int stm32_pconf_set_bias(struct stm32_gpio_bank *bank, + unsigned offset, u32 bias) { struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); unsigned long flags; @@ -828,6 +836,8 @@ static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, unlock: spin_unlock_irqrestore(&bank->lock, flags); clk_disable(bank->clk); + + return err; } static u32 stm32_pconf_get_bias(struct stm32_gpio_bank *bank, @@ -890,22 +900,22 @@ static int stm32_pconf_parse_conf(struct pinctrl_dev *pctldev, switch (param) { case PIN_CONFIG_DRIVE_PUSH_PULL: - stm32_pconf_set_driving(bank, offset, 0); + ret = stm32_pconf_set_driving(bank, offset, 0); break; case PIN_CONFIG_DRIVE_OPEN_DRAIN: - stm32_pconf_set_driving(bank, offset, 1); + ret = stm32_pconf_set_driving(bank, offset, 1); break; case PIN_CONFIG_SLEW_RATE: - stm32_pconf_set_speed(bank, offset, arg); + ret = stm32_pconf_set_speed(bank, offset, arg); break; case PIN_CONFIG_BIAS_DISABLE: - stm32_pconf_set_bias(bank, offset, 0); + ret = stm32_pconf_set_bias(bank, offset, 0); break; case PIN_CONFIG_BIAS_PULL_UP: - stm32_pconf_set_bias(bank, offset, 1); + ret = stm32_pconf_set_bias(bank, offset, 1); break; case PIN_CONFIG_BIAS_PULL_DOWN: - stm32_pconf_set_bias(bank, offset, 2); + ret = stm32_pconf_set_bias(bank, offset, 2); break; case PIN_CONFIG_OUTPUT: __stm32_gpio_set(bank, offset, arg); diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c index c63086c98335..e05dd9a5551d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c @@ -153,6 +153,7 @@ static const struct sunxi_pinctrl_desc sun9i_a80_r_pinctrl_data = { .pin_base = PL_BASE, .irq_banks = 2, .disable_strict_mode = true, + .has_io_bias_cfg = true, }; static int sun9i_a80_r_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c index 5553c0eb0f41..da37d594a13d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c @@ -722,6 +722,7 @@ static const struct sunxi_pinctrl_desc sun9i_a80_pinctrl_data = { .npins = ARRAY_SIZE(sun9i_a80_pins), .irq_banks = 5, .disable_strict_mode = true, + .has_io_bias_cfg = true, }; static int sun9i_a80_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 0e7fa69e93df..8dd25caea2cf 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -603,6 +603,45 @@ static const struct pinconf_ops sunxi_pconf_ops = { .pin_config_group_set = sunxi_pconf_group_set, }; +static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, + unsigned pin, + struct regulator *supply) +{ + u32 val, reg; + int uV; + + if (!pctl->desc->has_io_bias_cfg) + return 0; + + uV = regulator_get_voltage(supply); + if (uV < 0) + return uV; + + /* Might be dummy regulator with no voltage set */ + if (uV == 0) + return 0; + + /* Configured value must be equal or greater to actual voltage */ + if (uV <= 1800000) + val = 0x0; /* 1.8V */ + else if (uV <= 2500000) + val = 0x6; /* 2.5V */ + else if (uV <= 2800000) + val = 0x9; /* 2.8V */ + else if (uV <= 3000000) + val = 0xA; /* 3.0V */ + else + val = 0xD; /* 3.3V */ + + pin -= pctl->desc->pin_base; + + reg = readl(pctl->membase + sunxi_grp_config_reg(pin)); + reg &= ~IO_BIAS_MASK; + writel(reg | val, pctl->membase + sunxi_grp_config_reg(pin)); + + return 0; +} + static int sunxi_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -725,6 +764,8 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset) goto out; } + sunxi_pinctrl_set_io_bias_cfg(pctl, offset, reg); + s_reg->regulator = reg; refcount_set(&s_reg->refcount, 1); diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index 034c0317c8d6..ee15ab067b5f 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -79,6 +79,10 @@ #define IRQ_LEVEL_LOW 0x03 #define IRQ_EDGE_BOTH 0x04 +#define GRP_CFG_REG 0x300 + +#define IO_BIAS_MASK GENMASK(3, 0) + #define SUN4I_FUNC_INPUT 0 #define SUN4I_FUNC_IRQ 6 @@ -113,6 +117,7 @@ struct sunxi_pinctrl_desc { const unsigned int *irq_bank_map; bool irq_read_needs_mux; bool disable_strict_mode; + bool has_io_bias_cfg; }; struct sunxi_pinctrl_function { @@ -338,6 +343,13 @@ static inline u32 sunxi_irq_status_offset(u16 irq) return irq_num * IRQ_STATUS_IRQ_BITS; } +static inline u32 sunxi_grp_config_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + + return GRP_CFG_REG + bank * 0x4; +} + int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, const struct sunxi_pinctrl_desc *desc, unsigned long variant); diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c index a4bc506a01a3..e5e7f1f22813 100644 --- a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -263,9 +263,9 @@ static int ti_iodelay_pinconf_set(struct ti_iodelay_device *iod, reg_val |= reg->unlock_val << __ffs(reg->lock_mask); r = regmap_update_bits(iod->regmap, cfg->offset, reg_mask, reg_val); - dev_info(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n", - cfg->offset, cfg->a_delay, cfg->g_delay, c_elements, - f_elements, reg_val); + dev_dbg(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n", + cfg->offset, cfg->a_delay, cfg->g_delay, c_elements, + f_elements, reg_val); return r; } @@ -923,7 +923,6 @@ static struct platform_driver ti_iodelay_driver = { .probe = ti_iodelay_probe, .remove = ti_iodelay_remove, .driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, .of_match_table = ti_iodelay_of_match, }, diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 5e2fde5ff63d..9186d81a51cc 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -152,4 +152,6 @@ config CROS_EC_SYSFS To compile this driver as a module, choose M here: the module will be called cros_ec_sysfs. +source "drivers/platform/chrome/wilco_ec/Kconfig" + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index fdbee501931b..1e2f0029b597 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -14,3 +14,5 @@ obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o + +obj-$(CONFIG_WILCO_EC) += wilco_ec/ diff --git a/drivers/platform/chrome/chromeos_pstore.c b/drivers/platform/chrome/chromeos_pstore.c index b0693fdec8c6..d13770785fb5 100644 --- a/drivers/platform/chrome/chromeos_pstore.c +++ b/drivers/platform/chrome/chromeos_pstore.c @@ -1,12 +1,7 @@ -/* - * chromeos_pstore.c - Driver to instantiate Chromebook ramoops device - * - * Copyright (C) 2013 Google, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - */ +// SPDX-License-Identifier: GPL-2.0 +// Driver to instantiate Chromebook ramoops device. +// +// Copyright (C) 2013 Google, Inc. #include <linux/acpi.h> #include <linux/dmi.h> @@ -138,5 +133,5 @@ static void __exit chromeos_pstore_exit(void) module_init(chromeos_pstore_init); module_exit(chromeos_pstore_exit); -MODULE_DESCRIPTION("Chrome OS pstore module"); -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS pstore module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 6cacac53dfce..71308766e891 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -1,21 +1,7 @@ -/* - * cros_ec_debugfs - debug logs for Chrome OS EC - * - * Copyright 2015 Google, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-License-Identifier: GPL-2.0+ +// Debug logs for the ChromeOS EC +// +// Copyright (C) 2015 Google, Inc. #include <linux/circ_buf.h> #include <linux/debugfs.h> @@ -454,7 +440,7 @@ static int cros_ec_debugfs_probe(struct platform_device *pd) ret = cros_ec_create_pdinfo(debug_info); if (ret) - goto remove_debugfs; + goto remove_log; ec->debug_info = debug_info; @@ -462,6 +448,8 @@ static int cros_ec_debugfs_probe(struct platform_device *pd) return 0; +remove_log: + cros_ec_cleanup_console_log(debug_info); remove_debugfs: debugfs_remove_recursive(debug_info->dir); return ret; @@ -481,7 +469,8 @@ static int __maybe_unused cros_ec_debugfs_suspend(struct device *dev) { struct cros_ec_dev *ec = dev_get_drvdata(dev); - cancel_delayed_work_sync(&ec->debug_info->log_poll_work); + if (ec->debug_info->log_buffer.buf) + cancel_delayed_work_sync(&ec->debug_info->log_poll_work); return 0; } @@ -490,7 +479,8 @@ static int __maybe_unused cros_ec_debugfs_resume(struct device *dev) { struct cros_ec_dev *ec = dev_get_drvdata(dev); - schedule_delayed_work(&ec->debug_info->log_poll_work, 0); + if (ec->debug_info->log_buffer.buf) + schedule_delayed_work(&ec->debug_info->log_poll_work, 0); return 0; } diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index 9a009eaa4ada..61d75395f86d 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -1,17 +1,7 @@ -/* - * ChromeOS EC multi-function device (I2C) - * - * Copyright (C) 2012 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +// I2C interface for ChromeOS Embedded Controller +// +// Copyright (C) 2012 Google, Inc #include <linux/acpi.h> #include <linux/delay.h> @@ -372,5 +362,5 @@ static struct i2c_driver cros_ec_driver = { module_i2c_driver(cros_ec_driver); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("ChromeOS EC multi function device"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("I2C interface for ChromeOS Embedded Controller"); diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index c3e4e6e5211d..d30a6650b0b5 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -1,23 +1,7 @@ -/* - * cros_ec_lightbar - expose the Chromebook Pixel lightbar to userspace - * - * Copyright (C) 2014 Google, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define pr_fmt(fmt) "cros_ec_lightbar: " fmt +// SPDX-License-Identifier: GPL-2.0+ +// Expose the Chromebook Pixel lightbar to userspace +// +// Copyright (C) 2014 Google, Inc. #include <linux/ctype.h> #include <linux/delay.h> diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 14684a56e40f..c9c240fbe7c6 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -1,25 +1,15 @@ -/* - * cros_ec_lpc - LPC access to the Chrome OS Embedded Controller - * - * Copyright (C) 2012-2015 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. - */ +// SPDX-License-Identifier: GPL-2.0 +// LPC interface for ChromeOS Embedded Controller +// +// Copyright (C) 2012-2015 Google, Inc +// +// This driver uses the ChromeOS EC byte-level message-based protocol for +// communicating the keyboard state (which keys are pressed) from a keyboard EC +// to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, +// but everything else (including deghosting) is done here. The main +// motivation for this is to keep the EC firmware as simple as possible, since +// it cannot be easily upgraded and EC flash/IRAM space is relatively +// expensive. #include <linux/acpi.h> #include <linux/dmi.h> diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c index c4edfa83e493..d8890bafb55d 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.c +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c @@ -1,29 +1,10 @@ -/* - * cros_ec_lpc_mec - LPC variant I/O for Microchip EC - * - * Copyright (C) 2016 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. - */ +// SPDX-License-Identifier: GPL-2.0 +// LPC variant I/O for Microchip EC +// +// Copyright (C) 2016 Google, Inc #include <linux/delay.h> #include <linux/io.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/mutex.h> #include <linux/types.h> @@ -34,6 +15,7 @@ * EC mutex because memmap data may be accessed without it being held. */ static struct mutex io_mutex; +static u16 mec_emi_base, mec_emi_end; /* * cros_ec_lpc_mec_emi_write_address @@ -46,10 +28,37 @@ static struct mutex io_mutex; static void cros_ec_lpc_mec_emi_write_address(u16 addr, enum cros_ec_lpc_mec_emi_access_mode access_type) { - /* Address relative to start of EMI range */ - addr -= MEC_EMI_RANGE_START; - outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0); - outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1); + outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0(mec_emi_base)); + outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1(mec_emi_base)); +} + +/** + * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range. + * + * @offset: Address offset + * @length: Number of bytes to check + * + * Return: 1 if in range, 0 if not, and -EINVAL on failure + * such as the mec range not being initialized + */ +int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length) +{ + if (length == 0) + return -EINVAL; + + if (WARN_ON(mec_emi_base == 0 || mec_emi_end == 0)) + return -EINVAL; + + if (offset >= mec_emi_base && offset < mec_emi_end) { + if (WARN_ON(offset + length - 1 >= mec_emi_end)) + return -EINVAL; + return 1; + } + + if (WARN_ON(offset + length > mec_emi_base && offset < mec_emi_end)) + return -EINVAL; + + return 0; } /* @@ -71,6 +80,11 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, u8 sum = 0; enum cros_ec_lpc_mec_emi_access_mode access, new_access; + /* Return checksum of 0 if window is not initialized */ + WARN_ON(mec_emi_base == 0 || mec_emi_end == 0); + if (mec_emi_base == 0 || mec_emi_end == 0) + return 0; + /* * Long access cannot be used on misaligned data since reading B0 loads * the data register and writing B3 flushes. @@ -86,9 +100,9 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, cros_ec_lpc_mec_emi_write_address(offset, access); /* Skip bytes in case of misaligned offset */ - io_addr = MEC_EMI_EC_DATA_B0 + (offset & 0x3); + io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base) + (offset & 0x3); while (i < length) { - while (io_addr <= MEC_EMI_EC_DATA_B3) { + while (io_addr <= MEC_EMI_EC_DATA_B3(mec_emi_base)) { if (io_type == MEC_IO_READ) buf[i] = inb(io_addr++); else @@ -118,7 +132,7 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, } /* Access [B0, B3] on each loop pass */ - io_addr = MEC_EMI_EC_DATA_B0; + io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base); } done: @@ -128,9 +142,11 @@ done: } EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec); -void cros_ec_lpc_mec_init(void) +void cros_ec_lpc_mec_init(unsigned int base, unsigned int end) { mutex_init(&io_mutex); + mec_emi_base = base; + mec_emi_end = end; } EXPORT_SYMBOL(cros_ec_lpc_mec_init); diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h index 105068c0e919..aa1018f6b0f2 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.h +++ b/drivers/platform/chrome/cros_ec_lpc_mec.h @@ -1,31 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * cros_ec_lpc_mec - LPC variant I/O for Microchip EC + * LPC variant I/O for Microchip EC * * Copyright (C) 2016 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. */ #ifndef __CROS_EC_LPC_MEC_H #define __CROS_EC_LPC_MEC_H -#include <linux/mfd/cros_ec_commands.h> - enum cros_ec_lpc_mec_emi_access_mode { /* 8-bit access */ ACCESS_TYPE_BYTE = 0x0, @@ -45,27 +27,23 @@ enum cros_ec_lpc_mec_io_type { MEC_IO_WRITE, }; -/* Access IO ranges 0x800 thru 0x9ff using EMI interface instead of LPC */ -#define MEC_EMI_RANGE_START EC_HOST_CMD_REGION0 -#define MEC_EMI_RANGE_END (EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE) - /* EMI registers are relative to base */ -#define MEC_EMI_BASE 0x800 -#define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0) -#define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1) -#define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2) -#define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3) -#define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4) -#define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5) -#define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6) -#define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7) +#define MEC_EMI_HOST_TO_EC(MEC_EMI_BASE) ((MEC_EMI_BASE) + 0) +#define MEC_EMI_EC_TO_HOST(MEC_EMI_BASE) ((MEC_EMI_BASE) + 1) +#define MEC_EMI_EC_ADDRESS_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 2) +#define MEC_EMI_EC_ADDRESS_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 3) +#define MEC_EMI_EC_DATA_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 4) +#define MEC_EMI_EC_DATA_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 5) +#define MEC_EMI_EC_DATA_B2(MEC_EMI_BASE) ((MEC_EMI_BASE) + 6) +#define MEC_EMI_EC_DATA_B3(MEC_EMI_BASE) ((MEC_EMI_BASE) + 7) -/* - * cros_ec_lpc_mec_init +/** + * cros_ec_lpc_mec_init() - Initialize MEC I/O. * - * Initialize MEC I/O. + * @base: MEC EMI Base address + * @end: MEC EMI End address */ -void cros_ec_lpc_mec_init(void); +void cros_ec_lpc_mec_init(unsigned int base, unsigned int end); /* * cros_ec_lpc_mec_destroy @@ -75,6 +53,17 @@ void cros_ec_lpc_mec_init(void); void cros_ec_lpc_mec_destroy(void); /** + * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range. + * + * @offset: Address offset + * @length: Number of bytes to check + * + * Return: 1 if in range, 0 if not, and -EINVAL on failure + * such as the mec range not being initialized + */ +int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length); + +/** * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port * * @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c index fc23d535c404..0f5cd0ac8b49 100644 --- a/drivers/platform/chrome/cros_ec_lpc_reg.c +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c @@ -1,25 +1,7 @@ -/* - * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller - * - * Copyright (C) 2016 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. - */ +// SPDX-License-Identifier: GPL-2.0 +// LPC interface for ChromeOS Embedded Controller +// +// Copyright (C) 2016 Google, Inc #include <linux/io.h> #include <linux/mfd/cros_ec.h> @@ -59,51 +41,36 @@ static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) { - if (length == 0) - return 0; - - /* Access desired range through EMI interface */ - if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) { - /* Ensure we don't straddle EMI region */ - if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END)) - return 0; + int in_range = cros_ec_lpc_mec_in_range(offset, length); - return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length, - dest); - } - - if (WARN_ON(offset + length > MEC_EMI_RANGE_START && - offset < MEC_EMI_RANGE_START)) + if (in_range < 0) return 0; - return lpc_read_bytes(offset, length, dest); + return in_range ? + cros_ec_lpc_io_bytes_mec(MEC_IO_READ, + offset - EC_HOST_CMD_REGION0, + length, dest) : + lpc_read_bytes(offset, length, dest); } u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) { - if (length == 0) - return 0; - - /* Access desired range through EMI interface */ - if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) { - /* Ensure we don't straddle EMI region */ - if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END)) - return 0; + int in_range = cros_ec_lpc_mec_in_range(offset, length); - return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length, - msg); - } - - if (WARN_ON(offset + length > MEC_EMI_RANGE_START && - offset < MEC_EMI_RANGE_START)) + if (in_range < 0) return 0; - return lpc_write_bytes(offset, length, msg); + return in_range ? + cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, + offset - EC_HOST_CMD_REGION0, + length, msg) : + lpc_write_bytes(offset, length, msg); } void cros_ec_lpc_reg_init(void) { - cros_ec_lpc_mec_init(); + cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, + EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); } void cros_ec_lpc_reg_destroy(void) diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.h b/drivers/platform/chrome/cros_ec_lpc_reg.h index 1c12c38b306a..416fd2572182 100644 --- a/drivers/platform/chrome/cros_ec_lpc_reg.h +++ b/drivers/platform/chrome/cros_ec_lpc_reg.h @@ -1,24 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller + * LPC interface for ChromeOS Embedded Controller * * Copyright (C) 2016 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. */ #ifndef __CROS_EC_LPC_REG_H diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index cc7baf0ecb3c..97a068dff192 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -1,18 +1,7 @@ -/* - * ChromeOS EC communication protocol helper functions - * - * Copyright (C) 2015 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0 +// ChromeOS EC communication protocol helper functions +// +// Copyright (C) 2015 Google, Inc #include <linux/mfd/cros_ec.h> #include <linux/delay.h> diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 6cfbc2835beb..ffc38f9d4829 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -1,17 +1,7 @@ -/* - * ChromeOS EC multi-function device (SPI) - * - * Copyright (C) 2012 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +// SPI interface for ChromeOS Embedded Controller +// +// Copyright (C) 2012 Google, Inc #include <linux/delay.h> #include <linux/kernel.h> @@ -729,4 +719,4 @@ static struct spi_driver cros_ec_driver_spi = { module_spi_driver(cros_ec_driver_spi); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)"); +MODULE_DESCRIPTION("SPI interface for ChromeOS Embedded Controller"); diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index 0ff5aa30c070..fe0b7614ae1b 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -1,23 +1,7 @@ -/* - * cros_ec_sysfs - expose the Chrome OS EC through sysfs - * - * Copyright (C) 2014 Google, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define pr_fmt(fmt) "cros_ec_sysfs: " fmt +// SPDX-License-Identifier: GPL-2.0+ +// Expose the ChromeOS EC through sysfs +// +// Copyright (C) 2014 Google, Inc. #include <linux/ctype.h> #include <linux/delay.h> @@ -389,5 +373,5 @@ static struct platform_driver cros_ec_sysfs_driver = { module_platform_driver(cros_ec_sysfs_driver); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("ChromeOS EC control driver"); +MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index af9bfe6f385c..8392a1ec33a7 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -1,22 +1,8 @@ -/* - * cros_ec_vbc - Expose the vboot context nvram to userspace - * - * Copyright (C) 2015 Collabora Ltd. - * - * based on vendor driver, - * - * Copyright (C) 2012 The Chromium OS Authors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0+ +// Expose the vboot context nvram to userspace +// +// Copyright (C) 2012 Google, Inc. +// Copyright (C) 2015 Collabora Ltd. #include <linux/of.h> #include <linux/platform_device.h> diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c index ca3e4da852b4..aa409f0201fb 100644 --- a/drivers/platform/chrome/cros_kbd_led_backlight.c +++ b/drivers/platform/chrome/cros_kbd_led_backlight.c @@ -1,18 +1,7 @@ -/* - * Keyboard backlight LED driver for Chrome OS. - * - * Copyright (C) 2012 Google, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0+ +// Keyboard backlight LED driver for ChromeOS +// +// Copyright (C) 2012 Google, Inc. #include <linux/acpi.h> #include <linux/leds.h> diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig new file mode 100644 index 000000000000..e09e4cebe9b4 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/Kconfig @@ -0,0 +1,20 @@ +config WILCO_EC + tristate "ChromeOS Wilco Embedded Controller" + depends on ACPI && X86 && CROS_EC_LPC && CROS_EC_LPC_MEC + help + If you say Y here, you get support for talking to the ChromeOS + Wilco EC over an eSPI bus. This uses a simple byte-level protocol + with a checksum. + + To compile this driver as a module, choose M here: the + module will be called wilco_ec. + +config WILCO_EC_DEBUGFS + tristate "Enable raw access to EC via debugfs" + depends on WILCO_EC + help + If you say Y here, you get support for sending raw commands to + the Wilco EC via debugfs. These commands do not do any byte + manipulation and allow for testing arbitrary commands. This + interface is intended for debug only and will not be present + on production devices. diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile new file mode 100644 index 000000000000..063e7fb4ea17 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +wilco_ec-objs := core.o mailbox.o +obj-$(CONFIG_WILCO_EC) += wilco_ec.o +wilco_ec_debugfs-objs := debugfs.o +obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c new file mode 100644 index 000000000000..05e1e2be1c91 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/core.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Core driver for Wilco Embedded Controller + * + * Copyright 2018 Google LLC + * + * This is the entry point for the drivers that control the Wilco EC. + * This driver is responsible for several tasks: + * - Initialize the register interface that is used by wilco_ec_mailbox() + * - Create a platform device which is picked up by the debugfs driver + * - Create a platform device which is picked up by the RTC driver + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/platform_data/wilco-ec.h> +#include <linux/platform_device.h> + +#include "../cros_ec_lpc_mec.h" + +#define DRV_NAME "wilco-ec" + +static struct resource *wilco_get_resource(struct platform_device *pdev, + int index) +{ + struct device *dev = &pdev->dev; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IO, index); + if (!res) { + dev_dbg(dev, "Couldn't find IO resource %d\n", index); + return res; + } + + return devm_request_region(dev, res->start, resource_size(res), + dev_name(dev)); +} + +static int wilco_ec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wilco_ec_device *ec; + int ret; + + ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); + if (!ec) + return -ENOMEM; + + platform_set_drvdata(pdev, ec); + ec->dev = dev; + mutex_init(&ec->mailbox_lock); + + /* Largest data buffer size requirement is extended data response */ + ec->data_size = sizeof(struct wilco_ec_response) + + EC_MAILBOX_DATA_SIZE_EXTENDED; + ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL); + if (!ec->data_buffer) + return -ENOMEM; + + /* Prepare access to IO regions provided by ACPI */ + ec->io_data = wilco_get_resource(pdev, 0); /* Host Data */ + ec->io_command = wilco_get_resource(pdev, 1); /* Host Command */ + ec->io_packet = wilco_get_resource(pdev, 2); /* MEC EMI */ + if (!ec->io_data || !ec->io_command || !ec->io_packet) + return -ENODEV; + + /* Initialize cros_ec register interface for communication */ + cros_ec_lpc_mec_init(ec->io_packet->start, + ec->io_packet->start + EC_MAILBOX_DATA_SIZE); + + /* + * Register a child device that will be found by the debugfs driver. + * Ignore failure. + */ + ec->debugfs_pdev = platform_device_register_data(dev, + "wilco-ec-debugfs", + PLATFORM_DEVID_AUTO, + NULL, 0); + + /* Register a child device that will be found by the RTC driver. */ + ec->rtc_pdev = platform_device_register_data(dev, "rtc-wilco-ec", + PLATFORM_DEVID_AUTO, + NULL, 0); + if (IS_ERR(ec->rtc_pdev)) { + dev_err(dev, "Failed to create RTC platform device\n"); + ret = PTR_ERR(ec->rtc_pdev); + goto unregister_debugfs; + } + + return 0; + +unregister_debugfs: + if (ec->debugfs_pdev) + platform_device_unregister(ec->debugfs_pdev); + cros_ec_lpc_mec_destroy(); + return ret; +} + +static int wilco_ec_remove(struct platform_device *pdev) +{ + struct wilco_ec_device *ec = platform_get_drvdata(pdev); + + platform_device_unregister(ec->rtc_pdev); + if (ec->debugfs_pdev) + platform_device_unregister(ec->debugfs_pdev); + + /* Teardown cros_ec interface */ + cros_ec_lpc_mec_destroy(); + + return 0; +} + +static const struct acpi_device_id wilco_ec_acpi_device_ids[] = { + { "GOOG000C", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids); + +static struct platform_driver wilco_ec_driver = { + .driver = { + .name = DRV_NAME, + .acpi_match_table = wilco_ec_acpi_device_ids, + }, + .probe = wilco_ec_probe, + .remove = wilco_ec_remove, +}; + +module_platform_driver(wilco_ec_driver); + +MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); +MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/platform/chrome/wilco_ec/debugfs.c b/drivers/platform/chrome/wilco_ec/debugfs.c new file mode 100644 index 000000000000..c090db2cd5be --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/debugfs.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * debugfs attributes for Wilco EC + * + * Copyright 2019 Google LLC + * + * There is only one attribute used for debugging, called raw. + * You can write a hexadecimal sentence to raw, and that series of bytes + * will be sent to the EC. Then, you can read the bytes of response + * by reading from raw. + * + * For writing: + * Bytes 0-1 indicate the message type: + * 00 F0 = Execute Legacy Command + * 00 F2 = Read/Write NVRAM Property + * Byte 2 provides the command code + * Bytes 3+ consist of the data passed in the request + * + * When referencing the EC interface spec, byte 2 corresponds to MBOX[0], + * byte 3 corresponds to MBOX[1], etc. + * + * At least three bytes are required, for the msg type and command, + * with additional bytes optional for additional data. + * + * Example: + * // Request EC info type 3 (EC firmware build date) + * $ echo 00 f0 38 00 03 00 > raw + * // View the result. The decoded ASCII result "12/21/18" is + * // included after the raw hex. + * $ cat raw + * 00 31 32 2f 32 31 2f 31 38 00 38 00 01 00 2f 00 .12/21/18.8... + */ + +#include <linux/ctype.h> +#include <linux/debugfs.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/platform_data/wilco-ec.h> +#include <linux/platform_device.h> + +#define DRV_NAME "wilco-ec-debugfs" + +/* The 256 raw bytes will take up more space when represented as a hex string */ +#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE_EXTENDED * 4) + +struct wilco_ec_debugfs { + struct wilco_ec_device *ec; + struct dentry *dir; + size_t response_size; + u8 raw_data[EC_MAILBOX_DATA_SIZE_EXTENDED]; + u8 formatted_data[FORMATTED_BUFFER_SIZE]; +}; +static struct wilco_ec_debugfs *debug_info; + +/** + * parse_hex_sentence() - Convert a ascii hex representation into byte array. + * @in: Input buffer of ascii. + * @isize: Length of input buffer. + * @out: Output buffer. + * @osize: Length of output buffer, e.g. max number of bytes to parse. + * + * An valid input is a series of ascii hexadecimal numbers, separated by spaces. + * An example valid input is + * " 00 f2 0 000076 6 0 ff" + * + * If an individual "word" within the hex sentence is longer than MAX_WORD_SIZE, + * then the sentence is illegal, and parsing will fail. + * + * Return: Number of bytes parsed, or negative error code on failure. + */ +static int parse_hex_sentence(const char *in, int isize, u8 *out, int osize) +{ + int n_parsed = 0; + int word_start = 0; + int word_end; + int word_len; + /* Temp buffer for holding a "word" of chars that represents one byte */ + #define MAX_WORD_SIZE 16 + char tmp[MAX_WORD_SIZE + 1]; + u8 byte; + + while (word_start < isize && n_parsed < osize) { + /* Find the start of the next word */ + while (word_start < isize && isspace(in[word_start])) + word_start++; + /* reached the end of the input before next word? */ + if (word_start >= isize) + break; + + /* Find the end of this word */ + word_end = word_start; + while (word_end < isize && !isspace(in[word_end])) + word_end++; + + /* Copy to a tmp NULL terminated string */ + word_len = word_end - word_start; + if (word_len > MAX_WORD_SIZE) + return -EINVAL; + memcpy(tmp, in + word_start, word_len); + tmp[word_len] = '\0'; + + /* + * Convert from hex string, place in output. If fails to parse, + * just return -EINVAL because specific error code is only + * relevant for this one word, returning it would be confusing. + */ + if (kstrtou8(tmp, 16, &byte)) + return -EINVAL; + out[n_parsed++] = byte; + + word_start = word_end; + } + return n_parsed; +} + +/* The message type takes up two bytes*/ +#define TYPE_AND_DATA_SIZE ((EC_MAILBOX_DATA_SIZE) + 2) + +static ssize_t raw_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buf = debug_info->formatted_data; + struct wilco_ec_message msg; + u8 request_data[TYPE_AND_DATA_SIZE]; + ssize_t kcount; + int ret; + + if (count > FORMATTED_BUFFER_SIZE) + return -EINVAL; + + kcount = simple_write_to_buffer(buf, FORMATTED_BUFFER_SIZE, ppos, + user_buf, count); + if (kcount < 0) + return kcount; + + ret = parse_hex_sentence(buf, kcount, request_data, TYPE_AND_DATA_SIZE); + if (ret < 0) + return ret; + /* Need at least two bytes for message type and one for command */ + if (ret < 3) + return -EINVAL; + + /* Clear response data buffer */ + memset(debug_info->raw_data, '\0', EC_MAILBOX_DATA_SIZE_EXTENDED); + + msg.type = request_data[0] << 8 | request_data[1]; + msg.flags = WILCO_EC_FLAG_RAW; + msg.command = request_data[2]; + msg.request_data = ret > 3 ? request_data + 3 : 0; + msg.request_size = ret - 3; + msg.response_data = debug_info->raw_data; + msg.response_size = EC_MAILBOX_DATA_SIZE; + + /* Telemetry commands use extended response data */ + if (msg.type == WILCO_EC_MSG_TELEMETRY_LONG) { + msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA; + msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED; + } + + ret = wilco_ec_mailbox(debug_info->ec, &msg); + if (ret < 0) + return ret; + debug_info->response_size = ret; + + return count; +} + +static ssize_t raw_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + int fmt_len = 0; + + if (debug_info->response_size) { + fmt_len = hex_dump_to_buffer(debug_info->raw_data, + debug_info->response_size, + 16, 1, debug_info->formatted_data, + FORMATTED_BUFFER_SIZE, true); + /* Only return response the first time it is read */ + debug_info->response_size = 0; + } + + return simple_read_from_buffer(user_buf, count, ppos, + debug_info->formatted_data, fmt_len); +} + +static const struct file_operations fops_raw = { + .owner = THIS_MODULE, + .read = raw_read, + .write = raw_write, + .llseek = no_llseek, +}; + +/** + * wilco_ec_debugfs_probe() - Create the debugfs node + * @pdev: The platform device, probably created in core.c + * + * Try to create a debugfs node. If it fails, then we don't want to change + * behavior at all, this is for debugging after all. Just fail silently. + * + * Return: 0 always. + */ +static int wilco_ec_debugfs_probe(struct platform_device *pdev) +{ + struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + + debug_info = devm_kzalloc(&pdev->dev, sizeof(*debug_info), GFP_KERNEL); + if (!debug_info) + return 0; + debug_info->ec = ec; + debug_info->dir = debugfs_create_dir("wilco_ec", NULL); + if (!debug_info->dir) + return 0; + debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw); + + return 0; +} + +static int wilco_ec_debugfs_remove(struct platform_device *pdev) +{ + debugfs_remove_recursive(debug_info->dir); + + return 0; +} + +static struct platform_driver wilco_ec_debugfs_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = wilco_ec_debugfs_probe, + .remove = wilco_ec_debugfs_remove, +}; + +module_platform_driver(wilco_ec_debugfs_driver); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Wilco EC debugfs driver"); diff --git a/drivers/platform/chrome/wilco_ec/mailbox.c b/drivers/platform/chrome/wilco_ec/mailbox.c new file mode 100644 index 000000000000..14355668ddfa --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/mailbox.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mailbox interface for Wilco Embedded Controller + * + * Copyright 2018 Google LLC + * + * The Wilco EC is similar to a typical ChromeOS embedded controller. + * It uses the same MEC based low-level communication and a similar + * protocol, but with some important differences. The EC firmware does + * not support the same mailbox commands so it is not registered as a + * cros_ec device type. + * + * Most messages follow a standard format, but there are some exceptions + * and an interface is provided to do direct/raw transactions that do not + * make assumptions about byte placement. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/platform_data/wilco-ec.h> +#include <linux/platform_device.h> + +#include "../cros_ec_lpc_mec.h" + +/* Version of mailbox interface */ +#define EC_MAILBOX_VERSION 0 + +/* Command to start mailbox transaction */ +#define EC_MAILBOX_START_COMMAND 0xda + +/* Version of EC protocol */ +#define EC_MAILBOX_PROTO_VERSION 3 + +/* Number of header bytes to be counted as data bytes */ +#define EC_MAILBOX_DATA_EXTRA 2 + +/* Maximum timeout */ +#define EC_MAILBOX_TIMEOUT HZ + +/* EC response flags */ +#define EC_CMDR_DATA BIT(0) /* Data ready for host to read */ +#define EC_CMDR_PENDING BIT(1) /* Write pending to EC */ +#define EC_CMDR_BUSY BIT(2) /* EC is busy processing a command */ +#define EC_CMDR_CMD BIT(3) /* Last host write was a command */ + +/** + * wilco_ec_response_timed_out() - Wait for EC response. + * @ec: EC device. + * + * Return: true if EC timed out, false if EC did not time out. + */ +static bool wilco_ec_response_timed_out(struct wilco_ec_device *ec) +{ + unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT; + + do { + if (!(inb(ec->io_command->start) & + (EC_CMDR_PENDING | EC_CMDR_BUSY))) + return false; + usleep_range(100, 200); + } while (time_before(jiffies, timeout)); + + return true; +} + +/** + * wilco_ec_checksum() - Compute 8-bit checksum over data range. + * @data: Data to checksum. + * @size: Number of bytes to checksum. + * + * Return: 8-bit checksum of provided data. + */ +static u8 wilco_ec_checksum(const void *data, size_t size) +{ + u8 *data_bytes = (u8 *)data; + u8 checksum = 0; + size_t i; + + for (i = 0; i < size; i++) + checksum += data_bytes[i]; + + return checksum; +} + +/** + * wilco_ec_prepare() - Prepare the request structure for the EC. + * @msg: EC message with request information. + * @rq: EC request structure to fill. + */ +static void wilco_ec_prepare(struct wilco_ec_message *msg, + struct wilco_ec_request *rq) +{ + memset(rq, 0, sizeof(*rq)); + + /* Handle messages without trimming bytes from the request */ + if (msg->request_size && msg->flags & WILCO_EC_FLAG_RAW_REQUEST) { + rq->reserved_raw = *(u8 *)msg->request_data; + msg->request_size--; + memmove(msg->request_data, msg->request_data + 1, + msg->request_size); + } + + /* Fill in request packet */ + rq->struct_version = EC_MAILBOX_PROTO_VERSION; + rq->mailbox_id = msg->type; + rq->mailbox_version = EC_MAILBOX_VERSION; + rq->data_size = msg->request_size + EC_MAILBOX_DATA_EXTRA; + rq->command = msg->command; + + /* Checksum header and data */ + rq->checksum = wilco_ec_checksum(rq, sizeof(*rq)); + rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size); + rq->checksum = -rq->checksum; +} + +/** + * wilco_ec_transfer() - Perform actual data transfer. + * @ec: EC device. + * @msg: EC message data for request and response. + * @rq: Filled in request structure + * + * Context: ec->mailbox_lock should be held while using this function. + * Return: number of bytes received or negative error code on failure. + */ +static int wilco_ec_transfer(struct wilco_ec_device *ec, + struct wilco_ec_message *msg, + struct wilco_ec_request *rq) +{ + struct wilco_ec_response *rs; + u8 checksum; + u8 flag; + size_t size; + + /* Write request header, then data */ + cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq); + cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size, + msg->request_data); + + /* Start the command */ + outb(EC_MAILBOX_START_COMMAND, ec->io_command->start); + + /* For some commands (eg shutdown) the EC will not respond, that's OK */ + if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) { + dev_dbg(ec->dev, "EC does not respond to this command\n"); + return 0; + } + + /* Wait for it to complete */ + if (wilco_ec_response_timed_out(ec)) { + dev_dbg(ec->dev, "response timed out\n"); + return -ETIMEDOUT; + } + + /* Check result */ + flag = inb(ec->io_data->start); + if (flag) { + dev_dbg(ec->dev, "bad response: 0x%02x\n", flag); + return -EIO; + } + + if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA) + size = EC_MAILBOX_DATA_SIZE_EXTENDED; + else + size = EC_MAILBOX_DATA_SIZE; + + /* Read back response */ + rs = ec->data_buffer; + checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0, + sizeof(*rs) + size, (u8 *)rs); + if (checksum) { + dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum); + return -EBADMSG; + } + + /* Check that the EC reported success */ + msg->result = rs->result; + if (msg->result) { + dev_dbg(ec->dev, "bad response: 0x%02x\n", msg->result); + return -EBADMSG; + } + + /* Check the returned data size, skipping the header */ + if (rs->data_size != size) { + dev_dbg(ec->dev, "unexpected packet size (%u != %zu)", + rs->data_size, size); + return -EMSGSIZE; + } + + /* Skip 1 response data byte unless specified */ + size = (msg->flags & WILCO_EC_FLAG_RAW_RESPONSE) ? 0 : 1; + if ((ssize_t) rs->data_size - size < msg->response_size) { + dev_dbg(ec->dev, "response data too short (%zd < %zu)", + (ssize_t) rs->data_size - size, msg->response_size); + return -EMSGSIZE; + } + + /* Ignore response data bytes as requested */ + memcpy(msg->response_data, rs->data + size, msg->response_size); + + /* Return actual amount of data received */ + return msg->response_size; +} + +/** + * wilco_ec_mailbox() - Send EC request and receive EC response. + * @ec: EC device. + * @msg: EC message data for request and response. + * + * On entry msg->type, msg->flags, msg->command, msg->request_size, + * msg->response_size, and msg->request_data should all be filled in. + * + * On exit msg->result and msg->response_data will be filled. + * + * Return: number of bytes received or negative error code on failure. + */ +int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg) +{ + struct wilco_ec_request *rq; + int ret; + + dev_dbg(ec->dev, "cmd=%02x type=%04x flags=%02x rslen=%zu rqlen=%zu\n", + msg->command, msg->type, msg->flags, msg->response_size, + msg->request_size); + + mutex_lock(&ec->mailbox_lock); + /* Prepare request packet */ + rq = ec->data_buffer; + wilco_ec_prepare(msg, rq); + + ret = wilco_ec_transfer(ec, msg, rq); + mutex_unlock(&ec->mailbox_lock); + + return ret; + +} +EXPORT_SYMBOL_GPL(wilco_ec_mailbox); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a8f47df0655a..54f8238aac0d 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -192,14 +192,23 @@ config PWM_IMG To compile this driver as a module, choose M here: the module will be called pwm-img -config PWM_IMX - tristate "i.MX PWM support" +config PWM_IMX1 + tristate "i.MX1 PWM support" depends on ARCH_MXC help - Generic PWM framework driver for i.MX. + Generic PWM framework driver for i.MX1 and i.MX21 To compile this driver as a module, choose M here: the module - will be called pwm-imx. + will be called pwm-imx1. + +config PWM_IMX27 + tristate "i.MX27 PWM support" + depends on ARCH_MXC + help + Generic PWM framework driver for i.MX27 and later i.MX SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-imx27. config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 9c676a0dadf5..448825e892bc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -17,7 +17,8 @@ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o obj-$(CONFIG_PWM_IMG) += pwm-img.o -obj-$(CONFIG_PWM_IMX) += pwm-imx.o +obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o +obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1581f6ab1b1f..3149204567f3 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -472,7 +472,10 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) state->duty_cycle > state->period) return -EINVAL; - if (!memcmp(state, &pwm->state, sizeof(*state))) + if (state->period == pwm->state.period && + state->duty_cycle == pwm->state.duty_cycle && + state->polarity == pwm->state.polarity && + state->enabled == pwm->state.enabled) return 0; if (pwm->chip->ops->apply) { @@ -1033,10 +1036,7 @@ static int pwm_seq_show(struct seq_file *s, void *v) dev_name(chip->dev), chip->npwm, (chip->npwm != 1) ? "s" : ""); - if (chip->ops->dbg_show) - chip->ops->dbg_show(chip, s); - else - pwm_dbg_show(chip, s); + pwm_dbg_show(chip, s); return 0; } diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 530d7dc5f1b5..a9fd6f0d408c 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -48,16 +48,6 @@ #define PWMV2_CPRD 0x0C #define PWMV2_CPRDUPD 0x10 -/* - * Max value for duty and period - * - * Although the duty and period register is 32 bit, - * however only the LSB 16 bits are significant. - */ -#define PWM_MAX_DTY 0xFFFF -#define PWM_MAX_PRD 0xFFFF -#define PRD_MAX_PRES 10 - struct atmel_pwm_registers { u8 period; u8 period_upd; @@ -65,11 +55,21 @@ struct atmel_pwm_registers { u8 duty_upd; }; +struct atmel_pwm_config { + u32 max_period; + u32 max_pres; +}; + +struct atmel_pwm_data { + struct atmel_pwm_registers regs; + struct atmel_pwm_config cfg; +}; + struct atmel_pwm_chip { struct pwm_chip chip; struct clk *clk; void __iomem *base; - const struct atmel_pwm_registers *regs; + const struct atmel_pwm_data *data; unsigned int updated_pwms; /* ISR is cleared when read, ensure only one thread does that */ @@ -121,10 +121,10 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, cycles *= clk_get_rate(atmel_pwm->clk); do_div(cycles, NSEC_PER_SEC); - for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1) + for (*pres = 0; cycles > atmel_pwm->data->cfg.max_period; cycles >>= 1) (*pres)++; - if (*pres > PRD_MAX_PRES) { + if (*pres > atmel_pwm->data->cfg.max_pres) { dev_err(chip->dev, "pres exceeds the maximum value\n"); return -EINVAL; } @@ -150,15 +150,15 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm, struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); u32 val; - if (atmel_pwm->regs->duty_upd == - atmel_pwm->regs->period_upd) { + if (atmel_pwm->data->regs.duty_upd == + atmel_pwm->data->regs.period_upd) { val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR); val &= ~PWM_CMR_UPD_CDTY; atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val); } atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->duty_upd, cdty); + atmel_pwm->data->regs.duty_upd, cdty); } static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip, @@ -168,9 +168,9 @@ static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip, struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->duty, cdty); + atmel_pwm->data->regs.duty, cdty); atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->period, cprd); + atmel_pwm->data->regs.period, cprd); } static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, @@ -225,7 +225,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, cstate.polarity == state->polarity && cstate.period == state->period) { cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->period); + atmel_pwm->data->regs.period); atmel_pwm_calculate_cdty(state, cprd, &cdty); atmel_pwm_update_cdty(chip, pwm, cdty); return 0; @@ -277,27 +277,55 @@ static const struct pwm_ops atmel_pwm_ops = { .owner = THIS_MODULE, }; -static const struct atmel_pwm_registers atmel_pwm_regs_v1 = { - .period = PWMV1_CPRD, - .period_upd = PWMV1_CUPD, - .duty = PWMV1_CDTY, - .duty_upd = PWMV1_CUPD, +static const struct atmel_pwm_data atmel_sam9rl_pwm_data = { + .regs = { + .period = PWMV1_CPRD, + .period_upd = PWMV1_CUPD, + .duty = PWMV1_CDTY, + .duty_upd = PWMV1_CUPD, + }, + .cfg = { + /* 16 bits to keep period and duty. */ + .max_period = 0xffff, + .max_pres = 10, + }, +}; + +static const struct atmel_pwm_data atmel_sama5_pwm_data = { + .regs = { + .period = PWMV2_CPRD, + .period_upd = PWMV2_CPRDUPD, + .duty = PWMV2_CDTY, + .duty_upd = PWMV2_CDTYUPD, + }, + .cfg = { + /* 16 bits to keep period and duty. */ + .max_period = 0xffff, + .max_pres = 10, + }, }; -static const struct atmel_pwm_registers atmel_pwm_regs_v2 = { - .period = PWMV2_CPRD, - .period_upd = PWMV2_CPRDUPD, - .duty = PWMV2_CDTY, - .duty_upd = PWMV2_CDTYUPD, +static const struct atmel_pwm_data mchp_sam9x60_pwm_data = { + .regs = { + .period = PWMV1_CPRD, + .period_upd = PWMV1_CUPD, + .duty = PWMV1_CDTY, + .duty_upd = PWMV1_CUPD, + }, + .cfg = { + /* 32 bits to keep period and duty. */ + .max_period = 0xffffffff, + .max_pres = 10, + }, }; static const struct platform_device_id atmel_pwm_devtypes[] = { { .name = "at91sam9rl-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1, + .driver_data = (kernel_ulong_t)&atmel_sam9rl_pwm_data, }, { .name = "sama5d3-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2, + .driver_data = (kernel_ulong_t)&atmel_sama5_pwm_data, }, { /* sentinel */ }, @@ -307,20 +335,23 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes); static const struct of_device_id atmel_pwm_dt_ids[] = { { .compatible = "atmel,at91sam9rl-pwm", - .data = &atmel_pwm_regs_v1, + .data = &atmel_sam9rl_pwm_data, }, { .compatible = "atmel,sama5d3-pwm", - .data = &atmel_pwm_regs_v2, + .data = &atmel_sama5_pwm_data, }, { .compatible = "atmel,sama5d2-pwm", - .data = &atmel_pwm_regs_v2, + .data = &atmel_sama5_pwm_data, + }, { + .compatible = "microchip,sam9x60-pwm", + .data = &mchp_sam9x60_pwm_data, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids); -static inline const struct atmel_pwm_registers * +static inline const struct atmel_pwm_data * atmel_pwm_get_driver_data(struct platform_device *pdev) { const struct platform_device_id *id; @@ -330,18 +361,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev) id = platform_get_device_id(pdev); - return (struct atmel_pwm_registers *)id->driver_data; + return (struct atmel_pwm_data *)id->driver_data; } static int atmel_pwm_probe(struct platform_device *pdev) { - const struct atmel_pwm_registers *regs; + const struct atmel_pwm_data *data; struct atmel_pwm_chip *atmel_pwm; struct resource *res; int ret; - regs = atmel_pwm_get_driver_data(pdev); - if (!regs) + data = atmel_pwm_get_driver_data(pdev); + if (!data) return -ENODEV; atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL); @@ -373,7 +404,7 @@ static int atmel_pwm_probe(struct platform_device *pdev) atmel_pwm->chip.base = -1; atmel_pwm->chip.npwm = 4; - atmel_pwm->regs = regs; + atmel_pwm->data = data; atmel_pwm->updated_pwms = 0; mutex_init(&atmel_pwm->isr_lock); diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 09a95aeb3a70..81da91df2529 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -45,25 +45,25 @@ * high or low depending on its state at that exact instant. */ -#define PWM_CONTROL_OFFSET (0x00000000) +#define PWM_CONTROL_OFFSET 0x00000000 #define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan)) #define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan)) #define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan)) #define PWM_CONTROL_TRIGGER_SHIFT(chan) (chan) -#define PRESCALE_OFFSET (0x00000004) +#define PRESCALE_OFFSET 0x00000004 #define PRESCALE_SHIFT(chan) ((chan) << 2) #define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan)) -#define PRESCALE_MIN (0x00000000) -#define PRESCALE_MAX (0x00000007) +#define PRESCALE_MIN 0x00000000 +#define PRESCALE_MAX 0x00000007 #define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3)) -#define PERIOD_COUNT_MIN (0x00000002) -#define PERIOD_COUNT_MAX (0x00ffffff) +#define PERIOD_COUNT_MIN 0x00000002 +#define PERIOD_COUNT_MAX 0x00ffffff #define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3)) -#define DUTY_CYCLE_HIGH_MIN (0x00000000) -#define DUTY_CYCLE_HIGH_MAX (0x00ffffff) +#define DUTY_CYCLE_HIGH_MIN 0x00000000 +#define DUTY_CYCLE_HIGH_MAX 0x00ffffff struct kona_pwmc { struct pwm_chip chip; diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index 27c107e78d59..a0b09603d13d 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -49,15 +49,30 @@ struct hibvt_pwm_chip { struct clk *clk; void __iomem *base; struct reset_control *rstc; + const struct hibvt_pwm_soc *soc; }; struct hibvt_pwm_soc { u32 num_pwms; + bool quirk_force_enable; }; -static const struct hibvt_pwm_soc pwm_soc[2] = { - { .num_pwms = 4 }, - { .num_pwms = 8 }, +static const struct hibvt_pwm_soc hi3516cv300_soc_info = { + .num_pwms = 4, +}; + +static const struct hibvt_pwm_soc hi3519v100_soc_info = { + .num_pwms = 8, +}; + +static const struct hibvt_pwm_soc hi3559v100_shub_soc_info = { + .num_pwms = 8, + .quirk_force_enable = true, +}; + +static const struct hibvt_pwm_soc hi3559v100_soc_info = { + .num_pwms = 2, + .quirk_force_enable = true, }; static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip) @@ -148,13 +163,23 @@ static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { + struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); + if (state->polarity != pwm->state.polarity) hibvt_pwm_set_polarity(chip, pwm, state->polarity); if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) + state->duty_cycle != pwm->state.duty_cycle) { hibvt_pwm_config(chip, pwm, state->duty_cycle, state->period); + /* + * Some implementations require the PWM to be enabled twice + * each time the duty cycle is refreshed. + */ + if (hi_pwm_chip->soc->quirk_force_enable && state->enabled) + hibvt_pwm_enable(chip, pwm); + } + if (state->enabled != pwm->state.enabled) { if (state->enabled) hibvt_pwm_enable(chip, pwm); @@ -198,6 +223,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev) pwm_chip->chip.npwm = soc->num_pwms; pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags; pwm_chip->chip.of_pwm_n_cells = 3; + pwm_chip->soc = soc; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pwm_chip->base = devm_ioremap_resource(&pdev->dev, res); @@ -250,8 +276,14 @@ static int hibvt_pwm_remove(struct platform_device *pdev) } static const struct of_device_id hibvt_pwm_of_match[] = { - { .compatible = "hisilicon,hi3516cv300-pwm", .data = &pwm_soc[0] }, - { .compatible = "hisilicon,hi3519v100-pwm", .data = &pwm_soc[1] }, + { .compatible = "hisilicon,hi3516cv300-pwm", + .data = &hi3516cv300_soc_info }, + { .compatible = "hisilicon,hi3519v100-pwm", + .data = &hi3519v100_soc_info }, + { .compatible = "hisilicon,hi3559v100-shub-pwm", + .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-imx1.c b/drivers/pwm/pwm-imx1.c new file mode 100644 index 000000000000..f8b2c2e001a7 --- /dev/null +++ b/drivers/pwm/pwm-imx1.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * simple driver for PWM (Pulse Width Modulator) controller + * + * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +#define MX1_PWMC 0x00 /* PWM Control Register */ +#define MX1_PWMS 0x04 /* PWM Sample Register */ +#define MX1_PWMP 0x08 /* PWM Period Register */ + +#define MX1_PWMC_EN BIT(4) + +struct pwm_imx1_chip { + struct clk *clk_ipg; + struct clk *clk_per; + void __iomem *mmio_base; + struct pwm_chip chip; +}; + +#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip) + +static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + int ret; + + ret = clk_prepare_enable(imx->clk_ipg); + if (ret) + return ret; + + ret = clk_prepare_enable(imx->clk_per); + if (ret) { + clk_disable_unprepare(imx->clk_ipg); + return ret; + } + + return 0; +} + +static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + + clk_disable_unprepare(imx->clk_per); + clk_disable_unprepare(imx->clk_ipg); +} + +static int pwm_imx1_config(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 max, p; + + /* + * The PWM subsystem allows for exact frequencies. However, + * I cannot connect a scope on my device to the PWM line and + * thus cannot provide the program the PWM controller + * exactly. Instead, I'm relying on the fact that the + * Bootloader (u-boot or WinCE+haret) has programmed the PWM + * function group already. So I'll just modify the PWM sample + * register to follow the ratio of duty_ns vs. period_ns + * accordingly. + * + * This is good enough for programming the brightness of + * the LCD backlight. + * + * The real implementation would divide PERCLK[0] first by + * both the prescaler (/1 .. /128) and then by CLKSEL + * (/2 .. /16). + */ + max = readl(imx->mmio_base + MX1_PWMP); + p = max * duty_ns / period_ns; + + writel(max - p, imx->mmio_base + MX1_PWMS); + + return 0; +} + +static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + int ret; + + ret = pwm_imx1_clk_prepare_enable(chip); + if (ret < 0) + return ret; + + value = readl(imx->mmio_base + MX1_PWMC); + value |= MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + return 0; +} + +static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + + value = readl(imx->mmio_base + MX1_PWMC); + value &= ~MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + pwm_imx1_clk_disable_unprepare(chip); +} + +static const struct pwm_ops pwm_imx1_ops = { + .enable = pwm_imx1_enable, + .disable = pwm_imx1_disable, + .config = pwm_imx1_config, + .owner = THIS_MODULE, +}; + +static const struct of_device_id pwm_imx1_dt_ids[] = { + { .compatible = "fsl,imx1-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids); + +static int pwm_imx1_probe(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx; + struct resource *r; + + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); + if (!imx) + return -ENOMEM; + + platform_set_drvdata(pdev, imx); + + imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(imx->clk_ipg)) { + dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", + PTR_ERR(imx->clk_ipg)); + return PTR_ERR(imx->clk_ipg); + } + + imx->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(imx->clk_per)) { + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; + } + + imx->chip.ops = &pwm_imx1_ops; + imx->chip.dev = &pdev->dev; + imx->chip.base = -1; + imx->chip.npwm = 1; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(imx->mmio_base)) + return PTR_ERR(imx->mmio_base); + + return pwmchip_add(&imx->chip); +} + +static int pwm_imx1_remove(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx = platform_get_drvdata(pdev); + + pwm_imx1_clk_disable_unprepare(&imx->chip); + + return pwmchip_remove(&imx->chip); +} + +static struct platform_driver pwm_imx1_driver = { + .driver = { + .name = "pwm-imx1", + .of_match_table = pwm_imx1_dt_ids, + }, + .probe = pwm_imx1_probe, + .remove = pwm_imx1_remove, +}; +module_platform_driver(pwm_imx1_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx27.c index 55a3a363d5be..806130654211 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx27.c @@ -19,16 +19,6 @@ #include <linux/pwm.h> #include <linux/slab.h> -/* i.MX1 and i.MX21 share the same PWM function block: */ - -#define MX1_PWMC 0x00 /* PWM Control Register */ -#define MX1_PWMS 0x04 /* PWM Sample Register */ -#define MX1_PWMP 0x08 /* PWM Period Register */ - -#define MX1_PWMC_EN BIT(4) - -/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ - #define MX3_PWMCR 0x00 /* PWM Control Register */ #define MX3_PWMSR 0x04 /* PWM Status Register */ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ @@ -86,21 +76,18 @@ /* PWMPR register value of 0xffff has the same effect as 0xfffe */ #define MX3_PWMPR_MAX 0xfffe -struct imx_chip { +struct pwm_imx27_chip { struct clk *clk_ipg; - struct clk *clk_per; - void __iomem *mmio_base; - struct pwm_chip chip; }; -#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) +#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip) -static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip) +static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); int ret; ret = clk_prepare_enable(imx->clk_ipg); @@ -116,35 +103,32 @@ static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip) return 0; } -static void imx_pwm_clk_disable_unprepare(struct pwm_chip *chip) +static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); clk_disable_unprepare(imx->clk_per); clk_disable_unprepare(imx->clk_ipg); } -static void imx_pwm_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, struct pwm_state *state) +static void pwm_imx27_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, struct pwm_state *state) { - struct imx_chip *imx = to_imx_chip(chip); - u32 period, prescaler, pwm_clk, ret, val; + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); + u32 period, prescaler, pwm_clk, val; u64 tmp; + int ret; - ret = imx_pwm_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(chip); if (ret < 0) return; val = readl(imx->mmio_base + MX3_PWMCR); - if (val & MX3_PWMCR_EN) { + if (val & MX3_PWMCR_EN) state->enabled = true; - ret = imx_pwm_clk_prepare_enable(chip); - if (ret) - return; - } else { + else state->enabled = false; - } switch (FIELD_GET(MX3_PWMCR_POUTC, val)) { case MX3_PWMCR_POUTC_NORMAL: @@ -176,70 +160,13 @@ static void imx_pwm_get_state(struct pwm_chip *chip, state->duty_cycle = 0; } - imx_pwm_clk_disable_unprepare(chip); -} - -static int imx_pwm_config_v1(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns) -{ - struct imx_chip *imx = to_imx_chip(chip); - - /* - * The PWM subsystem allows for exact frequencies. However, - * I cannot connect a scope on my device to the PWM line and - * thus cannot provide the program the PWM controller - * exactly. Instead, I'm relying on the fact that the - * Bootloader (u-boot or WinCE+haret) has programmed the PWM - * function group already. So I'll just modify the PWM sample - * register to follow the ratio of duty_ns vs. period_ns - * accordingly. - * - * This is good enough for programming the brightness of - * the LCD backlight. - * - * The real implementation would divide PERCLK[0] first by - * both the prescaler (/1 .. /128) and then by CLKSEL - * (/2 .. /16). - */ - u32 max = readl(imx->mmio_base + MX1_PWMP); - u32 p = max * duty_ns / period_ns; - writel(max - p, imx->mmio_base + MX1_PWMS); - - return 0; -} - -static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - int ret; - - ret = imx_pwm_clk_prepare_enable(chip); - if (ret < 0) - return ret; - - val = readl(imx->mmio_base + MX1_PWMC); - val |= MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - return 0; -} - -static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - - val = readl(imx->mmio_base + MX1_PWMC); - val &= ~MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - imx_pwm_clk_disable_unprepare(chip); + if (!state->enabled) + pwm_imx27_clk_disable_unprepare(chip); } -static void imx_pwm_sw_reset(struct pwm_chip *chip) +static void pwm_imx27_sw_reset(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct device *dev = chip->dev; int wait_count = 0; u32 cr; @@ -255,10 +182,10 @@ static void imx_pwm_sw_reset(struct pwm_chip *chip) dev_warn(dev, "software reset timeout\n"); } -static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, - struct pwm_device *pwm) +static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, + struct pwm_device *pwm) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct device *dev = chip->dev; unsigned int period_ms; int fifoav; @@ -277,11 +204,11 @@ static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, } } -static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { unsigned long period_cycles, duty_cycles, prescale; - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct pwm_state cstate; unsigned long long c; int ret; @@ -318,13 +245,13 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, * enabled. */ if (cstate.enabled) { - imx_pwm_wait_fifo_slot(chip, pwm); + pwm_imx27_wait_fifo_slot(chip, pwm); } else { - ret = imx_pwm_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(chip); if (ret) return ret; - imx_pwm_sw_reset(chip); + pwm_imx27_sw_reset(chip); } writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); @@ -343,64 +270,35 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, } else if (cstate.enabled) { writel(0, imx->mmio_base + MX3_PWMCR); - imx_pwm_clk_disable_unprepare(chip); + pwm_imx27_clk_disable_unprepare(chip); } return 0; } -static const struct pwm_ops imx_pwm_ops_v1 = { - .enable = imx_pwm_enable_v1, - .disable = imx_pwm_disable_v1, - .config = imx_pwm_config_v1, +static const struct pwm_ops pwm_imx27_ops = { + .apply = pwm_imx27_apply, + .get_state = pwm_imx27_get_state, .owner = THIS_MODULE, }; -static const struct pwm_ops imx_pwm_ops_v2 = { - .apply = imx_pwm_apply_v2, - .get_state = imx_pwm_get_state, - .owner = THIS_MODULE, -}; - -struct imx_pwm_data { - bool polarity_supported; - const struct pwm_ops *ops; -}; - -static struct imx_pwm_data imx_pwm_data_v1 = { - .ops = &imx_pwm_ops_v1, -}; - -static struct imx_pwm_data imx_pwm_data_v2 = { - .polarity_supported = true, - .ops = &imx_pwm_ops_v2, -}; - -static const struct of_device_id imx_pwm_dt_ids[] = { - { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, - { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, +static const struct of_device_id pwm_imx27_dt_ids[] = { + { .compatible = "fsl,imx27-pwm", }, { /* sentinel */ } }; -MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids); +MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); -static int imx_pwm_probe(struct platform_device *pdev) +static int pwm_imx27_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(imx_pwm_dt_ids, &pdev->dev); - const struct imx_pwm_data *data; - struct imx_chip *imx; + struct pwm_imx27_chip *imx; struct resource *r; - int ret = 0; - - if (!of_id) - return -ENODEV; - - data = of_id->data; imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (imx == NULL) return -ENOMEM; + platform_set_drvdata(pdev, imx); + imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx->clk_ipg)) { dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", @@ -410,57 +308,51 @@ static int imx_pwm_probe(struct platform_device *pdev) imx->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(imx->clk_per)) { - dev_err(&pdev->dev, "getting per clock failed with %ld\n", - PTR_ERR(imx->clk_per)); - return PTR_ERR(imx->clk_per); + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; } - imx->chip.ops = data->ops; + imx->chip.ops = &pwm_imx27_ops; imx->chip.dev = &pdev->dev; imx->chip.base = -1; imx->chip.npwm = 1; - if (data->polarity_supported) { - dev_dbg(&pdev->dev, "PWM supports output inversion\n"); - imx->chip.of_xlate = of_pwm_xlate_with_flags; - imx->chip.of_pwm_n_cells = 3; - } + imx->chip.of_xlate = of_pwm_xlate_with_flags; + imx->chip.of_pwm_n_cells = 3; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); - ret = pwmchip_add(&imx->chip); - if (ret < 0) - return ret; - - platform_set_drvdata(pdev, imx); - return 0; + return pwmchip_add(&imx->chip); } -static int imx_pwm_remove(struct platform_device *pdev) +static int pwm_imx27_remove(struct platform_device *pdev) { - struct imx_chip *imx; + struct pwm_imx27_chip *imx; imx = platform_get_drvdata(pdev); - if (imx == NULL) - return -ENODEV; - imx_pwm_clk_disable_unprepare(&imx->chip); + pwm_imx27_clk_disable_unprepare(&imx->chip); return pwmchip_remove(&imx->chip); } static struct platform_driver imx_pwm_driver = { - .driver = { - .name = "imx-pwm", - .of_match_table = imx_pwm_dt_ids, + .driver = { + .name = "pwm-imx27", + .of_match_table = pwm_imx27_dt_ids, }, - .probe = imx_pwm_probe, - .remove = imx_pwm_remove, + .probe = pwm_imx27_probe, + .remove = pwm_imx27_remove, }; - module_platform_driver(imx_pwm_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 893940d45f0d..15803c71fe80 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -277,10 +277,21 @@ static const struct mtk_pwm_data mt8173_pwm_data = { .commit_mask = 0x1, }; +static const struct mtk_pwm_data mt8183_pwm_data = { + .enable_mask = BIT(0), + .con0 = 0x18, + .con0_sel = 0x0, + .con1 = 0x1c, + .has_commit = false, + .bls_debug = 0x80, + .bls_debug_mask = 0x3, +}; + 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}, { } }; MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match); diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index a41812fc6f95..cfe7dd1b448e 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -8,6 +8,8 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/log2.h> +#include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -68,19 +70,15 @@ static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data, static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns) { unsigned long clk_rate = clk_get_rate(rp->clk); - unsigned long long max; /* max cycle / nanoseconds */ - unsigned int div; + u64 div, tmp; if (clk_rate == 0) return -EINVAL; - for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) { - max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE * - (1 << div); - do_div(max, clk_rate); - if (period_ns <= max) - break; - } + div = (u64)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE; + tmp = (u64)period_ns * clk_rate + div - 1; + tmp = div64_u64(tmp, div); + div = ilog2(tmp - 1) + 1; return (div <= RCAR_PWM_MAX_DIVISION) ? div : -ERANGE; } @@ -139,39 +137,8 @@ static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) pm_runtime_put(chip->dev); } -static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +static int rcar_pwm_enable(struct rcar_pwm_chip *rp) { - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); - int div, ret; - - div = rcar_pwm_get_clock_division(rp, period_ns); - if (div < 0) - return div; - - /* - * Let the core driver set pwm->period if disabled and duty_ns == 0. - * But, this driver should prevent to set the new duty_ns if current - * duty_cycle is not set - */ - if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle) - return 0; - - rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); - - ret = rcar_pwm_set_counter(rp, div, duty_ns, period_ns); - if (!ret) - rcar_pwm_set_clock_control(rp, div); - - /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */ - rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR); - - return ret; -} - -static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); u32 value; /* Don't enable the PWM device if CYC0 or PH0 is 0 */ @@ -185,19 +152,51 @@ static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static void rcar_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static void rcar_pwm_disable(struct rcar_pwm_chip *rp) +{ + rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR); +} + +static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); + struct pwm_state cur_state; + int div, ret; - rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR); + /* This HW/driver only supports normal polarity */ + pwm_get_state(pwm, &cur_state); + if (state->polarity != PWM_POLARITY_NORMAL) + return -ENOTSUPP; + + if (!state->enabled) { + rcar_pwm_disable(rp); + return 0; + } + + div = rcar_pwm_get_clock_division(rp, state->period); + if (div < 0) + return div; + + rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); + + ret = rcar_pwm_set_counter(rp, div, state->duty_cycle, state->period); + if (!ret) + rcar_pwm_set_clock_control(rp, div); + + /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */ + rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR); + + if (!ret && state->enabled) + ret = rcar_pwm_enable(rp); + + return ret; } static const struct pwm_ops rcar_pwm_ops = { .request = rcar_pwm_request, .free = rcar_pwm_free, - .config = rcar_pwm_config, - .enable = rcar_pwm_enable, - .disable = rcar_pwm_disable, + .apply = rcar_pwm_apply, .owner = THIS_MODULE, }; @@ -279,18 +278,16 @@ static int rcar_pwm_suspend(struct device *dev) static int rcar_pwm_resume(struct device *dev) { struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev); + struct pwm_state state; if (!test_bit(PWMF_REQUESTED, &pwm->flags)) return 0; pm_runtime_get_sync(dev); - rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle, - pwm->state.period); - if (pwm_is_enabled(pwm)) - rcar_pwm_enable(pwm->chip, pwm); + pwm_get_state(pwm, &state); - return 0; + return rcar_pwm_apply(pwm->chip, pwm, &state); } #endif /* CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 79374d1de311..1f3ef9ee493c 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -48,7 +48,7 @@ /* list of clocks required by ADSP PIL */ static const char * const adsp_clk_id[] = { - "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", + "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", }; @@ -439,6 +439,10 @@ static int adsp_probe(struct platform_device *pdev) adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); + if (IS_ERR(adsp->sysmon)) { + ret = PTR_ERR(adsp->sysmon); + goto disable_pm; + } ret = rproc_add(rproc); if (ret) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 01be7314e176..eacdf10fcfaf 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -25,6 +25,8 @@ #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> @@ -131,6 +133,8 @@ struct rproc_hexagon_res { char **proxy_clk_names; char **reset_clk_names; char **active_clk_names; + char **active_pd_names; + char **proxy_pd_names; int version; bool need_mem_protection; bool has_alt_reset; @@ -156,9 +160,13 @@ struct q6v5 { struct clk *active_clks[8]; struct clk *reset_clks[4]; struct clk *proxy_clks[4]; + struct device *active_pds[1]; + struct device *proxy_pds[3]; int active_clk_count; int reset_clk_count; int proxy_clk_count; + int active_pd_count; + int proxy_pd_count; struct reg_info active_regs[1]; struct reg_info proxy_regs[3]; @@ -188,6 +196,7 @@ struct q6v5 { bool has_alt_reset; int mpss_perm; int mba_perm; + const char *hexagon_mdt_image; int version; }; @@ -321,6 +330,41 @@ static void q6v5_clk_disable(struct device *dev, clk_disable_unprepare(clks[i]); } +static int q6v5_pds_enable(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int ret; + int i; + + for (i = 0; i < pd_count; i++) { + dev_pm_genpd_set_performance_state(pds[i], INT_MAX); + ret = pm_runtime_get_sync(pds[i]); + if (ret < 0) + goto unroll_pd_votes; + } + + return 0; + +unroll_pd_votes: + for (i--; i >= 0; i--) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } + + return ret; +}; + +static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int i; + + for (i = 0; i < pd_count; i++) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } +} + static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, bool remote_owner, phys_addr_t addr, size_t size) @@ -690,11 +734,23 @@ static int q6v5_mba_load(struct q6v5 *qproc) qcom_q6v5_prepare(&qproc->q6v5); + ret = q6v5_pds_enable(qproc, qproc->active_pds, qproc->active_pd_count); + if (ret < 0) { + dev_err(qproc->dev, "failed to enable active power domains\n"); + goto disable_irqs; + } + + ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); + if (ret < 0) { + dev_err(qproc->dev, "failed to enable proxy power domains\n"); + goto disable_active_pds; + } + ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); if (ret) { dev_err(qproc->dev, "failed to enable proxy supplies\n"); - goto disable_irqs; + goto disable_proxy_pds; } ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, @@ -791,6 +847,10 @@ disable_proxy_clk: disable_proxy_reg: q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); +disable_proxy_pds: + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +disable_active_pds: + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); disable_irqs: qcom_q6v5_unprepare(&qproc->q6v5); @@ -830,6 +890,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) qproc->active_clk_count); q6v5_regulator_disable(qproc, qproc->active_regs, qproc->active_reg_count); + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); /* In case of failure or coredump scenario where reclaiming MBA memory * could not happen reclaim it here. @@ -841,6 +902,8 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) ret = qcom_q6v5_unprepare(&qproc->q6v5); if (ret) { + q6v5_pds_disable(qproc, qproc->proxy_pds, + qproc->proxy_pd_count); q6v5_clk_disable(qproc->dev, qproc->proxy_clks, qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, @@ -860,17 +923,26 @@ static int q6v5_mpss_load(struct q6v5 *qproc) phys_addr_t min_addr = PHYS_ADDR_MAX; phys_addr_t max_addr = 0; bool relocate = false; - char seg_name[10]; + char *fw_name; + size_t fw_name_len; ssize_t offset; size_t size = 0; void *ptr; int ret; int i; - ret = request_firmware(&fw, "modem.mdt", qproc->dev); + fw_name_len = strlen(qproc->hexagon_mdt_image); + if (fw_name_len <= 4) + return -EINVAL; + + fw_name = kstrdup(qproc->hexagon_mdt_image, GFP_KERNEL); + if (!fw_name) + return -ENOMEM; + + ret = request_firmware(&fw, fw_name, qproc->dev); if (ret < 0) { - dev_err(qproc->dev, "unable to load modem.mdt\n"); - return ret; + dev_err(qproc->dev, "unable to load %s\n", fw_name); + goto out; } /* Initialize the RMB validator */ @@ -918,10 +990,11 @@ static int q6v5_mpss_load(struct q6v5 *qproc) ptr = qproc->mpss_region + offset; if (phdr->p_filesz) { - snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i); - ret = request_firmware(&seg_fw, seg_name, qproc->dev); + /* Replace "xxx.xxx" with "xxx.bxx" */ + sprintf(fw_name + fw_name_len - 3, "b%02d", i); + ret = request_firmware(&seg_fw, fw_name, qproc->dev); if (ret) { - dev_err(qproc->dev, "failed to load %s\n", seg_name); + dev_err(qproc->dev, "failed to load %s\n", fw_name); goto release_firmware; } @@ -960,6 +1033,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc) release_firmware: release_firmware(fw); +out: + kfree(fw_name); return ret < 0 ? ret : 0; } @@ -1075,9 +1150,10 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, unsigned long i; int ret; - ret = request_firmware(&fw, "modem.mdt", qproc->dev); + ret = request_firmware(&fw, qproc->hexagon_mdt_image, qproc->dev); if (ret < 0) { - dev_err(qproc->dev, "unable to load modem.mdt\n"); + dev_err(qproc->dev, "unable to load %s\n", + qproc->hexagon_mdt_image); return ret; } @@ -1121,6 +1197,7 @@ static void qcom_msa_handover(struct qcom_q6v5 *q6v5) qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); } static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) @@ -1181,6 +1258,45 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks, return i; } +static int q6v5_pds_attach(struct device *dev, struct device **devs, + char **pd_names) +{ + size_t num_pds = 0; + int ret; + int i; + + if (!pd_names) + return 0; + + while (pd_names[num_pds]) + num_pds++; + + for (i = 0; i < num_pds; i++) { + devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]); + if (IS_ERR(devs[i])) { + ret = PTR_ERR(devs[i]); + goto unroll_attach; + } + } + + return num_pds; + +unroll_attach: + for (i--; i >= 0; i--) + dev_pm_domain_detach(devs[i], false); + + return ret; +}; + +static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int i; + + for (i = 0; i < pd_count; i++) + dev_pm_domain_detach(pds[i], false); +} + static int q6v5_init_reset(struct q6v5 *qproc) { qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev, @@ -1253,6 +1369,7 @@ static int q6v5_probe(struct platform_device *pdev) const struct rproc_hexagon_res *desc; struct q6v5 *qproc; struct rproc *rproc; + const char *mba_image; int ret; desc = of_device_get_match_data(&pdev->dev); @@ -1262,16 +1379,30 @@ static int q6v5_probe(struct platform_device *pdev) if (desc->need_mem_protection && !qcom_scm_is_available()) return -EPROBE_DEFER; + mba_image = desc->hexagon_mba_image; + ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", + 0, &mba_image); + if (ret < 0 && ret != -EINVAL) + return ret; + rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, - desc->hexagon_mba_image, sizeof(*qproc)); + mba_image, sizeof(*qproc)); if (!rproc) { dev_err(&pdev->dev, "failed to allocate rproc\n"); return -ENOMEM; } + rproc->auto_boot = false; + qproc = (struct q6v5 *)rproc->priv; qproc->dev = &pdev->dev; qproc->rproc = rproc; + qproc->hexagon_mdt_image = "modem.mdt"; + ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", + 1, &qproc->hexagon_mdt_image); + if (ret < 0 && ret != -EINVAL) + return ret; + platform_set_drvdata(pdev, qproc); ret = q6v5_init_mem(qproc, pdev); @@ -1322,10 +1453,26 @@ static int q6v5_probe(struct platform_device *pdev) } qproc->active_reg_count = ret; + ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds, + desc->active_pd_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to attach active power domains\n"); + goto free_rproc; + } + qproc->active_pd_count = ret; + + ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds, + desc->proxy_pd_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to init power domains\n"); + goto detach_active_pds; + } + qproc->proxy_pd_count = ret; + qproc->has_alt_reset = desc->has_alt_reset; ret = q6v5_init_reset(qproc); if (ret) - goto free_rproc; + goto detach_proxy_pds; qproc->version = desc->version; qproc->need_mem_protection = desc->need_mem_protection; @@ -1333,7 +1480,7 @@ static int q6v5_probe(struct platform_device *pdev) ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, qcom_msa_handover); if (ret) - goto free_rproc; + goto detach_proxy_pds; qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); @@ -1341,13 +1488,21 @@ static int q6v5_probe(struct platform_device *pdev) qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); + if (IS_ERR(qproc->sysmon)) { + ret = PTR_ERR(qproc->sysmon); + goto detach_proxy_pds; + } ret = rproc_add(rproc); if (ret) - goto free_rproc; + goto detach_proxy_pds; return 0; +detach_proxy_pds: + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +detach_active_pds: + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); free_rproc: rproc_free(rproc); @@ -1364,6 +1519,10 @@ static int q6v5_remove(struct platform_device *pdev) qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev); qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); + + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); + rproc_free(qproc->rproc); return 0; @@ -1388,6 +1547,16 @@ static const struct rproc_hexagon_res sdm845_mss = { "mnoc_axi", NULL }, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + "mss", + NULL + }, .need_mem_protection = true, .has_alt_reset = true, .version = MSS_SDM845, @@ -1395,16 +1564,26 @@ static const struct rproc_hexagon_res sdm845_mss = { static const struct rproc_hexagon_res msm8996_mss = { .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, .proxy_clk_names = (char*[]){ "xo", "pnoc", + "qdss", NULL }, .active_clk_names = (char*[]){ "iface", "bus", "mem", - "gpll0_mss_clk", + "gpll0_mss", + "snoc_axi", + "mnoc_axi", NULL }, .need_mem_protection = true, diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index b1e63fcd5fdf..f280f196d007 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -258,6 +258,7 @@ static int adsp_probe(struct platform_device *pdev) const struct adsp_data *desc; struct qcom_adsp *adsp; struct rproc *rproc; + const char *fw_name; int ret; desc = of_device_get_match_data(&pdev->dev); @@ -267,8 +268,14 @@ static int adsp_probe(struct platform_device *pdev) if (!qcom_scm_is_available()) return -EPROBE_DEFER; + fw_name = desc->firmware_name; + ret = of_property_read_string(pdev->dev.of_node, "firmware-name", + &fw_name); + if (ret < 0 && ret != -EINVAL) + return ret; + rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, - desc->firmware_name, sizeof(*adsp)); + fw_name, sizeof(*adsp)); if (!rproc) { dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; @@ -304,6 +311,10 @@ static int adsp_probe(struct platform_device *pdev) adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); + if (IS_ERR(adsp->sysmon)) { + ret = PTR_ERR(adsp->sysmon); + goto free_rproc; + } ret = rproc_add(rproc); if (ret) diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index e976a602b015..c231314eab66 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -6,8 +6,9 @@ #include <linux/module.h> #include <linux/notifier.h> #include <linux/slab.h> +#include <linux/interrupt.h> #include <linux/io.h> -#include <linux/notifier.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/remoteproc/qcom_rproc.h> @@ -25,6 +26,7 @@ struct qcom_sysmon { const char *name; + int shutdown_irq; int ssctl_version; int ssctl_instance; @@ -34,6 +36,8 @@ struct qcom_sysmon { struct rpmsg_endpoint *ept; struct completion comp; + struct completion ind_comp; + struct completion shutdown_comp; struct mutex lock; bool ssr_ack; @@ -137,6 +141,7 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, } #define SSCTL_SHUTDOWN_REQ 0x21 +#define SSCTL_SHUTDOWN_READY_IND 0x21 #define SSCTL_SUBSYS_EVENT_REQ 0x23 #define SSCTL_MAX_MSG_LEN 7 @@ -252,6 +257,29 @@ static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { {} }; +static struct qmi_elem_info ssctl_shutdown_ind_ei[] = { + {} +}; + +static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, + struct qmi_txn *txn, const void *data) +{ + struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); + + complete(&sysmon->ind_comp); +} + +static struct qmi_msg_handler qmi_indication_handler[] = { + { + .type = QMI_INDICATION, + .msg_id = SSCTL_SHUTDOWN_READY_IND, + .ei = ssctl_shutdown_ind_ei, + .decoded_size = 0, + .fn = sysmon_ind_cb + }, + {} +}; + /** * ssctl_request_shutdown() - request shutdown via SSCTL QMI service * @sysmon: sysmon context @@ -262,6 +290,8 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) struct qmi_txn txn; int ret; + reinit_completion(&sysmon->ind_comp); + reinit_completion(&sysmon->shutdown_comp); ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); if (ret < 0) { dev_err(sysmon->dev, "failed to allocate QMI txn\n"); @@ -283,6 +313,17 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) dev_err(sysmon->dev, "shutdown request failed\n"); else dev_dbg(sysmon->dev, "shutdown request completed\n"); + + if (sysmon->shutdown_irq > 0) { + ret = wait_for_completion_timeout(&sysmon->shutdown_comp, + 10 * HZ); + if (!ret) { + ret = try_wait_for_completion(&sysmon->ind_comp); + if (!ret) + dev_err(sysmon->dev, + "timeout waiting for shutdown ack\n"); + } + } } /** @@ -432,6 +473,15 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; } +static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) +{ + struct qcom_sysmon *sysmon = data; + + complete(&sysmon->shutdown_comp); + + return IRQ_HANDLED; +} + /** * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc * @rproc: rproc context to associate the subdev with @@ -449,7 +499,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); if (!sysmon) - return NULL; + return ERR_PTR(-ENOMEM); sysmon->dev = rproc->dev.parent; sysmon->rproc = rproc; @@ -458,13 +508,37 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, sysmon->ssctl_instance = ssctl_instance; init_completion(&sysmon->comp); + init_completion(&sysmon->ind_comp); + init_completion(&sysmon->shutdown_comp); mutex_init(&sysmon->lock); - ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); + sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, + "shutdown-ack"); + if (sysmon->shutdown_irq < 0) { + if (sysmon->shutdown_irq != -ENODATA) { + dev_err(sysmon->dev, + "failed to retrieve shutdown-ack IRQ\n"); + return ERR_PTR(sysmon->shutdown_irq); + } + } else { + ret = devm_request_threaded_irq(sysmon->dev, + sysmon->shutdown_irq, + NULL, sysmon_shutdown_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 shutdown-ack", sysmon); + if (ret) { + dev_err(sysmon->dev, + "failed to acquire shutdown-ack IRQ\n"); + return ERR_PTR(ret); + } + } + + ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, + qmi_indication_handler); if (ret < 0) { dev_err(sysmon->dev, "failed to initialize qmi handle\n"); kfree(sysmon); - return NULL; + return ERR_PTR(ret); } qmi_add_lookup(&sysmon->qmi, 43, 0, 0); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index b0e07e9f42d5..adcce523971e 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -553,6 +553,10 @@ static int wcnss_probe(struct platform_device *pdev) qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); + if (IS_ERR(wcnss->sysmon)) { + ret = PTR_ERR(wcnss->sysmon); + goto free_rproc; + } ret = rproc_add(rproc); if (ret) @@ -622,5 +626,5 @@ static void __exit wcnss_exit(void) } module_exit(wcnss_exit); -MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem"); +MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Wireless Subsystem"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 54ec38fc5dca..48feebd6d0a2 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -39,12 +39,16 @@ #include <linux/idr.h> #include <linux/elf.h> #include <linux/crc32.h> +#include <linux/of_reserved_mem.h> #include <linux/virtio_ids.h> #include <linux/virtio_ring.h> #include <asm/byteorder.h> +#include <linux/platform_device.h> #include "remoteproc_internal.h" +#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL + static DEFINE_MUTEX(rproc_list_mutex); static LIST_HEAD(rproc_list); @@ -145,7 +149,7 @@ static void rproc_disable_iommu(struct rproc *rproc) iommu_domain_free(domain); } -static phys_addr_t rproc_va_to_pa(void *cpu_addr) +phys_addr_t rproc_va_to_pa(void *cpu_addr) { /* * Return physical address according to virtual address location @@ -160,6 +164,7 @@ static phys_addr_t rproc_va_to_pa(void *cpu_addr) WARN_ON(!virt_addr_valid(cpu_addr)); return virt_to_phys(cpu_addr); } +EXPORT_SYMBOL(rproc_va_to_pa); /** * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address @@ -204,6 +209,10 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) list_for_each_entry(carveout, &rproc->carveouts, node) { int offset = da - carveout->da; + /* Verify that carveout is allocated */ + if (!carveout->va) + continue; + /* try next carveout if da is too small */ if (offset < 0) continue; @@ -272,25 +281,27 @@ rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...) * @len: associated area size * * This function is a helper function to verify requested device area (couple - * da, len) is part of specified carevout. + * da, len) is part of specified carveout. + * If da is not set (defined as FW_RSC_ADDR_ANY), only requested length is + * checked. * - * Return: 0 if carveout match request else -ENOMEM + * Return: 0 if carveout matches request else error */ -int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem, - u32 da, u32 len) +static int rproc_check_carveout_da(struct rproc *rproc, + struct rproc_mem_entry *mem, u32 da, u32 len) { struct device *dev = &rproc->dev; - int delta = 0; + int delta; /* Check requested resource length */ if (len > mem->len) { dev_err(dev, "Registered carveout doesn't fit len request\n"); - return -ENOMEM; + return -EINVAL; } if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) { - /* Update existing carveout da */ - mem->da = da; + /* Address doesn't match registered carveout configuration */ + return -EINVAL; } else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) { delta = da - mem->da; @@ -298,13 +309,13 @@ int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem, if (delta < 0) { dev_err(dev, "Registered carveout doesn't fit da request\n"); - return -ENOMEM; + return -EINVAL; } if (delta + len > mem->len) { dev_err(dev, "Registered carveout doesn't fit len request\n"); - return -ENOMEM; + return -EINVAL; } } @@ -418,8 +429,25 @@ static int rproc_vdev_do_start(struct rproc_subdev *subdev) static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) { struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); + int ret; - rproc_remove_virtio_dev(rvdev); + ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev); + if (ret) + dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret); +} + +/** + * rproc_rvdev_release() - release the existence of a rvdev + * + * @dev: the subdevice's dev + */ +static void rproc_rvdev_release(struct device *dev) +{ + struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); + + of_reserved_mem_device_release(dev); + + kfree(rvdev); } /** @@ -455,6 +483,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, struct device *dev = &rproc->dev; struct rproc_vdev *rvdev; int i, ret; + char name[16]; /* make sure resource isn't truncated */ if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) @@ -488,6 +517,29 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, rvdev->rproc = rproc; rvdev->index = rproc->nb_vdev++; + /* Initialise vdev subdevice */ + snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); + rvdev->dev.parent = rproc->dev.parent; + rvdev->dev.release = rproc_rvdev_release; + dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); + dev_set_drvdata(&rvdev->dev, rvdev); + + ret = device_register(&rvdev->dev); + if (ret) { + put_device(&rvdev->dev); + return ret; + } + /* Make device dma capable by inheriting from parent's capabilities */ + set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent)); + + ret = dma_coerce_mask_and_coherent(&rvdev->dev, + dma_get_mask(rproc->dev.parent)); + if (ret) { + dev_warn(dev, + "Failed to set DMA mask %llx. Trying to continue... %x\n", + dma_get_mask(rproc->dev.parent), ret); + } + /* parse the vrings */ for (i = 0; i < rsc->num_of_vrings; i++) { ret = rproc_parse_vring(rvdev, rsc, i); @@ -518,7 +570,7 @@ unwind_vring_allocations: for (i--; i >= 0; i--) rproc_free_vring(&rvdev->vring[i]); free_rvdev: - kfree(rvdev); + device_unregister(&rvdev->dev); return ret; } @@ -536,7 +588,7 @@ void rproc_vdev_release(struct kref *ref) rproc_remove_subdev(rproc, &rvdev->subdev); list_del(&rvdev->node); - kfree(rvdev); + device_unregister(&rvdev->dev); } /** @@ -558,9 +610,8 @@ void rproc_vdev_release(struct kref *ref) static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, int offset, int avail) { - struct rproc_mem_entry *trace; + struct rproc_debug_trace *trace; struct device *dev = &rproc->dev; - void *ptr; char name[15]; if (sizeof(*rsc) > avail) { @@ -574,28 +625,23 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, return -EINVAL; } - /* what's the kernel address of this resource ? */ - ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); - if (!ptr) { - dev_err(dev, "erroneous trace resource entry\n"); - return -EINVAL; - } - trace = kzalloc(sizeof(*trace), GFP_KERNEL); if (!trace) return -ENOMEM; /* set the trace buffer dma properties */ - trace->len = rsc->len; - trace->va = ptr; + trace->trace_mem.len = rsc->len; + trace->trace_mem.da = rsc->da; + + /* set pointer on rproc device */ + trace->rproc = rproc; /* make sure snprintf always null terminates, even if truncating */ snprintf(name, sizeof(name), "trace%d", rproc->num_traces); /* create the debugfs entry */ - trace->priv = rproc_create_trace_file(name, rproc, trace); - if (!trace->priv) { - trace->va = NULL; + trace->tfile = rproc_create_trace_file(name, rproc, trace); + if (!trace->tfile) { kfree(trace); return -EINVAL; } @@ -604,8 +650,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, rproc->num_traces++; - dev_dbg(dev, "%s added: va %pK, da 0x%x, len 0x%x\n", - name, ptr, rsc->da, rsc->len); + dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n", + name, rsc->da, rsc->len); return 0; } @@ -715,6 +761,18 @@ static int rproc_alloc_carveout(struct rproc *rproc, dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", va, &dma, mem->len); + if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) { + /* + * Check requested da is equal to dma address + * and print a warn message in case of missalignment. + * Don't stop rproc_start sequence as coprocessor may + * build pa to da translation on its side. + */ + if (mem->da != (u32)dma) + dev_warn(dev->parent, + "Allocated carveout doesn't fit device address request\n"); + } + /* * Ok, this is non-standard. * @@ -732,15 +790,7 @@ static int rproc_alloc_carveout(struct rproc *rproc, * to use the iommu-based DMA API: we expect 'dma' to contain the * physical address in this case. */ - - if (mem->da != FW_RSC_ADDR_ANY) { - if (!rproc->domain) { - dev_err(dev->parent, - "Bad carveout rsc configuration\n"); - ret = -ENOMEM; - goto dma_free; - } - + if (mem->da != FW_RSC_ADDR_ANY && rproc->domain) { mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); if (!mapping) { ret = -ENOMEM; @@ -767,11 +817,17 @@ static int rproc_alloc_carveout(struct rproc *rproc, dev_dbg(dev, "carveout mapped 0x%x to %pad\n", mem->da, &dma); - } else { + } + + if (mem->da == FW_RSC_ADDR_ANY) { + /* Update device address as undefined by requester */ + if ((u64)dma & HIGH_BITS_MASK) + dev_warn(dev, "DMA address cast in 32bit to fit resource table format\n"); + mem->da = (u32)dma; } - mem->dma = (u32)dma; + mem->dma = dma; mem->va = va; return 0; @@ -900,7 +956,8 @@ EXPORT_SYMBOL(rproc_add_carveout); * @dma: dma address * @len: memory carveout length * @da: device address - * @release: memory carveout function + * @alloc: memory carveout allocation function + * @release: memory carveout release function * @name: carveout name * * This function allocates a rproc_mem_entry struct and fill it with parameters @@ -1110,6 +1167,7 @@ static int rproc_alloc_registered_carveouts(struct rproc *rproc) struct rproc_mem_entry *entry, *tmp; struct fw_rsc_carveout *rsc; struct device *dev = &rproc->dev; + u64 pa; int ret; list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { @@ -1146,10 +1204,15 @@ static int rproc_alloc_registered_carveouts(struct rproc *rproc) /* Use va if defined else dma to generate pa */ if (entry->va) - rsc->pa = (u32)rproc_va_to_pa(entry->va); + pa = (u64)rproc_va_to_pa(entry->va); else - rsc->pa = (u32)entry->dma; + pa = (u64)entry->dma; + + if (((u64)pa) & HIGH_BITS_MASK) + dev_warn(dev, + "Physical address cast in 32bit to fit resource table format\n"); + rsc->pa = (u32)pa; rsc->da = entry->da; rsc->len = entry->len; } @@ -1182,15 +1245,16 @@ static void rproc_coredump_cleanup(struct rproc *rproc) static void rproc_resource_cleanup(struct rproc *rproc) { struct rproc_mem_entry *entry, *tmp; + struct rproc_debug_trace *trace, *ttmp; struct rproc_vdev *rvdev, *rvtmp; struct device *dev = &rproc->dev; /* clean up debugfs trace entries */ - list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { - rproc_remove_trace_file(entry->priv); + list_for_each_entry_safe(trace, ttmp, &rproc->traces, node) { + rproc_remove_trace_file(trace->tfile); rproc->num_traces--; - list_del(&entry->node); - kfree(entry); + list_del(&trace->node); + kfree(trace); } /* clean up iommu mapping entries */ diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index e90135c64af0..6da934b8dc4b 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -47,10 +47,23 @@ static struct dentry *rproc_dbg; static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf, size_t count, loff_t *ppos) { - struct rproc_mem_entry *trace = filp->private_data; - int len = strnlen(trace->va, trace->len); + struct rproc_debug_trace *data = filp->private_data; + struct rproc_mem_entry *trace = &data->trace_mem; + void *va; + char buf[100]; + int len; + + va = rproc_da_to_va(data->rproc, trace->da, trace->len); - return simple_read_from_buffer(userbuf, count, ppos, trace->va, len); + if (!va) { + len = scnprintf(buf, sizeof(buf), "Trace %s not available\n", + trace->name); + va = buf; + } else { + len = strnlen(va, trace->len); + } + + return simple_read_from_buffer(userbuf, count, ppos, va, len); } static const struct file_operations trace_rproc_ops = { @@ -155,6 +168,30 @@ static const struct file_operations rproc_recovery_ops = { .llseek = generic_file_llseek, }; +/* expose the crash trigger via debugfs */ +static ssize_t +rproc_crash_write(struct file *filp, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + unsigned int type; + int ret; + + ret = kstrtouint_from_user(user_buf, count, 0, &type); + if (ret < 0) + return ret; + + rproc_report_crash(rproc, type); + + return count; +} + +static const struct file_operations rproc_crash_ops = { + .write = rproc_crash_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + /* Expose resource table content via debugfs */ static int rproc_rsc_table_show(struct seq_file *seq, void *p) { @@ -288,7 +325,7 @@ void rproc_remove_trace_file(struct dentry *tfile) } struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, - struct rproc_mem_entry *trace) + struct rproc_debug_trace *trace) { struct dentry *tfile; @@ -325,6 +362,8 @@ void rproc_create_debug_dir(struct rproc *rproc) rproc, &rproc_name_ops); debugfs_create_file("recovery", 0400, rproc->dbg_dir, rproc, &rproc_recovery_ops); + debugfs_create_file("crash", 0200, rproc->dbg_dir, + rproc, &rproc_crash_ops); debugfs_create_file("resource_table", 0400, rproc->dbg_dir, rproc, &rproc_rsc_table_ops); debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index f6cad243d7ca..45ff76a06c72 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -25,6 +25,13 @@ struct rproc; +struct rproc_debug_trace { + struct rproc *rproc; + struct dentry *tfile; + struct list_head node; + struct rproc_mem_entry trace_mem; +}; + /* from remoteproc_core.c */ void rproc_release(struct kref *kref); irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); @@ -32,12 +39,12 @@ void rproc_vdev_release(struct kref *ref); /* from remoteproc_virtio.c */ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id); -void rproc_remove_virtio_dev(struct rproc_vdev *rvdev); +int rproc_remove_virtio_dev(struct device *dev, void *data); /* from remoteproc_debugfs.c */ void rproc_remove_trace_file(struct dentry *tfile); struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, - struct rproc_mem_entry *trace); + struct rproc_debug_trace *trace); void rproc_delete_debug_dir(struct rproc *rproc); void rproc_create_debug_dir(struct rproc *rproc); void rproc_init_debugfs(void); @@ -52,6 +59,7 @@ void rproc_free_vring(struct rproc_vring *rvring); int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); +phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 2d7cd344f3bf..44774de6f17b 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -17,7 +17,9 @@ * GNU General Public License for more details. */ +#include <linux/dma-mapping.h> #include <linux/export.h> +#include <linux/of_reserved_mem.h> #include <linux/remoteproc.h> #include <linux/virtio.h> #include <linux/virtio_config.h> @@ -316,6 +318,8 @@ static void rproc_virtio_dev_release(struct device *dev) struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct rproc *rproc = vdev_to_rproc(vdev); + kfree(vdev); + kref_put(&rvdev->refcount, rproc_vdev_release); put_device(&rproc->dev); @@ -333,10 +337,53 @@ static void rproc_virtio_dev_release(struct device *dev) int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) { struct rproc *rproc = rvdev->rproc; - struct device *dev = &rproc->dev; - struct virtio_device *vdev = &rvdev->vdev; + struct device *dev = &rvdev->dev; + struct virtio_device *vdev; + struct rproc_mem_entry *mem; int ret; + /* Try to find dedicated vdev buffer carveout */ + mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index); + if (mem) { + phys_addr_t pa; + + if (mem->of_resm_idx != -1) { + struct device_node *np = rproc->dev.parent->of_node; + + /* Associate reserved memory to vdev device */ + ret = of_reserved_mem_device_init_by_idx(dev, np, + mem->of_resm_idx); + if (ret) { + dev_err(dev, "Can't associate reserved memory\n"); + goto out; + } + } else { + if (mem->va) { + dev_warn(dev, "vdev %d buffer already mapped\n", + rvdev->index); + pa = rproc_va_to_pa(mem->va); + } else { + /* Use dma address as carveout no memmapped yet */ + pa = (phys_addr_t)mem->dma; + } + + /* Associate vdev buffer memory pool to vdev subdev */ + ret = dma_declare_coherent_memory(dev, pa, + mem->da, + mem->len); + if (ret < 0) { + dev_err(dev, "Failed to associate buffer\n"); + goto out; + } + } + } + + /* Allocate virtio device */ + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + ret = -ENOMEM; + goto out; + } vdev->id.device = id, vdev->config = &rproc_virtio_config_ops, vdev->dev.parent = dev; @@ -370,11 +417,15 @@ out: /** * rproc_remove_virtio_dev() - remove an rproc-induced virtio device - * @rvdev: the remote vdev + * @dev: the virtio device + * @data: must be null * * This function unregisters an existing virtio device. */ -void rproc_remove_virtio_dev(struct rproc_vdev *rvdev) +int rproc_remove_virtio_dev(struct device *dev, void *data) { - unregister_virtio_device(&rvdev->vdev); + struct virtio_device *vdev = dev_to_virtio(dev); + + unregister_virtio_device(vdev); + return 0; } diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index aacef0ea3b90..51049d17b1e5 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -19,6 +19,7 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> @@ -91,6 +92,77 @@ static void st_rproc_kick(struct rproc *rproc, int vqid) dev_err(dev, "failed to send message via mbox: %d\n", ret); } +static int st_rproc_mem_alloc(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + struct device *dev = rproc->dev.parent; + void *va; + + va = ioremap_wc(mem->dma, mem->len); + if (!va) { + dev_err(dev, "Unable to map memory region: %pa+%zx\n", + &mem->dma, mem->len); + return -ENOMEM; + } + + /* Update memory entry va */ + mem->va = va; + + return 0; +} + +static int st_rproc_mem_release(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + iounmap(mem->va); + + return 0; +} + +static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + struct device *dev = rproc->dev.parent; + struct device_node *np = dev->of_node; + struct rproc_mem_entry *mem; + struct reserved_mem *rmem; + struct of_phandle_iterator it; + int index = 0; + + of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); + while (of_phandle_iterator_next(&it) == 0) { + rmem = of_reserved_mem_lookup(it.node); + if (!rmem) { + dev_err(dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + /* No need to map vdev buffer */ + if (strcmp(it.node->name, "vdev0buffer")) { + /* Register memory region */ + mem = rproc_mem_entry_init(dev, NULL, + (dma_addr_t)rmem->base, + rmem->size, rmem->base, + st_rproc_mem_alloc, + st_rproc_mem_release, + it.node->name); + } else { + /* Register reserved memory for vdev buffer allocation */ + mem = rproc_of_resm_mem_entry_init(dev, index, + rmem->size, + rmem->base, + it.node->name); + } + + if (!mem) + return -ENOMEM; + + rproc_add_carveout(rproc, mem); + index++; + } + + return rproc_elf_load_rsc_table(rproc, fw); +} + static int st_rproc_start(struct rproc *rproc) { struct st_rproc *ddata = rproc->priv; @@ -158,9 +230,14 @@ static int st_rproc_stop(struct rproc *rproc) } static const struct rproc_ops st_rproc_ops = { - .kick = st_rproc_kick, - .start = st_rproc_start, - .stop = st_rproc_stop, + .kick = st_rproc_kick, + .start = st_rproc_start, + .stop = st_rproc_stop, + .parse_fw = st_rproc_parse_fw, + .load = rproc_elf_load_segments, + .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, + .sanity_check = rproc_elf_sanity_check, + .get_boot_addr = rproc_elf_get_boot_addr, }; /* @@ -254,12 +331,6 @@ static int st_rproc_parse_dt(struct platform_device *pdev) return -EINVAL; } - err = of_reserved_mem_device_init(dev); - if (err) { - dev_err(dev, "Failed to obtain shared memory\n"); - return err; - } - err = clk_prepare(ddata->clk); if (err) dev_err(dev, "failed to get clock\n"); @@ -387,8 +458,6 @@ static int st_rproc_remove(struct platform_device *pdev) clk_disable_unprepare(ddata->clk); - of_reserved_mem_device_release(&pdev->dev); - for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) mbox_free_channel(ddata->mbox_chan[i]); diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 664f957012cd..5d3685bd76a2 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -11,21 +11,21 @@ #define pr_fmt(fmt) "%s: " fmt, __func__ +#include <linux/dma-mapping.h> +#include <linux/idr.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/virtio.h> -#include <linux/virtio_ids.h> -#include <linux/virtio_config.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/rpmsg.h> #include <linux/scatterlist.h> -#include <linux/dma-mapping.h> #include <linux/slab.h> -#include <linux/idr.h> -#include <linux/jiffies.h> #include <linux/sched.h> +#include <linux/virtio.h> +#include <linux/virtio_ids.h> +#include <linux/virtio_config.h> #include <linux/wait.h> -#include <linux/rpmsg.h> -#include <linux/mutex.h> -#include <linux/of_device.h> #include "rpmsg_internal.h" @@ -912,7 +912,7 @@ static int rpmsg_probe(struct virtio_device *vdev) total_buf_space = vrp->num_bufs * vrp->buf_size; /* allocate coherent memory for the buffers */ - bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, + bufs_va = dma_alloc_coherent(vdev->dev.parent, total_buf_space, &vrp->bufs_dma, GFP_KERNEL); if (!bufs_va) { @@ -980,7 +980,7 @@ static int rpmsg_probe(struct virtio_device *vdev) return 0; free_coherent: - dma_free_coherent(vdev->dev.parent->parent, total_buf_space, + dma_free_coherent(vdev->dev.parent, total_buf_space, bufs_va, vrp->bufs_dma); vqs_del: vdev->config->del_vqs(vrp->vdev); @@ -1015,7 +1015,7 @@ static void rpmsg_remove(struct virtio_device *vdev) vdev->config->del_vqs(vrp->vdev); - dma_free_coherent(vdev->dev.parent->parent, total_buf_space, + dma_free_coherent(vdev->dev.parent, total_buf_space, vrp->rbufs, vrp->bufs_dma); kfree(vrp); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e1a1f2b1cbba..a71734c41693 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1864,4 +1864,15 @@ config RTC_DRV_GOLDFISH Goldfish is a code name for the virtual platform developed by Google for Android emulation. +config RTC_DRV_WILCO_EC + tristate "Wilco EC RTC" + depends on WILCO_EC + default m + help + If you say yes here, you get read/write support for the Real Time + Clock on the Wilco Embedded Controller (Wilco is a kind of Chromebook) + + This can also be built as a module. If so, the module will + be named "rtc_wilco_ec". + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 1bf9f75a7865..fe3962496685 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -177,6 +177,7 @@ obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o +obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o diff --git a/drivers/rtc/rtc-wilco-ec.c b/drivers/rtc/rtc-wilco-ec.c new file mode 100644 index 000000000000..e62bda0cb53e --- /dev/null +++ b/drivers/rtc/rtc-wilco-ec.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC interface for Wilco Embedded Controller with R/W abilities + * + * Copyright 2018 Google LLC + * + * The corresponding platform device is typically registered in + * drivers/platform/chrome/wilco_ec/core.c + */ + +#include <linux/bcd.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/platform_data/wilco-ec.h> +#include <linux/rtc.h> +#include <linux/timekeeping.h> + +#define EC_COMMAND_CMOS 0x7c +#define EC_CMOS_TOD_WRITE 0x02 +#define EC_CMOS_TOD_READ 0x08 + +/** + * struct ec_rtc_read - Format of RTC returned by EC. + * @second: Second value (0..59) + * @minute: Minute value (0..59) + * @hour: Hour value (0..23) + * @day: Day value (1..31) + * @month: Month value (1..12) + * @year: Year value (full year % 100) + * @century: Century value (full year / 100) + * + * All values are presented in binary (not BCD). + */ +struct ec_rtc_read { + u8 second; + u8 minute; + u8 hour; + u8 day; + u8 month; + u8 year; + u8 century; +} __packed; + +/** + * struct ec_rtc_write - Format of RTC sent to the EC. + * @param: EC_CMOS_TOD_WRITE + * @century: Century value (full year / 100) + * @year: Year value (full year % 100) + * @month: Month value (1..12) + * @day: Day value (1..31) + * @hour: Hour value (0..23) + * @minute: Minute value (0..59) + * @second: Second value (0..59) + * @weekday: Day of the week (0=Saturday) + * + * All values are presented in BCD. + */ +struct ec_rtc_write { + u8 param; + u8 century; + u8 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; + u8 weekday; +} __packed; + +static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev->parent); + u8 param = EC_CMOS_TOD_READ; + struct ec_rtc_read rtc; + struct wilco_ec_message msg = { + .type = WILCO_EC_MSG_LEGACY, + .flags = WILCO_EC_FLAG_RAW_RESPONSE, + .command = EC_COMMAND_CMOS, + .request_data = ¶m, + .request_size = sizeof(param), + .response_data = &rtc, + .response_size = sizeof(rtc), + }; + int ret; + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) + return ret; + + tm->tm_sec = rtc.second; + tm->tm_min = rtc.minute; + tm->tm_hour = rtc.hour; + tm->tm_mday = rtc.day; + tm->tm_mon = rtc.month - 1; + tm->tm_year = rtc.year + (rtc.century * 100) - 1900; + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + + /* Don't compute day of week, we don't need it. */ + tm->tm_wday = -1; + + return 0; +} + +static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev->parent); + struct ec_rtc_write rtc; + struct wilco_ec_message msg = { + .type = WILCO_EC_MSG_LEGACY, + .flags = WILCO_EC_FLAG_RAW_RESPONSE, + .command = EC_COMMAND_CMOS, + .request_data = &rtc, + .request_size = sizeof(rtc), + }; + int year = tm->tm_year + 1900; + /* + * Convert from 0=Sunday to 0=Saturday for the EC + * We DO need to set weekday because the EC controls battery charging + * schedules that depend on the day of the week. + */ + int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1; + int ret; + + rtc.param = EC_CMOS_TOD_WRITE; + rtc.century = bin2bcd(year / 100); + rtc.year = bin2bcd(year % 100); + rtc.month = bin2bcd(tm->tm_mon + 1); + rtc.day = bin2bcd(tm->tm_mday); + rtc.hour = bin2bcd(tm->tm_hour); + rtc.minute = bin2bcd(tm->tm_min); + rtc.second = bin2bcd(tm->tm_sec); + rtc.weekday = bin2bcd(wday); + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) + return ret; + + return 0; +} + +static const struct rtc_class_ops wilco_ec_rtc_ops = { + .read_time = wilco_ec_rtc_read, + .set_time = wilco_ec_rtc_write, +}; + +static int wilco_ec_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &wilco_ec_rtc_ops; + /* EC only supports this century */ + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + rtc->owner = THIS_MODULE; + + return rtc_register_device(rtc); +} + +static struct platform_driver wilco_ec_rtc_driver = { + .driver = { + .name = "rtc-wilco-ec", + }, + .probe = wilco_ec_rtc_probe, +}; + +module_platform_driver(wilco_ec_rtc_driver); + +MODULE_ALIAS("platform:rtc-wilco-ec"); +MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Wilco EC RTC driver"); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index a0baee25134c..4159c63a5fd2 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1382,3 +1382,40 @@ int chsc_pnso_brinfo(struct subchannel_id schid, return chsc_error_from_response(brinfo_area->response.code); } EXPORT_SYMBOL_GPL(chsc_pnso_brinfo); + +int chsc_sgib(u32 origin) +{ + struct { + struct chsc_header request; + u16 op; + u8 reserved01[2]; + u8 reserved02:4; + u8 fmt:4; + u8 reserved03[7]; + /* operation data area begin */ + u8 reserved04[4]; + u32 gib_origin; + u8 reserved05[10]; + u8 aix; + u8 reserved06[4029]; + struct chsc_header response; + u8 reserved07[4]; + } *sgib_area; + int ret; + + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + sgib_area = chsc_page; + sgib_area->request.length = 0x0fe0; + sgib_area->request.code = 0x0021; + sgib_area->op = 0x1; + sgib_area->gib_origin = origin; + + ret = chsc(sgib_area); + if (ret == 0) + ret = chsc_error_from_response(sgib_area->response.code); + spin_unlock_irq(&chsc_page_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(chsc_sgib); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 78aba8d94eec..e57d68e325a3 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -164,6 +164,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp); int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd); int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc, u64 summary_indicator_addr, u64 subchannel_indicator_addr); +int chsc_sgib(u32 origin); int chsc_error_from_response(int response); int chsc_siosl(struct subchannel_id schid); diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index a45f81ec80ce..8e28a505f7e8 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -413,13 +413,16 @@ static int aac_slave_configure(struct scsi_device *sdev) if (chn < AAC_MAX_BUSES && tid < AAC_MAX_TARGETS && aac->sa_firmware) { devtype = aac->hba_map[chn][tid].devtype; - if (devtype == AAC_DEVTYPE_NATIVE_RAW) + if (devtype == AAC_DEVTYPE_NATIVE_RAW) { depth = aac->hba_map[chn][tid].qd_limit; - else if (devtype == AAC_DEVTYPE_ARC_RAW) + set_timeout = 1; + goto common_config; + } + if (devtype == AAC_DEVTYPE_ARC_RAW) { set_qd_dev_type = true; - - set_timeout = 1; - goto common_config; + set_timeout = 1; + goto common_config; + } } if (aac->jbod && (sdev->type == TYPE_DISK)) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 6c87bd34509a..9bfa9f12d81e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -18,6 +18,7 @@ #include <linux/dmapool.h> #include <linux/iopoll.h> #include <linux/lcm.h> +#include <linux/libata.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_address.h> @@ -94,6 +95,11 @@ enum { PORT_TYPE_SATA = (1U << 0), }; +enum dev_status { + HISI_SAS_DEV_INIT, + HISI_SAS_DEV_NORMAL, +}; + enum { HISI_SAS_INT_ABT_CMD = 0, HISI_SAS_INT_ABT_DEV = 1, @@ -161,6 +167,7 @@ struct hisi_sas_phy { u8 in_reset; u8 reserved[2]; u32 phy_type; + u32 code_violation_err_count; enum sas_linkrate minimum_linkrate; enum sas_linkrate maximum_linkrate; }; @@ -194,6 +201,7 @@ struct hisi_sas_device { struct hisi_sas_dq *dq; struct list_head list; enum sas_device_type dev_type; + enum dev_status dev_status; int device_id; int sata_idx; spinlock_t lock; /* For protecting slots */ diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 13ca5a0bdf6b..3c3cf89f713f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -10,6 +10,7 @@ */ #include "hisi_sas.h" +#include "../libsas/sas_internal.h" #define DRV_NAME "hisi_sas" #define DEV_IS_GONE(dev) \ @@ -707,6 +708,7 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device) hisi_hba->devices[i].device_id = i; sas_dev = &hisi_hba->devices[i]; + sas_dev->dev_status = HISI_SAS_DEV_INIT; sas_dev->dev_type = device->dev_type; sas_dev->hisi_hba = hisi_hba; sas_dev->sas_device = device; @@ -731,6 +733,8 @@ static int hisi_sas_init_device(struct domain_device *device) struct hisi_sas_tmf_task tmf_task; int retry = HISI_SAS_SRST_ATA_DISK_CNT; struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + struct device *dev = hisi_hba->dev; + struct sas_phy *local_phy; switch (device->dev_type) { case SAS_END_DEVICE: @@ -746,6 +750,31 @@ static int hisi_sas_init_device(struct domain_device *device) case SAS_SATA_PM: case SAS_SATA_PM_PORT: case SAS_SATA_PENDING: + /* + * send HARD RESET to clear previous affiliation of + * STP target port + */ + local_phy = sas_get_local_phy(device); + if (!scsi_is_sas_phy_local(local_phy)) { + unsigned long deadline = ata_deadline(jiffies, 20000); + struct sata_device *sata_dev = &device->sata_dev; + struct ata_host *ata_host = sata_dev->ata_host; + struct ata_port_operations *ops = ata_host->ops; + struct ata_port *ap = sata_dev->ap; + struct ata_link *link; + unsigned int classes; + + ata_for_each_link(link, ap, EDGE) + rc = ops->hardreset(link, &classes, + deadline); + } + sas_put_local_phy(local_phy); + if (rc) { + dev_warn(dev, "SATA disk hardreset fail: 0x%x\n", + rc); + return rc; + } + while (retry-- > 0) { rc = hisi_sas_softreset_ata_disk(device); if (!rc) @@ -808,6 +837,7 @@ static int hisi_sas_dev_found(struct domain_device *device) rc = hisi_sas_init_device(device); if (rc) goto err_out; + sas_dev->dev_status = HISI_SAS_DEV_NORMAL; return 0; err_out: @@ -980,7 +1010,8 @@ static void hisi_sas_do_release_task(struct hisi_hba *hisi_hba, struct sas_task spin_lock_irqsave(&task->task_state_lock, flags); task->task_state_flags &= ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR); - task->task_state_flags |= SAS_TASK_STATE_DONE; + if (!slot->is_internal && task->task_proto != SAS_PROTOCOL_SMP) + task->task_state_flags |= SAS_TASK_STATE_DONE; spin_unlock_irqrestore(&task->task_state_lock, flags); } @@ -1713,20 +1744,23 @@ static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun) static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device) { struct sas_phy *local_phy = sas_get_local_phy(device); - int rc, reset_type = (device->dev_type == SAS_SATA_DEV || - (device->tproto & SAS_PROTOCOL_STP)) ? 0 : 1; + struct hisi_sas_device *sas_dev = device->lldd_dev; struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct sas_ha_struct *sas_ha = &hisi_hba->sha; struct asd_sas_phy *sas_phy = sas_ha->sas_phy[local_phy->number]; struct hisi_sas_phy *phy = container_of(sas_phy, struct hisi_sas_phy, sas_phy); DECLARE_COMPLETION_ONSTACK(phyreset); + int rc, reset_type; if (scsi_is_sas_phy_local(local_phy)) { phy->in_reset = 1; phy->reset_completion = &phyreset; } + reset_type = (sas_dev->dev_status == HISI_SAS_DEV_INIT || + !dev_is_sata(device)) ? 1 : 0; + rc = sas_phy_reset(local_phy, reset_type); sas_put_local_phy(local_phy); @@ -1742,8 +1776,13 @@ static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device) /* report PHY down if timed out */ if (!ret) hisi_sas_phy_down(hisi_hba, sas_phy->id, 0); - } else + } else if (sas_dev->dev_status != HISI_SAS_DEV_INIT) { + /* + * If in init state, we rely on caller to wait for link to be + * ready; otherwise, delay. + */ msleep(2000); + } return rc; } @@ -2125,9 +2164,18 @@ static int hisi_sas_write_gpio(struct sas_ha_struct *sha, u8 reg_type, static void hisi_sas_phy_disconnected(struct hisi_sas_phy *phy) { + struct asd_sas_phy *sas_phy = &phy->sas_phy; + struct sas_phy *sphy = sas_phy->phy; + struct sas_phy_data *d = sphy->hostdata; + phy->phy_attached = 0; phy->phy_type = 0; phy->port = NULL; + + if (d->enable) + sphy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + else + sphy->negotiated_linkrate = SAS_PHY_DISABLED; } void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy) @@ -2253,6 +2301,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba) for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED; hisi_hba->devices[i].device_id = i; + hisi_hba->devices[i].dev_status = HISI_SAS_DEV_INIT; } for (i = 0; i < hisi_hba->queue_count; i++) { diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index e40cc6b3b67b..89160ab3efb0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -868,6 +868,7 @@ hisi_sas_device *alloc_dev_quirk_v2_hw(struct domain_device *device) hisi_hba->devices[i].device_id = i; sas_dev = &hisi_hba->devices[i]; + sas_dev->dev_status = HISI_SAS_DEV_INIT; sas_dev->dev_type = device->dev_type; sas_dev->hisi_hba = hisi_hba; sas_dev->sas_device = device; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 9ec8848ec541..086695a4099f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -129,6 +129,7 @@ #define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF) #define CMD_HDR_PIR_OFF 8 #define CMD_HDR_PIR_MSK (0x1 << CMD_HDR_PIR_OFF) +#define SERDES_CFG (PORT_BASE + 0x1c) #define SL_CFG (PORT_BASE + 0x84) #define AIP_LIMIT (PORT_BASE + 0x90) #define SL_CONTROL (PORT_BASE + 0x94) @@ -181,6 +182,8 @@ #define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF 22 #define CHL_INT2 (PORT_BASE + 0x1bc) #define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0 +#define CHL_INT2_RX_DISP_ERR_OFF 28 +#define CHL_INT2_RX_CODE_ERR_OFF 29 #define CHL_INT2_RX_INVLD_DW_OFF 30 #define CHL_INT2_STP_LINK_TIMEOUT_OFF 31 #define CHL_INT0_MSK (PORT_BASE + 0x1c0) @@ -523,6 +526,7 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) } hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, prog_phy_link_rate); + hisi_sas_phy_write32(hisi_hba, i, SERDES_CFG, 0xffc00); hisi_sas_phy_write32(hisi_hba, i, SAS_RX_TRAIN_TIMER, 0x13e80); hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff); @@ -544,6 +548,8 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER, 0x7f7a120); hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER, 0x2a0a01); hisi_sas_phy_write32(hisi_hba, i, SAS_SSP_CON_TIMER_CFG, 0x32); + hisi_sas_phy_write32(hisi_hba, i, SAS_EC_INT_COAL_TIME, + 0x30f4240); /* used for 12G negotiate */ hisi_sas_phy_write32(hisi_hba, i, COARSETUNE_TIME, 0x1e); hisi_sas_phy_write32(hisi_hba, i, AIP_LIMIT, 0x2ffff); @@ -1344,7 +1350,8 @@ static void prep_abort_v3_hw(struct hisi_hba *hisi_hba, static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) { - int i, res; + int i; + irqreturn_t res; u32 context, port_id, link_rate; struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; @@ -1575,6 +1582,39 @@ static void handle_chl_int1_v3_hw(struct hisi_hba *hisi_hba, int phy_no) hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT1, irq_value); } +static void phy_get_events_v3_hw(struct hisi_hba *hisi_hba, int phy_no) +{ + struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + struct sas_phy *sphy = sas_phy->phy; + unsigned long flags; + u32 reg_value; + + spin_lock_irqsave(&phy->lock, flags); + + /* loss dword sync */ + reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DWS_LOST); + sphy->loss_of_dword_sync_count += reg_value; + + /* phy reset problem */ + reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_RESET_PROB); + sphy->phy_reset_problem_count += reg_value; + + /* invalid dword */ + reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_INVLD_DW); + sphy->invalid_dword_count += reg_value; + + /* disparity err */ + reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DISP_ERR); + sphy->running_disparity_error_count += reg_value; + + /* code violation error */ + reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_CODE_ERR); + phy->code_violation_err_count += reg_value; + + spin_unlock_irqrestore(&phy->lock, flags); +} + static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no) { u32 irq_msk = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2_MSK); @@ -1582,6 +1622,9 @@ static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no) struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct pci_dev *pci_dev = hisi_hba->pci_dev; struct device *dev = hisi_hba->dev; + static const u32 msk = BIT(CHL_INT2_RX_DISP_ERR_OFF) | + BIT(CHL_INT2_RX_CODE_ERR_OFF) | + BIT(CHL_INT2_RX_INVLD_DW_OFF); irq_value &= ~irq_msk; if (!irq_value) @@ -1602,6 +1645,25 @@ static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no) hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET); } + if (pci_dev->revision > 0x20 && (irq_value & msk)) { + struct asd_sas_phy *sas_phy = &phy->sas_phy; + struct sas_phy *sphy = sas_phy->phy; + + phy_get_events_v3_hw(hisi_hba, phy_no); + + if (irq_value & BIT(CHL_INT2_RX_INVLD_DW_OFF)) + dev_info(dev, "phy%d invalid dword cnt: %u\n", phy_no, + sphy->invalid_dword_count); + + if (irq_value & BIT(CHL_INT2_RX_CODE_ERR_OFF)) + dev_info(dev, "phy%d code violation cnt: %u\n", phy_no, + phy->code_violation_err_count); + + if (irq_value & BIT(CHL_INT2_RX_DISP_ERR_OFF)) + dev_info(dev, "phy%d disparity error cnt: %u\n", phy_no, + sphy->running_disparity_error_count); + } + if ((irq_value & BIT(CHL_INT2_RX_INVLD_DW_OFF)) && (pci_dev->revision == 0x20)) { u32 reg_value; @@ -2230,31 +2292,6 @@ static u32 get_phys_state_v3_hw(struct hisi_hba *hisi_hba) return hisi_sas_read32(hisi_hba, PHY_STATE); } -static void phy_get_events_v3_hw(struct hisi_hba *hisi_hba, int phy_no) -{ - struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; - struct asd_sas_phy *sas_phy = &phy->sas_phy; - struct sas_phy *sphy = sas_phy->phy; - u32 reg_value; - - /* loss dword sync */ - reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DWS_LOST); - sphy->loss_of_dword_sync_count += reg_value; - - /* phy reset problem */ - reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_RESET_PROB); - sphy->phy_reset_problem_count += reg_value; - - /* invalid dword */ - reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_INVLD_DW); - sphy->invalid_dword_count += reg_value; - - /* disparity err */ - reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DISP_ERR); - sphy->running_disparity_error_count += reg_value; - -} - static int disable_host_v3_hw(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 21309d5b456d..e893949a3d11 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -798,7 +798,7 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); * @datalen: len of buffer * * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and - * then completes the command and task. + * then completes the command and task. called under back_lock **/ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, struct iscsi_task *task, char *data, @@ -894,6 +894,9 @@ out: * @conn: iscsi connection * @hdr: iscsi pdu * @task: scsi command task + * + * iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received + * then completes the command and task. called under back_lock **/ static void iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, @@ -978,6 +981,16 @@ static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) return 0; } +/** + * iscsi_nop_out_rsp - SCSI NOP Response processing + * @task: scsi command task + * @nop: the nop structure + * @data: where to put the data + * @datalen: length of data + * + * iscsi_nop_out_rsp handles nop response from use or + * from user space. called under back_lock + **/ static int iscsi_nop_out_rsp(struct iscsi_task *task, struct iscsi_nopin *nop, char *data, int datalen) { @@ -1750,7 +1763,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) return 0; prepd_reject: + spin_lock_bh(&session->back_lock); iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); + spin_unlock_bh(&session->back_lock); reject: spin_unlock_bh(&session->frwd_lock); ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", @@ -1758,7 +1773,9 @@ reject: return SCSI_MLQUEUE_TARGET_BUSY; prepd_fault: + spin_lock_bh(&session->back_lock); iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); + spin_unlock_bh(&session->back_lock); fault: spin_unlock_bh(&session->frwd_lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", @@ -3075,8 +3092,9 @@ fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn) state = ISCSI_TASK_ABRT_SESS_RECOV; if (task->state == ISCSI_TASK_PENDING) state = ISCSI_TASK_COMPLETED; + spin_lock_bh(&session->back_lock); iscsi_complete_task(task, state); - + spin_unlock_bh(&session->back_lock); } } diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 9923e9e3b884..c3fe3f3a78f5 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -129,12 +129,17 @@ static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv) BUG_ON(sg->length == 0); /* + * We always map for the recv path. + * * If the page count is greater than one it is ok to send * to the network layer's zero copy send path. If not we - * have to go the slow sendmsg path. We always map for the - * recv path. + * have to go the slow sendmsg path. + * + * Same goes for slab pages: skb_can_coalesce() allows + * coalescing neighboring slab objects into a single frag which + * triggers one of hardened usercopy checks. */ - if (page_count(sg_page(sg)) >= 1 && !recv) + if (!recv && page_count(sg_page(sg)) >= 1 && !PageSlab(sg_page(sg))) return; if (recv) { diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 3b5873f6751e..7fcdaed3fa94 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4090,7 +4090,7 @@ lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc) /* Sanity check to ensure our sizing is right for both SCSI and NVME */ if (sizeof(struct lpfc_io_buf) > LPFC_COMMON_IO_BUF_SZ) { lpfc_printf_log(phba, KERN_ERR, LOG_FCP, - "6426 Common buffer size %ld exceeds %d\n", + "6426 Common buffer size %zd exceeds %d\n", sizeof(struct lpfc_io_buf), LPFC_COMMON_IO_BUF_SZ); return 0; @@ -10052,7 +10052,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) { struct pci_dev *pdev = phba->pcidev; unsigned long bar0map_len, bar1map_len, bar2map_len; - int error = -ENODEV; + int error; uint32_t if_type; if (!pdev) @@ -10071,7 +10071,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) */ if (pci_read_config_dword(pdev, LPFC_SLI_INTF, &phba->sli4_hba.sli_intf.word0)) { - return error; + return -ENODEV; } /* There is no SLI3 failback for SLI4 devices. */ @@ -10081,7 +10081,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) "2894 SLI_INTF reg contents invalid " "sli_intf reg 0x%x\n", phba->sli4_hba.sli_intf.word0); - return error; + return -ENODEV; } if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); @@ -10105,7 +10105,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) dev_printk(KERN_ERR, &pdev->dev, "ioremap failed for SLI4 PCI config " "registers.\n"); - goto out; + return -ENODEV; } phba->pci_bar0_memmap_p = phba->sli4_hba.conf_regs_memmap_p; /* Set up BAR0 PCI config space register memory map */ @@ -10116,7 +10116,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) { dev_printk(KERN_ERR, &pdev->dev, "FATAL - No BAR0 mapping for SLI4, if_type 2\n"); - goto out; + return -ENODEV; } phba->sli4_hba.conf_regs_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len); @@ -10124,7 +10124,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) dev_printk(KERN_ERR, &pdev->dev, "ioremap failed for SLI4 PCI config " "registers.\n"); - goto out; + return -ENODEV; } lpfc_sli4_bar0_register_memmap(phba, if_type); } @@ -10170,6 +10170,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) if (!phba->sli4_hba.drbl_regs_memmap_p) { dev_err(&pdev->dev, "ioremap failed for SLI4 HBA doorbell registers.\n"); + error = -ENOMEM; goto out_iounmap_conf; } phba->pci_bar2_memmap_p = phba->sli4_hba.drbl_regs_memmap_p; @@ -10219,6 +10220,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) if (!phba->sli4_hba.dpp_regs_memmap_p) { dev_err(&pdev->dev, "ioremap failed for SLI4 HBA dpp registers.\n"); + error = -ENOMEM; goto out_iounmap_ctrl; } phba->pci_bar4_memmap_p = phba->sli4_hba.dpp_regs_memmap_p; @@ -10249,7 +10251,7 @@ out_iounmap_ctrl: iounmap(phba->sli4_hba.ctrl_regs_memmap_p); out_iounmap_conf: iounmap(phba->sli4_hba.conf_regs_memmap_p); -out: + return error; } @@ -11137,7 +11139,8 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) lpfc_sli4_ras_dma_free(phba); /* Stop the SLI4 device port */ - phba->pport->work_port_events = 0; + if (phba->pport) + phba->pport->work_port_events = 0; } /** diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 55ab9d3ee4ba..1aa00d2c3f74 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -965,7 +965,7 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, struct lpfc_nodelist *ndlp; struct lpfc_nvme_fcpreq_priv *freqpriv; struct lpfc_nvme_lport *lport; - uint32_t code, status, idx, cpu; + uint32_t code, status, idx; uint16_t cid, sqhd, data; uint32_t *ptr; @@ -1138,6 +1138,7 @@ out_err: lpfc_nvme_ktime(phba, lpfc_ncmd); } if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) { + uint32_t cpu; idx = lpfc_ncmd->cur_iocbq.hba_wqidx; cpu = smp_processor_id(); if (cpu < LPFC_CHECK_CPU_CNT) { diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index d0817facdae3..57b4a463b589 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -9881,7 +9881,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, * The WQE can be either 64 or 128 bytes, */ - lockdep_assert_held(&phba->hbalock); + lockdep_assert_held(&pring->ring_lock); if (piocb->sli4_xritag == NO_XRI) { if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN || diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index dace907744a5..293f5cf524d7 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3924,12 +3924,12 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) /* * The cur_state should not last for more than max_wait secs */ - for (i = 0; i < max_wait; i++) { + for (i = 0; i < max_wait * 50; i++) { curr_abs_state = instance->instancet-> read_fw_status_reg(instance); if (abs_state == curr_abs_state) { - msleep(1000); + msleep(20); } else break; } diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 2eb1ae721a7d..f928c4d3a1ef 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1652,6 +1652,8 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr, } rval = kstrtol(buf, 10, &type); + if (rval) + return rval; speed = type; if (type == 40 || type == 80 || type == 160 || type == 320) { diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index ead17288e2a7..5819a45ac5ef 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -193,6 +193,8 @@ qla_dfs_tgt_counters_show(struct seq_file *s, void *unused) for (i = 0; i < vha->hw->max_qpairs; i++) { qpair = vha->hw->queue_pair_map[i]; + if (!qpair) + continue; qla_core_sbt_cmd += qpair->tgt_counters.qla_core_sbt_cmd; core_qla_que_buf += qpair->tgt_counters.core_qla_que_buf; qla_core_ret_ctio += qpair->tgt_counters.qla_core_ret_ctio; diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 63f8e3c19841..456a41d2e2c6 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -1132,7 +1132,7 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, /* if initiator doing write or target doing read */ if (direction_to_device) { for_each_sg(sgl, sg, tot_dsds, i) { - dma_addr_t sle_phys = sg_phys(sg); + u64 sle_phys = sg_phys(sg); /* If SGE addr + len flips bits in upper 32-bits */ if (MSD(sle_phys + sg->length) ^ MSD(sle_phys)) { @@ -1178,7 +1178,7 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe023, "%s: sg[%x] (phys=%llx sglen=%x) ldma_sg_len: %x dif_bundl_len: %x ldma_needed: %x\n", - __func__, i, sg_phys(sg), sglen, ldma_sg_len, + __func__, i, (u64)sg_phys(sg), sglen, ldma_sg_len, difctx->dif_bundl_len, ldma_needed); while (sglen) { diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 5d9ccbab7581..75ec43aa8df3 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2764,6 +2764,12 @@ static void pqi_process_raid_io_error(struct pqi_io_request *io_request) sshdr.sense_key == HARDWARE_ERROR && sshdr.asc == 0x3e && sshdr.ascq == 0x1) { + struct pqi_ctrl_info *ctrl_info = shost_to_hba(scmd->device->host); + struct pqi_scsi_dev *device = scmd->device->hostdata; + + if (printk_ratelimit()) + scmd_printk(KERN_ERR, scmd, "received 'logical unit failure' from controller for scsi %d:%d:%d:%d\n", + ctrl_info->scsi_host->host_no, device->bus, device->target, device->lun); pqi_take_device_offline(scmd->device, "RAID"); host_byte = DID_NO_CONNECT; } diff --git a/drivers/scsi/ufs/ufs-hisi.c b/drivers/scsi/ufs/ufs-hisi.c index f2d3df357a97..0e855b5afe82 100644 --- a/drivers/scsi/ufs/ufs-hisi.c +++ b/drivers/scsi/ufs/ufs-hisi.c @@ -640,7 +640,7 @@ static int ufs_hi3670_init(struct ufs_hba *hba) return 0; } -static struct ufs_hba_variant_ops ufs_hba_hi3660_vops = { +static const struct ufs_hba_variant_ops ufs_hba_hi3660_vops = { .name = "hi3660", .init = ufs_hi3660_init, .link_startup_notify = ufs_hisi_link_startup_notify, @@ -649,7 +649,7 @@ static struct ufs_hba_variant_ops ufs_hba_hi3660_vops = { .resume = ufs_hisi_resume, }; -static struct ufs_hba_variant_ops ufs_hba_hi3670_vops = { +static const struct ufs_hba_variant_ops ufs_hba_hi3670_vops = { .name = "hi3670", .init = ufs_hi3670_init, .link_startup_notify = ufs_hisi_link_startup_notify, @@ -669,13 +669,10 @@ MODULE_DEVICE_TABLE(of, ufs_hisi_of_match); static int ufs_hisi_probe(struct platform_device *pdev) { const struct of_device_id *of_id; - struct ufs_hba_variant_ops *vops; - struct device *dev = &pdev->dev; - of_id = of_match_node(ufs_hisi_of_match, dev->of_node); - vops = (struct ufs_hba_variant_ops *)of_id->data; + of_id = of_match_node(ufs_hisi_of_match, pdev->dev.of_node); - return ufshcd_pltfrm_init(pdev, vops); + return ufshcd_pltfrm_init(pdev, of_id->data); } static int ufs_hisi_remove(struct platform_device *pdev) diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 895a9b5ac989..27213676329c 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -297,7 +297,7 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) * Returns 0 on success, non-zero value on failure */ int ufshcd_pltfrm_init(struct platform_device *pdev, - struct ufs_hba_variant_ops *vops) + const struct ufs_hba_variant_ops *vops) { struct ufs_hba *hba; void __iomem *mmio_base; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.h b/drivers/scsi/ufs/ufshcd-pltfrm.h index df64c4180340..1f29e1fd6d52 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.h +++ b/drivers/scsi/ufs/ufshcd-pltfrm.h @@ -17,7 +17,7 @@ #include "ufshcd.h" int ufshcd_pltfrm_init(struct platform_device *pdev, - struct ufs_hba_variant_ops *vops); + const struct ufs_hba_variant_ops *vops); void ufshcd_pltfrm_shutdown(struct platform_device *pdev); #ifdef CONFIG_PM diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 69ba7445d2b3..ecfa898b9ccc 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -546,7 +546,7 @@ struct ufs_hba { int nutrs; int nutmrs; u32 ufs_version; - struct ufs_hba_variant_ops *vops; + const struct ufs_hba_variant_ops *vops; void *priv; unsigned int irq; bool is_irq_enabled; diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 1a6f150cd2d8..8af01777d09c 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -586,7 +586,6 @@ static int virtscsi_device_reset(struct scsi_cmnd *sc) return FAILED; memset(cmd, 0, sizeof(*cmd)); - cmd->sc = sc; cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){ .type = VIRTIO_SCSI_T_TMF, .subtype = cpu_to_virtio32(vscsi->vdev, @@ -645,7 +644,6 @@ static int virtscsi_abort(struct scsi_cmnd *sc) return FAILED; memset(cmd, 0, sizeof(*cmd)); - cmd->sc = sc; cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){ .type = VIRTIO_SCSI_T_TMF, .subtype = VIRTIO_SCSI_T_TMF_ABORT_TASK, diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 5831e0eecea1..9704b135a7bc 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1663,7 +1663,7 @@ static void tcmu_dev_kref_release(struct kref *kref) WARN_ON(!all_expired); tcmu_blocks_release(&udev->data_blocks, 0, udev->dbi_max + 1); - kfree(udev->data_bitmap); + bitmap_free(udev->data_bitmap); mutex_unlock(&udev->cmdr_lock); call_rcu(&dev->rcu_head, tcmu_dev_call_rcu); @@ -1794,11 +1794,12 @@ static int tcmu_netlink_event_send(struct tcmu_dev *udev, ret = genlmsg_multicast_allns(&tcmu_genl_family, skb, 0, TCMU_MCGRP_CONFIG, GFP_KERNEL); - /* We don't care if no one is listening */ - if (ret == -ESRCH) - ret = 0; - if (!ret) - ret = tcmu_wait_genl_cmd_reply(udev); + + /* Wait during an add as the listener may not be up yet */ + if (ret == 0 || + (ret == -ESRCH && cmd == TCMU_CMD_ADDED_DEVICE)) + return tcmu_wait_genl_cmd_reply(udev); + return ret; } @@ -1870,9 +1871,7 @@ static int tcmu_configure_device(struct se_device *dev) info = &udev->uio_info; mutex_lock(&udev->cmdr_lock); - udev->data_bitmap = kcalloc(BITS_TO_LONGS(udev->max_blocks), - sizeof(unsigned long), - GFP_KERNEL); + udev->data_bitmap = bitmap_zalloc(udev->max_blocks, GFP_KERNEL); mutex_unlock(&udev->cmdr_lock); if (!udev->data_bitmap) { ret = -ENOMEM; @@ -1959,7 +1958,7 @@ err_register: vfree(udev->mb_addr); udev->mb_addr = NULL; err_vzalloc: - kfree(udev->data_bitmap); + bitmap_free(udev->data_bitmap); udev->data_bitmap = NULL; err_bitmap_alloc: kfree(info->name); diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 98dbc796353f..53ca9ba6ab4b 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -153,7 +153,6 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port) #ifdef CONFIG_SERIAL_8250_DMA static const struct dw_dma_platform_data qrk_serial_dma_pdata = { .nr_channels = 2, - .is_private = true, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, .chan_priority = CHAN_PRIORITY_ASCENDING, .block_size = 4095, diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index d2652dccc699..c9cfb100ecdc 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -94,7 +94,7 @@ static void * __init xdbc_get_page(dma_addr_t *dma_addr) { void *virt; - virt = memblock_alloc_nopanic(PAGE_SIZE, PAGE_SIZE); + virt = memblock_alloc(PAGE_SIZE, PAGE_SIZE); if (!virt) return NULL; diff --git a/drivers/video/fbdev/aty/radeon_pm.c b/drivers/video/fbdev/aty/radeon_pm.c index e695adb0e573..2dc5703eac51 100644 --- a/drivers/video/fbdev/aty/radeon_pm.c +++ b/drivers/video/fbdev/aty/radeon_pm.c @@ -2844,8 +2844,8 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis * in some desktop G4s), Via (M9+ chip on iBook G4) and * Snowy (M11 chip on iBook G4 manufactured after July 2005) */ - if (!strcmp(rinfo->of_node->name, "ATY,JasperParent") || - !strcmp(rinfo->of_node->name, "ATY,SnowyParent")) { + if (of_node_name_eq(rinfo->of_node, "ATY,JasperParent") || + of_node_name_eq(rinfo->of_node, "ATY,SnowyParent")) { rinfo->reinit_func = radeon_reinitialize_M10; rinfo->pm_mode |= radeon_pm_off; } @@ -2855,7 +2855,7 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis rinfo->pm_mode |= radeon_pm_off; } #endif - if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) { + if (of_node_name_eq(rinfo->of_node, "ATY,ViaParent")) { rinfo->reinit_func = radeon_reinitialize_M9P; rinfo->pm_mode |= radeon_pm_off; } diff --git a/drivers/video/fbdev/cg14.c b/drivers/video/fbdev/cg14.c index 9af54c2368fd..a6dce1a78490 100644 --- a/drivers/video/fbdev/cg14.c +++ b/drivers/video/fbdev/cg14.c @@ -486,8 +486,8 @@ static int cg14_probe(struct platform_device *op) info->var.xres); info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); - if (!strcmp(dp->parent->name, "sbus") || - !strcmp(dp->parent->name, "sbi")) { + if (of_node_name_eq(dp->parent, "sbus") || + of_node_name_eq(dp->parent, "sbi")) { info->fix.smem_start = op->resource[0].start; par->iospace = op->resource[0].flags & IORESOURCE_BITS; } else { diff --git a/drivers/video/fbdev/cg3.c b/drivers/video/fbdev/cg3.c index 1bd95b02f3aa..6d42def8436b 100644 --- a/drivers/video/fbdev/cg3.c +++ b/drivers/video/fbdev/cg3.c @@ -369,7 +369,7 @@ static int cg3_probe(struct platform_device *op) info->var.red.length = 8; info->var.green.length = 8; info->var.blue.length = 8; - if (!strcmp(dp->name, "cgRDI")) + if (of_node_name_eq(dp, "cgRDI")) par->flags |= CG3_FLAG_RDI; if (par->flags & CG3_FLAG_RDI) cg3_rdi_maybe_fixup_var(&info->var, dp); diff --git a/drivers/video/fbdev/chipsfb.c b/drivers/video/fbdev/chipsfb.c index 40182ed85648..ca549e1532e6 100644 --- a/drivers/video/fbdev/chipsfb.c +++ b/drivers/video/fbdev/chipsfb.c @@ -349,7 +349,7 @@ static void init_chips(struct fb_info *p, unsigned long addr) static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) { struct fb_info *p; - unsigned long addr, size; + unsigned long addr; unsigned short cmd; int rc = -ENODEV; @@ -361,7 +361,6 @@ static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) goto err_disable; addr = pci_resource_start(dp, 0); - size = pci_resource_len(dp, 0); if (addr == 0) goto err_disable; diff --git a/drivers/video/fbdev/core/fb_cmdline.c b/drivers/video/fbdev/core/fb_cmdline.c index 39509ccd92f1..3b5bd666b952 100644 --- a/drivers/video/fbdev/core/fb_cmdline.c +++ b/drivers/video/fbdev/core/fb_cmdline.c @@ -75,36 +75,33 @@ EXPORT_SYMBOL(fb_get_options); * NOTE: This function is a __setup and __init function. * It only stores the options. Drivers have to call * fb_get_options() as necessary. - * - * Returns zero. - * */ static int __init video_setup(char *options) { - int i, global = 0; - if (!options || !*options) - global = 1; + goto out; - if (!global && !strncmp(options, "ofonly", 6)) { + if (!strncmp(options, "ofonly", 6)) { ofonly = 1; - global = 1; + goto out; } - if (!global && !strchr(options, ':')) { - fb_mode_option = options; - global = 1; - } + if (strchr(options, ':')) { + /* named */ + int i; - if (!global) { for (i = 0; i < FB_MAX; i++) { if (video_options[i] == NULL) { video_options[i] = options; break; } } + } else { + /* global */ + fb_mode_option = options; } +out: return 1; } __setup("video=", video_setup); diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index bfa1360ec750..cd059a801662 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -656,11 +656,14 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, kfree(save); } + if (logo_shown == FBCON_LOGO_DONTSHOW) + return; + if (logo_lines > vc->vc_bottom) { logo_shown = FBCON_LOGO_CANSHOW; printk(KERN_INFO "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); - } else if (logo_shown != FBCON_LOGO_DONTSHOW) { + } else { logo_shown = FBCON_LOGO_DRAW; vc->vc_top = logo_lines; } @@ -999,7 +1002,7 @@ static const char *fbcon_startup(void) if (!softback_buf) { softback_buf = (unsigned long) - kmalloc(fbcon_softback_size, + kvmalloc(fbcon_softback_size, GFP_KERNEL); if (!softback_buf) { fbcon_softback_size = 0; @@ -1008,7 +1011,7 @@ static const char *fbcon_startup(void) } } else { if (softback_buf) { - kfree((void *) softback_buf); + kvfree((void *) softback_buf); softback_buf = 0; softback_top = 0; } @@ -1066,6 +1069,9 @@ static void fbcon_init(struct vc_data *vc, int init) cap = info->flags; + if (console_loglevel <= CONSOLE_LOGLEVEL_QUIET) + logo_shown = FBCON_LOGO_DONTSHOW; + if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || (info->fix.type == FB_TYPE_TEXT)) logo = 0; @@ -3672,7 +3678,7 @@ static void fbcon_exit(void) } #endif - kfree((void *)softback_buf); + kvfree((void *)softback_buf); softback_buf = 0UL; for_each_registered_fb(i) { diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index cb43a2258c51..4721491e6c8c 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -431,6 +431,9 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, { unsigned int x; + if (image->width > info->var.xres || image->height > info->var.yres) + return; + if (rotate == FB_ROTATE_UR) { for (x = 0; x < num && image->dx + image->width <= info->var.xres; diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index dd3128990776..3558a70a6664 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -978,6 +978,8 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) get_monspecs(edid, specs); specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs); + if (!specs->modedb) + return; /* * Workaround for buggy EDIDs that sets that the first diff --git a/drivers/video/fbdev/ffb.c b/drivers/video/fbdev/ffb.c index 6b1915872af1..b7aee0c427a8 100644 --- a/drivers/video/fbdev/ffb.c +++ b/drivers/video/fbdev/ffb.c @@ -944,7 +944,7 @@ static int ffb_probe(struct platform_device *op) info->var.accel_flags = FB_ACCELF_TEXT; - if (!strcmp(dp->name, "SUNW,afb")) + if (of_node_name_eq(dp, "SUNW,afb")) par->flags |= FFB_FLAG_AFB; par->board_type = of_getintprop_default(dp, "board_type", 0); diff --git a/drivers/video/fbdev/geode/gxfb_core.c b/drivers/video/fbdev/geode/gxfb_core.c index f4f76373b2a8..b1906cf5a8f0 100644 --- a/drivers/video/fbdev/geode/gxfb_core.c +++ b/drivers/video/fbdev/geode/gxfb_core.c @@ -33,6 +33,8 @@ #include <linux/pci.h> #include <linux/cs5535.h> +#include <asm/olpc.h> + #include "gxfb.h" static char *mode_option; @@ -107,9 +109,6 @@ static struct fb_videomode gx_modedb[] = { FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, }; -#ifdef CONFIG_OLPC -#include <asm/olpc.h> - static struct fb_videomode gx_dcon_modedb[] = { /* The only mode the DCON has is 1200x900 */ { NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3, @@ -128,14 +127,6 @@ static void get_modedb(struct fb_videomode **modedb, unsigned int *size) } } -#else -static void get_modedb(struct fb_videomode **modedb, unsigned int *size) -{ - *modedb = (struct fb_videomode *) gx_modedb; - *size = ARRAY_SIZE(gx_modedb); -} -#endif - static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { if (var->xres > 1600 || var->yres > 1200) diff --git a/drivers/video/fbdev/geode/lxfb_core.c b/drivers/video/fbdev/geode/lxfb_core.c index 138da6cb6cbc..17ab905811b1 100644 --- a/drivers/video/fbdev/geode/lxfb_core.c +++ b/drivers/video/fbdev/geode/lxfb_core.c @@ -23,6 +23,8 @@ #include <linux/pci.h> #include <linux/uaccess.h> +#include <asm/olpc.h> + #include "lxfb.h" static char *mode_option; @@ -216,9 +218,6 @@ static struct fb_videomode geode_modedb[] = { 0, FB_VMODE_NONINTERLACED, 0 }, }; -#ifdef CONFIG_OLPC -#include <asm/olpc.h> - static struct fb_videomode olpc_dcon_modedb[] = { /* The only mode the DCON has is 1200x900 */ { NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3, @@ -237,14 +236,6 @@ static void get_modedb(struct fb_videomode **modedb, unsigned int *size) } } -#else -static void get_modedb(struct fb_videomode **modedb, unsigned int *size) -{ - *modedb = (struct fb_videomode *) geode_modedb; - *size = ARRAY_SIZE(geode_modedb); -} -#endif - static int lxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { if (var->xres > 1920 || var->yres > 1440) diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c index 5d9670daf60e..4b9615e4ce74 100644 --- a/drivers/video/fbdev/imsttfb.c +++ b/drivers/video/fbdev/imsttfb.c @@ -1497,8 +1497,8 @@ static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) switch (pdev->device) { case PCI_DEVICE_ID_IMS_TT128: /* IMS,tt128mbA */ par->ramdac = IBM; - if (dp && ((strcmp(dp->name, "IMS,tt128mb8") == 0) || - (strcmp(dp->name, "IMS,tt128mb8A") == 0))) + if (of_node_name_eq(dp, "IMS,tt128mb8") || + of_node_name_eq(dp, "IMS,tt128mb8A")) par->ramdac = TVP; break; case PCI_DEVICE_ID_IMS_TT3D: /* IMS,tt3d */ diff --git a/drivers/video/fbdev/mbx/mbxdebugfs.c b/drivers/video/fbdev/mbx/mbxdebugfs.c index 2bd328883178..09af721638fb 100644 --- a/drivers/video/fbdev/mbx/mbxdebugfs.c +++ b/drivers/video/fbdev/mbx/mbxdebugfs.c @@ -211,36 +211,22 @@ static const struct file_operations misc_fops = { static void mbxfb_debugfs_init(struct fb_info *fbi) { struct mbxfb_info *mfbi = fbi->par; - struct mbxfb_debugfs_data *dbg; - - dbg = kzalloc(sizeof(struct mbxfb_debugfs_data), GFP_KERNEL); - mfbi->debugfs_data = dbg; - - dbg->dir = debugfs_create_dir("mbxfb", NULL); - dbg->sysconf = debugfs_create_file("sysconf", 0444, dbg->dir, - fbi, &sysconf_fops); - dbg->clock = debugfs_create_file("clock", 0444, dbg->dir, - fbi, &clock_fops); - dbg->display = debugfs_create_file("display", 0444, dbg->dir, - fbi, &display_fops); - dbg->gsctl = debugfs_create_file("gsctl", 0444, dbg->dir, - fbi, &gsctl_fops); - dbg->sdram = debugfs_create_file("sdram", 0444, dbg->dir, - fbi, &sdram_fops); - dbg->misc = debugfs_create_file("misc", 0444, dbg->dir, - fbi, &misc_fops); + struct dentry *dir; + + dir = debugfs_create_dir("mbxfb", NULL); + mfbi->debugfs_dir = dir; + + debugfs_create_file("sysconf", 0444, dir, fbi, &sysconf_fops); + debugfs_create_file("clock", 0444, dir, fbi, &clock_fops); + debugfs_create_file("display", 0444, dir, fbi, &display_fops); + debugfs_create_file("gsctl", 0444, dir, fbi, &gsctl_fops); + debugfs_create_file("sdram", 0444, dir, fbi, &sdram_fops); + debugfs_create_file("misc", 0444, dir, fbi, &misc_fops); } static void mbxfb_debugfs_remove(struct fb_info *fbi) { struct mbxfb_info *mfbi = fbi->par; - struct mbxfb_debugfs_data *dbg = mfbi->debugfs_data; - - debugfs_remove(dbg->misc); - debugfs_remove(dbg->sdram); - debugfs_remove(dbg->gsctl); - debugfs_remove(dbg->display); - debugfs_remove(dbg->clock); - debugfs_remove(dbg->sysconf); - debugfs_remove(dbg->dir); + + debugfs_remove_recursive(mfbi->debugfs_dir); } diff --git a/drivers/video/fbdev/mbx/mbxfb.c b/drivers/video/fbdev/mbx/mbxfb.c index 539b85da0897..6ded480a69b4 100644 --- a/drivers/video/fbdev/mbx/mbxfb.c +++ b/drivers/video/fbdev/mbx/mbxfb.c @@ -74,7 +74,7 @@ struct mbxfb_info { u32 pseudo_palette[MAX_PALETTES]; #ifdef CONFIG_FB_MBX_DEBUG - void *debugfs_data; + struct dentry *debugfs_dir; #endif }; diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index 057d3cdef92e..fbc6eafb63c7 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -141,6 +141,7 @@ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ out_le32(par->cmap_adr + 0x58, in_le32(par->cmap_adr + 0x58) & ~0x20); + /* fall through */ case cmap_r128: /* Set palette index & data */ out_8(par->cmap_adr + 0xb0, regno); @@ -210,6 +211,7 @@ static int offb_blank(int blank, struct fb_info *info) /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ out_le32(par->cmap_adr + 0x58, in_le32(par->cmap_adr + 0x58) & ~0x20); + /* fall through */ case cmap_r128: /* Set palette index & data */ out_8(par->cmap_adr + 0xb0, i); @@ -646,7 +648,7 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) } #endif /* kludge for valkyrie */ - if (strcmp(dp->name, "valkyrie") == 0) + if (of_node_name_eq(dp, "valkyrie")) address += 0x1000; offb_init_fb(no_real_node ? "bootx" : NULL, width, height, depth, pitch, address, diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c index b4bcf3a4a647..b5956a1a30d4 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c @@ -110,19 +110,12 @@ DEFINE_SHOW_ATTRIBUTE(dss); static struct dentry *dss_debugfs_dir; -static int dss_initialize_debugfs(void) +static void dss_initialize_debugfs(void) { dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); - if (IS_ERR(dss_debugfs_dir)) { - int err = PTR_ERR(dss_debugfs_dir); - dss_debugfs_dir = NULL; - return err; - } debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, &dss_debug_dump_clocks, &dss_fops); - - return 0; } static void dss_uninitialize_debugfs(void) @@ -130,26 +123,19 @@ static void dss_uninitialize_debugfs(void) debugfs_remove_recursive(dss_debugfs_dir); } -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) +void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) { - struct dentry *d; - - d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, - write, &dss_fops); - - return PTR_ERR_OR_ZERO(d); + debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, write, &dss_fops); } #else /* CONFIG_FB_OMAP2_DSS_DEBUGFS */ -static inline int dss_initialize_debugfs(void) +static inline void dss_initialize_debugfs(void) { - return 0; } static inline void dss_uninitialize_debugfs(void) { } -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) +void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) { - return 0; } #endif /* CONFIG_FB_OMAP2_DSS_DEBUGFS */ @@ -182,15 +168,11 @@ static struct notifier_block omap_dss_pm_notif_block = { static int __init omap_dss_probe(struct platform_device *pdev) { - int r; - core.pdev = pdev; dss_features_init(omapdss_get_version()); - r = dss_initialize_debugfs(); - if (r) - goto err_debugfs; + dss_initialize_debugfs(); if (def_disp_name) core.default_display_name = def_disp_name; @@ -198,10 +180,6 @@ static int __init omap_dss_probe(struct platform_device *pdev) register_pm_notifier(&omap_dss_pm_notif_block); return 0; - -err_debugfs: - - return r; } static int omap_dss_remove(struct platform_device *pdev) diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c index f1eb8b0f8a2a..5ce893c1923d 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c @@ -60,7 +60,7 @@ omapdss_of_get_next_port(const struct device_node *parent, return NULL; } prev = port; - } while (of_node_cmp(port->name, "port") != 0); + } while (!of_node_name_eq(port, "port")); of_node_put(ports); } @@ -83,7 +83,7 @@ omapdss_of_get_next_endpoint(const struct device_node *parent, if (!ep) return NULL; prev = ep; - } while (of_node_cmp(ep->name, "endpoint") != 0); + } while (!of_node_name_eq(ep, "endpoint")); return ep; } diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss.h b/drivers/video/fbdev/omap2/omapfb/dss/dss.h index a3cc0ca8f9d2..b1a354494144 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.h @@ -214,7 +214,7 @@ struct platform_device *dss_get_core_pdev(void); int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); int dss_set_min_bus_tput(struct device *dev, unsigned long tput); -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); +void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); /* display */ int dss_suspend_all_devices(void); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c index fa72e735dad2..d146793dd044 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c @@ -712,7 +712,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, else acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; /* - * The I2S input word length is twice the lenght given in the IEC-60958 + * The I2S input word length is twice the length given in the IEC-60958 * status word. If the word size is greater than * 20 bits, increment by one. */ diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 4061a20cfe24..3b361bc9feb8 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -667,10 +667,10 @@ static int ssd1307fb_probe(struct i2c_client *client, if (par->reset) { /* Reset the screen */ - gpiod_set_value_cansleep(par->reset, 0); - udelay(4); gpiod_set_value_cansleep(par->reset, 1); udelay(4); + gpiod_set_value_cansleep(par->reset, 0); + udelay(4); } if (par->vbat_reg) { diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c index 7bb7e90b8f00..bdf5a0ea876d 100644 --- a/drivers/video/fbdev/via/viafbdev.c +++ b/drivers/video/fbdev/via/viafbdev.c @@ -2110,7 +2110,7 @@ MODULE_PARM_DESC(viafb_lcd_panel_id, module_param(viafb_lcd_dsp_method, int, S_IRUSR); MODULE_PARM_DESC(viafb_lcd_dsp_method, - "Set Flat Panel display scaling method.(Default=Expandsion)"); + "Set Flat Panel display scaling method.(Default=Expansion)"); module_param(viafb_SAMM_ON, int, S_IRUSR); MODULE_PARM_DESC(viafb_SAMM_ON, diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 65c3c42b5e7c..242eea859637 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -241,6 +241,22 @@ config RAVE_SP_WATCHDOG help Support for the watchdog on RAVE SP device. +config MLX_WDT + tristate "Mellanox Watchdog" + depends on MELLANOX_PLATFORM + select WATCHDOG_CORE + select REGMAP + help + This is the driver for the hardware watchdog on Mellanox systems. + If you are going to use it, say Y here, otherwise N. + This driver can be used together with the watchdog daemon. + It can also watch your kernel to make sure it doesn't freeze, + and if it does, it reboots your system after a certain amount of + time. + + To compile this driver as a module, choose M here: the + module will be called mlx-wdt. + # ALPHA Architecture # ARM Architecture @@ -1157,7 +1173,7 @@ config HP_WATCHDOG select WATCHDOG_CORE depends on X86 && PCI help - A software monitoring watchdog and NMI sourcing driver. This driver + A software monitoring watchdog and NMI handling driver. This driver will detect lockups and provide a stack trace. This is a driver that will only load on an HP ProLiant system with a minimum of iLO2 support. To compile this driver as a module, choose M here: the module will be @@ -1175,12 +1191,13 @@ config KEMPLD_WDT called kempld_wdt. config HPWDT_NMI_DECODING - bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer" + bool "NMI support for the HP ProLiant iLO2+ Hardware Watchdog Timer" depends on HP_WATCHDOG default y help - When an NMI occurs this feature will make the necessary BIOS calls to - log the cause of the NMI. + Enables the NMI handler for the watchdog pretimeout NMI and the iLO + "Generate NMI to System" virtual button. When an NMI is claimed + by the driver, panic is called. config SC1200_WDT tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 4e78a8c73f0c..ba930e464657 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -142,6 +142,7 @@ obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o +obj-$(CONFIG_MLX_WDT) += mlx_wdt.o # M68K Architecture obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 501aebb5b81f..aa95f57cc1c3 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -16,8 +16,6 @@ * heartbeat requests after the watchdog device has been closed. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> diff --git a/drivers/watchdog/mlx_wdt.c b/drivers/watchdog/mlx_wdt.c new file mode 100644 index 000000000000..70c2cbf9c993 --- /dev/null +++ b/drivers/watchdog/mlx_wdt.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Mellanox watchdog driver + * + * Copyright (C) 2019 Mellanox Technologies + * Copyright (C) 2019 Michael Shych <mshych@mellanox.com> + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/platform_data/mlxreg.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/watchdog.h> + +#define MLXREG_WDT_CLOCK_SCALE 1000 +#define MLXREG_WDT_MAX_TIMEOUT_TYPE1 32 +#define MLXREG_WDT_MAX_TIMEOUT_TYPE2 255 +#define MLXREG_WDT_MIN_TIMEOUT 1 +#define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \ + WDIOF_SETTIMEOUT) + +/** + * struct mlxreg_wdt - wd private data: + * + * @wdd: watchdog device; + * @device: basic device; + * @pdata: data received from platform driver; + * @regmap: register map of parent device; + * @timeout: defined timeout in sec.; + * @action_idx: index for direct access to action register; + * @timeout_idx:index for direct access to TO register; + * @tleft_idx: index for direct access to time left register; + * @ping_idx: index for direct access to ping register; + * @reset_idx: index for direct access to reset cause register; + * @wd_type: watchdog HW type; + */ +struct mlxreg_wdt { + struct watchdog_device wdd; + struct mlxreg_core_platform_data *pdata; + void *regmap; + int action_idx; + int timeout_idx; + int tleft_idx; + int ping_idx; + int reset_idx; + enum mlxreg_wdt_type wdt_type; +}; + +static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt) +{ + struct mlxreg_core_data *reg_data; + u32 regval; + int rc; + + if (wdt->reset_idx == -EINVAL) + return; + + if (!(wdt->wdd.info->options & WDIOF_CARDRESET)) + return; + + reg_data = &wdt->pdata->data[wdt->reset_idx]; + rc = regmap_read(wdt->regmap, reg_data->reg, ®val); + if (!rc) { + if (regval & ~reg_data->mask) { + wdt->wdd.bootstatus = WDIOF_CARDRESET; + dev_info(wdt->wdd.parent, + "watchdog previously reset the CPU\n"); + } + } +} + +static int mlxreg_wdt_start(struct watchdog_device *wdd) +{ + struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); + struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx]; + + return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask, + BIT(reg_data->bit)); +} + +static int mlxreg_wdt_stop(struct watchdog_device *wdd) +{ + struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); + struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx]; + + return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask, + ~BIT(reg_data->bit)); +} + +static int mlxreg_wdt_ping(struct watchdog_device *wdd) +{ + struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); + struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx]; + + return regmap_update_bits_base(wdt->regmap, reg_data->reg, + ~reg_data->mask, BIT(reg_data->bit), + NULL, false, true); +} + +static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); + struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx]; + u32 regval, set_time, hw_timeout; + int rc; + + if (wdt->wdt_type == MLX_WDT_TYPE1) { + rc = regmap_read(wdt->regmap, reg_data->reg, ®val); + if (rc) + return rc; + + hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE); + regval = (regval & reg_data->mask) | hw_timeout; + /* Rowndown to actual closest number of sec. */ + set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE; + } else { + set_time = timeout; + regval = timeout; + } + + wdd->timeout = set_time; + rc = regmap_write(wdt->regmap, reg_data->reg, regval); + + if (!rc) { + /* + * Restart watchdog with new timeout period + * if watchdog is already started. + */ + if (watchdog_active(wdd)) { + rc = mlxreg_wdt_stop(wdd); + if (!rc) + rc = mlxreg_wdt_start(wdd); + } + } + + return rc; +} + +static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); + struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx]; + u32 regval; + int rc; + + rc = regmap_read(wdt->regmap, reg_data->reg, ®val); + /* Return 0 timeleft in case of failure register read. */ + return rc == 0 ? regval : 0; +} + +static const struct watchdog_ops mlxreg_wdt_ops_type1 = { + .start = mlxreg_wdt_start, + .stop = mlxreg_wdt_stop, + .ping = mlxreg_wdt_ping, + .set_timeout = mlxreg_wdt_set_timeout, + .owner = THIS_MODULE, +}; + +static const struct watchdog_ops mlxreg_wdt_ops_type2 = { + .start = mlxreg_wdt_start, + .stop = mlxreg_wdt_stop, + .ping = mlxreg_wdt_ping, + .set_timeout = mlxreg_wdt_set_timeout, + .get_timeleft = mlxreg_wdt_get_timeleft, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info mlxreg_wdt_main_info = { + .options = MLXREG_WDT_OPTIONS_BASE + | WDIOF_CARDRESET, + .identity = "mlx-wdt-main", +}; + +static const struct watchdog_info mlxreg_wdt_aux_info = { + .options = MLXREG_WDT_OPTIONS_BASE + | WDIOF_ALARMONLY, + .identity = "mlx-wdt-aux", +}; + +static void mlxreg_wdt_config(struct mlxreg_wdt *wdt, + struct mlxreg_core_platform_data *pdata) +{ + struct mlxreg_core_data *data = pdata->data; + int i; + + wdt->reset_idx = -EINVAL; + for (i = 0; i < pdata->counter; i++, data++) { + if (strnstr(data->label, "action", sizeof(data->label))) + wdt->action_idx = i; + else if (strnstr(data->label, "timeout", sizeof(data->label))) + wdt->timeout_idx = i; + else if (strnstr(data->label, "timeleft", sizeof(data->label))) + wdt->tleft_idx = i; + else if (strnstr(data->label, "ping", sizeof(data->label))) + wdt->ping_idx = i; + else if (strnstr(data->label, "reset", sizeof(data->label))) + wdt->reset_idx = i; + } + + wdt->pdata = pdata; + if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity, + sizeof(mlxreg_wdt_main_info.identity))) + wdt->wdd.info = &mlxreg_wdt_main_info; + else + wdt->wdd.info = &mlxreg_wdt_aux_info; + + wdt->wdt_type = pdata->version; + if (wdt->wdt_type == MLX_WDT_TYPE2) { + wdt->wdd.ops = &mlxreg_wdt_ops_type2; + wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2; + } else { + wdt->wdd.ops = &mlxreg_wdt_ops_type1; + wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1; + } + wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT; +} + +static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt, + struct mlxreg_core_platform_data *pdata) +{ + u32 timeout; + + timeout = pdata->data[wdt->timeout_idx].health_cntr; + return mlxreg_wdt_set_timeout(&wdt->wdd, timeout); +} + +static int mlxreg_wdt_probe(struct platform_device *pdev) +{ + struct mlxreg_core_platform_data *pdata; + struct mlxreg_wdt *wdt; + int rc; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "Failed to get platform data.\n"); + return -EINVAL; + } + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->wdd.parent = &pdev->dev; + wdt->regmap = pdata->regmap; + mlxreg_wdt_config(wdt, pdata); + + if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT)) + watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT); + watchdog_stop_on_reboot(&wdt->wdd); + watchdog_stop_on_unregister(&wdt->wdd); + watchdog_set_drvdata(&wdt->wdd, wdt); + rc = mlxreg_wdt_init_timeout(wdt, pdata); + if (rc) + goto register_error; + + if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) { + rc = mlxreg_wdt_start(&wdt->wdd); + if (rc) + goto register_error; + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); + } + mlxreg_wdt_check_card_reset(wdt); + rc = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); + +register_error: + if (rc) + dev_err(&pdev->dev, + "Cannot register watchdog device (err=%d)\n", rc); + return rc; +} + +static struct platform_driver mlxreg_wdt_driver = { + .probe = mlxreg_wdt_probe, + .driver = { + .name = "mlx-wdt", + }, +}; + +module_platform_driver(mlxreg_wdt_driver); + +MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>"); +MODULE_DESCRIPTION("Mellanox watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mlx-wdt"); diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index 06a892e36a8d..2ffa39b46970 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -437,7 +437,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd, return -EINVAL; timeout = new_timeout; pc87413_refresh(); - /* fall through and return the new timeout... */ + /* fall through - and return the new timeout... */ case WDIOC_GETTIMEOUT: new_timeout = timeout * 60; return put_user(new_timeout, uarg.i); diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index e0a6f8c0f03c..bb97f5b2f7eb 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -225,7 +225,7 @@ static int __init pikawdt_init(void) { struct device_node *np; void __iomem *fpga; - static u32 post1; + u32 post1; int ret; np = of_find_compatible_node(NULL, NULL, "pika,fpga"); diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 780971318810..5dfd604477a4 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -245,6 +245,28 @@ static int qcom_wdt_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused qcom_wdt_suspend(struct device *dev) +{ + struct qcom_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + qcom_wdt_stop(&wdt->wdd); + + return 0; +} + +static int __maybe_unused qcom_wdt_resume(struct device *dev) +{ + struct qcom_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + qcom_wdt_start(&wdt->wdd); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(qcom_wdt_pm_ops, qcom_wdt_suspend, qcom_wdt_resume); + static const struct of_device_id qcom_wdt_of_table[] = { { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr }, { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr }, @@ -259,6 +281,7 @@ static struct platform_driver qcom_watchdog_driver = { .driver = { .name = KBUILD_MODNAME, .of_match_table = qcom_wdt_of_table, + .pm = &qcom_wdt_pm_ops, }, }; module_platform_driver(qcom_watchdog_driver); diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c index 87333a41f753..72d15fd1f183 100644 --- a/drivers/watchdog/sbc60xxwdt.c +++ b/drivers/watchdog/sbc60xxwdt.c @@ -270,8 +270,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) timeout = new_timeout; wdt_keepalive(); - /* Fall through */ } + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c index 8e4e2fc13f87..e035a4d4b299 100644 --- a/drivers/watchdog/sc1200wdt.c +++ b/drivers/watchdog/sc1200wdt.c @@ -239,7 +239,7 @@ static long sc1200wdt_ioctl(struct file *file, unsigned int cmd, return -EINVAL; timeout = new_timeout; sc1200wdt_write_data(WDTO, timeout); - /* fall through and return the new timeout */ + /* fall through - and return the new timeout */ case WDIOC_GETTIMEOUT: return put_user(timeout * 60, p); diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c index 6aadb56e7faa..403542f9ed8d 100644 --- a/drivers/watchdog/sc520_wdt.c +++ b/drivers/watchdog/sc520_wdt.c @@ -324,8 +324,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; wdt_keepalive(); - /* Fall through */ } + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index 445ea1ad1fa9..c768dcd53034 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -478,7 +478,7 @@ static long wb_smsc_wdt_ioctl(struct file *file, return -EINVAL; timeout = new_timeout; wb_smsc_wdt_set_timeout(timeout); - /* fall through and return the new timeout... */ + /* fall through - and return the new timeout... */ case WDIOC_GETTIMEOUT: new_timeout = timeout; if (unit == UNIT_MINUTE) diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c index 05658ecc0aa4..db9b6488e388 100644 --- a/drivers/watchdog/w83877f_wdt.c +++ b/drivers/watchdog/w83877f_wdt.c @@ -292,8 +292,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) timeout = new_timeout; wdt_keepalive(); - /* Fall through */ } + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index c48927a58e10..ad3844d9f876 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o -obj-$(CONFIG_X86) += fallback.o obj-y += grant-table.o features.o balloon.o manage.o preempt.o time.o obj-y += mem-reservation.o obj-y += events/ diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 39b229f9e256..d37dd5bb7a8f 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -604,6 +604,7 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages) while (pgno < nr_pages) { page = balloon_retrieve(true); if (page) { + __ClearPageOffline(page); pages[pgno++] = page; #ifdef CONFIG_XEN_HAVE_PVMMU /* @@ -645,8 +646,10 @@ void free_xenballooned_pages(int nr_pages, struct page **pages) mutex_lock(&balloon_mutex); for (i = 0; i < nr_pages; i++) { - if (pages[i]) + if (pages[i]) { + __SetPageOffline(pages[i]); balloon_append(pages[i]); + } } balloon_stats.target_unpopulated -= nr_pages; diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index b1357aa4bc55..f192b6f42da9 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -54,7 +54,7 @@ static int vcpu_online(unsigned int cpu) } static void vcpu_hotplug(unsigned int cpu) { - if (!cpu_possible(cpu)) + if (cpu >= nr_cpu_ids || !cpu_possible(cpu)) return; switch (vcpu_online(cpu)) { diff --git a/drivers/xen/fallback.c b/drivers/xen/fallback.c deleted file mode 100644 index b04fb64c5a91..000000000000 --- a/drivers/xen/fallback.c +++ /dev/null @@ -1,81 +0,0 @@ -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/bug.h> -#include <linux/export.h> -#include <asm/hypervisor.h> -#include <asm/xen/hypercall.h> - -int xen_event_channel_op_compat(int cmd, void *arg) -{ - struct evtchn_op op; - int rc; - - op.cmd = cmd; - memcpy(&op.u, arg, sizeof(op.u)); - rc = _hypercall1(int, event_channel_op_compat, &op); - - switch (cmd) { - case EVTCHNOP_close: - case EVTCHNOP_send: - case EVTCHNOP_bind_vcpu: - case EVTCHNOP_unmask: - /* no output */ - break; - -#define COPY_BACK(eop) \ - case EVTCHNOP_##eop: \ - memcpy(arg, &op.u.eop, sizeof(op.u.eop)); \ - break - - COPY_BACK(bind_interdomain); - COPY_BACK(bind_virq); - COPY_BACK(bind_pirq); - COPY_BACK(status); - COPY_BACK(alloc_unbound); - COPY_BACK(bind_ipi); -#undef COPY_BACK - - default: - WARN_ON(rc != -ENOSYS); - break; - } - - return rc; -} -EXPORT_SYMBOL_GPL(xen_event_channel_op_compat); - -int xen_physdev_op_compat(int cmd, void *arg) -{ - struct physdev_op op; - int rc; - - op.cmd = cmd; - memcpy(&op.u, arg, sizeof(op.u)); - rc = _hypercall1(int, physdev_op_compat, &op); - - switch (cmd) { - case PHYSDEVOP_IRQ_UNMASK_NOTIFY: - case PHYSDEVOP_set_iopl: - case PHYSDEVOP_set_iobitmap: - case PHYSDEVOP_apic_write: - /* no output */ - break; - -#define COPY_BACK(pop, fld) \ - case PHYSDEVOP_##pop: \ - memcpy(arg, &op.u.fld, sizeof(op.u.fld)); \ - break - - COPY_BACK(irq_status_query, irq_status_query); - COPY_BACK(apic_read, apic_op); - COPY_BACK(ASSIGN_VECTOR, irq_op); -#undef COPY_BACK - - default: - WARN_ON(rc != -ENOSYS); - break; - } - - return rc; -} -EXPORT_SYMBOL_GPL(xen_physdev_op_compat); diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c index cba6b586bfbd..2c4f324f8626 100644 --- a/drivers/xen/gntdev-dmabuf.c +++ b/drivers/xen/gntdev-dmabuf.c @@ -80,6 +80,12 @@ struct gntdev_dmabuf_priv { struct list_head imp_list; /* This is the lock which protects dma_buf_xxx lists. */ struct mutex lock; + /* + * We reference this file while exporting dma-bufs, so + * the grant device context is not destroyed while there are + * external users alive. + */ + struct file *filp; }; /* DMA buffer export support. */ @@ -311,6 +317,7 @@ static void dmabuf_exp_release(struct kref *kref) dmabuf_exp_wait_obj_signal(gntdev_dmabuf->priv, gntdev_dmabuf); list_del(&gntdev_dmabuf->next); + fput(gntdev_dmabuf->priv->filp); kfree(gntdev_dmabuf); } @@ -423,6 +430,7 @@ static int dmabuf_exp_from_pages(struct gntdev_dmabuf_export_args *args) mutex_lock(&args->dmabuf_priv->lock); list_add(&gntdev_dmabuf->next, &args->dmabuf_priv->exp_list); mutex_unlock(&args->dmabuf_priv->lock); + get_file(gntdev_dmabuf->priv->filp); return 0; fail: @@ -737,6 +745,14 @@ static int dmabuf_imp_release(struct gntdev_dmabuf_priv *priv, u32 fd) return 0; } +static void dmabuf_imp_release_all(struct gntdev_dmabuf_priv *priv) +{ + struct gntdev_dmabuf *q, *gntdev_dmabuf; + + list_for_each_entry_safe(gntdev_dmabuf, q, &priv->imp_list, next) + dmabuf_imp_release(priv, gntdev_dmabuf->fd); +} + /* DMA buffer IOCTL support. */ long gntdev_ioctl_dmabuf_exp_from_refs(struct gntdev_priv *priv, int use_ptemod, @@ -834,7 +850,7 @@ long gntdev_ioctl_dmabuf_imp_release(struct gntdev_priv *priv, return dmabuf_imp_release(priv->dmabuf_priv, op.fd); } -struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void) +struct gntdev_dmabuf_priv *gntdev_dmabuf_init(struct file *filp) { struct gntdev_dmabuf_priv *priv; @@ -847,10 +863,13 @@ struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void) INIT_LIST_HEAD(&priv->exp_wait_list); INIT_LIST_HEAD(&priv->imp_list); + priv->filp = filp; + return priv; } void gntdev_dmabuf_fini(struct gntdev_dmabuf_priv *priv) { + dmabuf_imp_release_all(priv); kfree(priv); } diff --git a/drivers/xen/gntdev-dmabuf.h b/drivers/xen/gntdev-dmabuf.h index 7220a53d0fc5..3d9b9cf9d5a1 100644 --- a/drivers/xen/gntdev-dmabuf.h +++ b/drivers/xen/gntdev-dmabuf.h @@ -14,7 +14,7 @@ struct gntdev_dmabuf_priv; struct gntdev_priv; -struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void); +struct gntdev_dmabuf_priv *gntdev_dmabuf_init(struct file *filp); void gntdev_dmabuf_fini(struct gntdev_dmabuf_priv *priv); diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 5efc5eee9544..7cf9c51318aa 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -600,7 +600,7 @@ static int gntdev_open(struct inode *inode, struct file *flip) mutex_init(&priv->lock); #ifdef CONFIG_XEN_GNTDEV_DMABUF - priv->dmabuf_priv = gntdev_dmabuf_init(); + priv->dmabuf_priv = gntdev_dmabuf_init(flip); if (IS_ERR(priv->dmabuf_priv)) { ret = PTR_ERR(priv->dmabuf_priv); kfree(priv); diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index bb7888429be6..877baf2a94f4 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -214,10 +214,13 @@ retry: /* * Get IO TLB memory from any location. */ - if (early) + if (early) { xen_io_tlb_start = memblock_alloc(PAGE_ALIGN(bytes), PAGE_SIZE); - else { + if (!xen_io_tlb_start) + panic("%s: Failed to allocate %lu bytes align=0x%lx\n", + __func__, PAGE_ALIGN(bytes), PAGE_SIZE); + } else { #define SLABS_PER_PAGE (1 << (PAGE_SHIFT - IO_TLB_SHIFT)) #define IO_TLB_MIN_SLABS ((1<<20) >> IO_TLB_SHIFT) while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) { diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index fbb9137c7d02..98e35644fda7 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -410,21 +410,21 @@ static int check_acpi_ids(struct acpi_processor *pr_backup) /* All online CPUs have been processed at this stage. Now verify * whether in fact "online CPUs" == physical CPUs. */ - acpi_id_present = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL); + acpi_id_present = bitmap_zalloc(nr_acpi_bits, GFP_KERNEL); if (!acpi_id_present) return -ENOMEM; - acpi_id_cst_present = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL); + acpi_id_cst_present = bitmap_zalloc(nr_acpi_bits, GFP_KERNEL); if (!acpi_id_cst_present) { - kfree(acpi_id_present); + bitmap_free(acpi_id_present); return -ENOMEM; } acpi_psd = kcalloc(nr_acpi_bits, sizeof(struct acpi_psd_package), GFP_KERNEL); if (!acpi_psd) { - kfree(acpi_id_present); - kfree(acpi_id_cst_present); + bitmap_free(acpi_id_present); + bitmap_free(acpi_id_cst_present); return -ENOMEM; } @@ -533,14 +533,14 @@ static int __init xen_acpi_processor_init(void) return -ENODEV; nr_acpi_bits = get_max_acpi_id() + 1; - acpi_ids_done = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL); + acpi_ids_done = bitmap_zalloc(nr_acpi_bits, GFP_KERNEL); if (!acpi_ids_done) return -ENOMEM; acpi_perf_data = alloc_percpu(struct acpi_processor_performance); if (!acpi_perf_data) { pr_debug("Memory allocation error for acpi_perf_data\n"); - kfree(acpi_ids_done); + bitmap_free(acpi_ids_done); return -ENOMEM; } for_each_possible_cpu(i) { @@ -584,7 +584,7 @@ err_unregister: err_out: /* Freeing a NULL pointer is OK: alloc_percpu zeroes. */ free_acpi_perf_data(); - kfree(acpi_ids_done); + bitmap_free(acpi_ids_done); return rc; } static void __exit xen_acpi_processor_exit(void) @@ -592,9 +592,9 @@ static void __exit xen_acpi_processor_exit(void) int i; unregister_syscore_ops(&xap_syscore_ops); - kfree(acpi_ids_done); - kfree(acpi_id_present); - kfree(acpi_id_cst_present); + bitmap_free(acpi_ids_done); + bitmap_free(acpi_id_present); + bitmap_free(acpi_id_cst_present); kfree(acpi_psd); for_each_possible_cpu(i) acpi_processor_unregister_performance(i); diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index 2acbfe104e46..a67236b02452 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -37,6 +37,7 @@ #include <linux/mm_types.h> #include <linux/init.h> #include <linux/capability.h> +#include <linux/memory_hotplug.h> #include <xen/xen.h> #include <xen/interface/xen.h> @@ -50,6 +51,10 @@ #define BALLOON_CLASS_NAME "xen_memory" +#ifdef CONFIG_MEMORY_HOTPLUG +u64 xen_saved_max_mem_size = 0; +#endif + static struct device balloon_dev; static int register_balloon(struct device *dev); @@ -63,6 +68,12 @@ static void watch_target(struct xenbus_watch *watch, static bool watch_fired; static long target_diff; +#ifdef CONFIG_MEMORY_HOTPLUG + /* The balloon driver will take care of adding memory now. */ + if (xen_saved_max_mem_size) + max_mem_size = xen_saved_max_mem_size; +#endif + err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target); if (err != 1) { /* This is ok (for domain0 at least) - so just return */ diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index ea4a08b83fa0..787966f44589 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -127,8 +127,6 @@ void xen_pcibk_reset_device(struct pci_dev *dev) if (pci_is_enabled(dev)) pci_disable_device(dev); - pci_write_config_word(dev, PCI_COMMAND, 0); - dev->is_busmaster = 0; } else { pci_read_config_word(dev, PCI_COMMAND, &cmd); diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index 581c4e1a8b82..23f7f6ec7d1f 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -544,7 +544,7 @@ static void xen_pcibk_frontend_changed(struct xenbus_device *xdev, xenbus_switch_state(xdev, XenbusStateClosed); if (xenbus_dev_is_online(xdev)) break; - /* fall through if not online */ + /* fall through - if not online */ case XenbusStateUnknown: dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); device_unregister(&xdev->dev); diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index e59937293a32..ba0942e481bc 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -1184,7 +1184,7 @@ static void scsiback_frontend_changed(struct xenbus_device *dev, xenbus_switch_state(dev, XenbusStateClosed); if (xenbus_dev_is_online(dev)) break; - /* fall through if not online */ + /* fall through - if not online */ case XenbusStateUnknown: device_unregister(&dev->dev); break; |