From 5ee8f30fd1b558af6832e732bc0abe4463de8c27 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 28 Sep 2018 10:26:55 -0700 Subject: New rr-cache entries from ci-merge Signed-off-by: Bjorn Andersson --- .../thisimage | 408 +++++++ .../thisimage | 1284 ++++++++++++++++++++ .../thisimage | 650 ++++++++++ .../thisimage | 142 +++ .../thisimage | 795 ++++++++++++ .../thisimage | 1113 +++++++++++++++++ 6 files changed, 4392 insertions(+) create mode 100644 rr-cache/0e9015a648e645b6a0d77f2ba63448810d7162f6/thisimage create mode 100644 rr-cache/118cd054ba05d92106ce317a9fb6db6c3f30ea33/thisimage create mode 100644 rr-cache/4c17c8b289a6bfbb3811a8e73f904b12cc8266f6/thisimage create mode 100644 rr-cache/4f2f9c338bac6803576b99dbedbc648d8d33dd33/thisimage create mode 100644 rr-cache/9a750d358b41d2247d9104afed62b9810c183004/thisimage create mode 100644 rr-cache/d5ab1173612d441f2265071527c6ed2b444e753b/thisimage diff --git a/rr-cache/0e9015a648e645b6a0d77f2ba63448810d7162f6/thisimage b/rr-cache/0e9015a648e645b6a0d77f2ba63448810d7162f6/thisimage new file mode 100644 index 0000000..d0c4325 --- /dev/null +++ b/rr-cache/0e9015a648e645b6a0d77f2ba63448810d7162f6/thisimage @@ -0,0 +1,408 @@ +/* + * Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996 + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * 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. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_common.h" +#include "qcom_q6v5.h" +#include "remoteproc_internal.h" + +struct adsp_data { + int crash_reason_smem; + const char *firmware_name; + int pas_id; + bool has_aggre2_clk; + + const char *ssr_name; + const char *sysmon_name; + int ssctl_id; +}; + +struct qcom_adsp { + struct device *dev; + struct rproc *rproc; + + struct qcom_q6v5 q6v5; + + struct clk *xo; + struct clk *aggre2_clk; + + struct regulator *cx_supply; + struct regulator *px_supply; + + int pas_id; + int crash_reason_smem; + bool has_aggre2_clk; + + struct completion start_done; + struct completion stop_done; + + phys_addr_t mem_phys; + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; + + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_subdev smd_subdev; + struct qcom_rproc_ssr ssr_subdev; + struct qcom_sysmon *sysmon; +}; + +static int adsp_load(struct rproc *rproc, const struct firmware *fw) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + + return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id, + adsp->mem_region, adsp->mem_phys, adsp->mem_size, + &adsp->mem_reloc); + +} + +static int adsp_start(struct rproc *rproc) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int ret; + + qcom_q6v5_prepare(&adsp->q6v5); + + ret = clk_prepare_enable(adsp->xo); + if (ret) + return ret; + + ret = clk_prepare_enable(adsp->aggre2_clk); + if (ret) + goto disable_xo_clk; + + ret = regulator_enable(adsp->cx_supply); + if (ret) + goto disable_aggre2_clk; + + ret = regulator_enable(adsp->px_supply); + if (ret) + goto disable_cx_supply; + + ret = qcom_scm_pas_auth_and_reset(adsp->pas_id); + if (ret) { + dev_err(adsp->dev, + "failed to authenticate image and release reset\n"); + goto disable_px_supply; + } + + ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); + if (ret == -ETIMEDOUT) { + dev_err(adsp->dev, "start timed out\n"); + qcom_scm_pas_shutdown(adsp->pas_id); + goto disable_px_supply; + } + + return 0; + +disable_px_supply: + regulator_disable(adsp->px_supply); +disable_cx_supply: + regulator_disable(adsp->cx_supply); +disable_aggre2_clk: + clk_disable_unprepare(adsp->aggre2_clk); +disable_xo_clk: + clk_disable_unprepare(adsp->xo); + + return ret; +} + +static void qcom_pas_handover(struct qcom_q6v5 *q6v5) +{ + struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5); + + regulator_disable(adsp->px_supply); + regulator_disable(adsp->cx_supply); + clk_disable_unprepare(adsp->aggre2_clk); + clk_disable_unprepare(adsp->xo); +} + +static int adsp_stop(struct rproc *rproc) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int handover; + int ret; + + ret = qcom_q6v5_request_stop(&adsp->q6v5); + if (ret == -ETIMEDOUT) + dev_err(adsp->dev, "timed out on wait\n"); + + ret = qcom_scm_pas_shutdown(adsp->pas_id); + if (ret) + dev_err(adsp->dev, "failed to shutdown: %d\n", ret); + + handover = qcom_q6v5_unprepare(&adsp->q6v5); + if (handover) + qcom_pas_handover(&adsp->q6v5); + + return ret; +} + +static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int offset; + + offset = da - adsp->mem_reloc; + if (offset < 0 || offset + len > adsp->mem_size) + return NULL; + + return adsp->mem_region + offset; +} + +static const struct rproc_ops adsp_ops = { + .start = adsp_start, + .stop = adsp_stop, + .da_to_va = adsp_da_to_va, + .parse_fw = qcom_register_dump_segments, + .load = adsp_load, +}; + +static int adsp_init_clock(struct qcom_adsp *adsp) +{ + int ret; + + adsp->xo = devm_clk_get(adsp->dev, "xo"); + if (IS_ERR(adsp->xo)) { + ret = PTR_ERR(adsp->xo); + if (ret != -EPROBE_DEFER) + dev_err(adsp->dev, "failed to get xo clock"); + return ret; + } + + if (adsp->has_aggre2_clk) { + adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2"); + if (IS_ERR(adsp->aggre2_clk)) { + ret = PTR_ERR(adsp->aggre2_clk); + if (ret != -EPROBE_DEFER) + dev_err(adsp->dev, + "failed to get aggre2 clock"); + return ret; + } + } + + return 0; +} + +static int adsp_init_regulator(struct qcom_adsp *adsp) +{ + adsp->cx_supply = devm_regulator_get(adsp->dev, "cx"); + if (IS_ERR(adsp->cx_supply)) + return PTR_ERR(adsp->cx_supply); + + regulator_set_load(adsp->cx_supply, 100000); + + adsp->px_supply = devm_regulator_get(adsp->dev, "px"); + return PTR_ERR_OR_ZERO(adsp->px_supply); +} + +static int adsp_alloc_memory_region(struct qcom_adsp *adsp) +{ + struct device_node *node; + struct resource r; + int ret; + + node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0); + if (!node) { + dev_err(adsp->dev, "no memory-region specified\n"); + return -EINVAL; + } + + ret = of_address_to_resource(node, 0, &r); + if (ret) + return ret; + + adsp->mem_phys = adsp->mem_reloc = r.start; + adsp->mem_size = resource_size(&r); + adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size); + if (!adsp->mem_region) { + dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n", + &r.start, adsp->mem_size); + return -EBUSY; + } + + return 0; +} + +static int adsp_probe(struct platform_device *pdev) +{ + const struct adsp_data *desc; + struct qcom_adsp *adsp; + struct rproc *rproc; + int ret; + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, + desc->firmware_name, sizeof(*adsp)); + if (!rproc) { + dev_err(&pdev->dev, "unable to allocate remoteproc\n"); + return -ENOMEM; + } + + adsp = (struct qcom_adsp *)rproc->priv; + adsp->dev = &pdev->dev; + adsp->rproc = rproc; + adsp->pas_id = desc->pas_id; + adsp->has_aggre2_clk = desc->has_aggre2_clk; + platform_set_drvdata(pdev, adsp); + + ret = adsp_alloc_memory_region(adsp); + if (ret) + goto free_rproc; + + ret = adsp_init_clock(adsp); + if (ret) + goto free_rproc; + + ret = adsp_init_regulator(adsp); + if (ret) + goto free_rproc; + + ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, + qcom_pas_handover); + if (ret) + goto free_rproc; + + qcom_add_glink_subdev(rproc, &adsp->glink_subdev); + qcom_add_smd_subdev(rproc, &adsp->smd_subdev); + qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); + adsp->sysmon = qcom_add_sysmon_subdev(rproc, + desc->sysmon_name, + desc->ssctl_id); + + ret = rproc_add(rproc); + if (ret) + goto free_rproc; + + return 0; + +free_rproc: + rproc_free(rproc); + + return ret; +} + +static int adsp_remove(struct platform_device *pdev) +{ + struct qcom_adsp *adsp = platform_get_drvdata(pdev); + + rproc_del(adsp->rproc); + + qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); + qcom_remove_sysmon_subdev(adsp->sysmon); + qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); + qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); + rproc_free(adsp->rproc); + + return 0; +} + +static const struct adsp_data adsp_resource_init = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mdt", + .pas_id = 1, + .has_aggre2_clk = false, + .ssr_name = "lpass", + .sysmon_name = "adsp", + .ssctl_id = 0x14, +}; + +static const struct adsp_data cdsp_resource_init = { +<<<<<<< + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .pas_id = 18, + .has_aggre2_clk = false, + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .ssctl_id = 0x17, +======= + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .pas_id = 18, + .has_aggre2_clk = false, + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .ssctl_id = 0x17, +>>>>>>> +}; + +static const struct adsp_data slpi_resource_init = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = true, + .ssr_name = "dsps", + .sysmon_name = "slpi", + .ssctl_id = 0x16, +}; + +static const struct adsp_data wcss_resource_init = { + .crash_reason_smem = 421, + .firmware_name = "wcnss.mdt", + .pas_id = 6, + .ssr_name = "mpss", + .sysmon_name = "wcnss", + .ssctl_id = 0x12, +}; + +static const struct of_device_id adsp_of_match[] = { + { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init}, + { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init}, + { .compatible = "qcom,sdm845-adsp-pas", .data = &adsp_resource_init}, + { .compatible = "qcom,sdm845-cdsp-pas", .data = &cdsp_resource_init}, + { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init}, + { .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init }, + { .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init }, + { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init }, + { }, +}; +MODULE_DEVICE_TABLE(of, adsp_of_match); + +static struct platform_driver adsp_driver = { + .probe = adsp_probe, + .remove = adsp_remove, + .driver = { + .name = "qcom_adsp_pil", + .of_match_table = adsp_of_match, + }, +}; + +module_platform_driver(adsp_driver); +MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader"); +MODULE_LICENSE("GPL v2"); diff --git a/rr-cache/118cd054ba05d92106ce317a9fb6db6c3f30ea33/thisimage b/rr-cache/118cd054ba05d92106ce317a9fb6db6c3f30ea33/thisimage new file mode 100644 index 0000000..87c3f2c --- /dev/null +++ b/rr-cache/118cd054ba05d92106ce317a9fb6db6c3f30ea33/thisimage @@ -0,0 +1,1284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * main.c - Multi purpose firmware loading support + * + * Copyright (c) 2003 Manuel Estrada Sainz + * + * Please see Documentation/firmware_class/ for more information. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../base.h" +#include "firmware.h" +#include "fallback.h" + +MODULE_AUTHOR("Manuel Estrada Sainz"); +MODULE_DESCRIPTION("Multi purpose firmware loading support"); +MODULE_LICENSE("GPL"); + +struct firmware_cache { + /* firmware_buf instance will be added into the below list */ + spinlock_t lock; + struct list_head head; + int state; + +#ifdef CONFIG_PM_SLEEP + /* + * Names of firmware images which have been cached successfully + * will be added into the below list so that device uncache + * helper can trace which firmware images have been cached + * before. + */ + spinlock_t name_lock; + struct list_head fw_names; + + struct delayed_work work; + + struct notifier_block pm_notify; +#endif +}; + +struct fw_cache_entry { + struct list_head list; + const char *name; +}; + +struct fw_name_devm { + unsigned long magic; + const char *name; +}; + +static inline struct fw_priv *to_fw_priv(struct kref *ref) +{ + return container_of(ref, struct fw_priv, ref); +} + +#define FW_LOADER_NO_CACHE 0 +#define FW_LOADER_START_CACHE 1 + +/* fw_lock could be moved to 'struct fw_sysfs' but since it is just + * guarding for corner cases a global lock should be OK */ +DEFINE_MUTEX(fw_lock); + +static struct firmware_cache fw_cache; + +/* Builtin firmware support */ + +#ifdef CONFIG_FW_LOADER + +extern struct builtin_fw __start_builtin_fw[]; +extern struct builtin_fw __end_builtin_fw[]; + +static void fw_copy_to_prealloc_buf(struct firmware *fw, + void *buf, size_t size) +{ + if (!buf || size < fw->size) + return; + memcpy(buf, fw->data, fw->size); +} + +static bool fw_get_builtin_firmware(struct firmware *fw, const char *name, + void *buf, size_t size) +{ + struct builtin_fw *b_fw; + + for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) { + if (strcmp(name, b_fw->name) == 0) { + fw->size = b_fw->size; + fw->data = b_fw->data; + fw_copy_to_prealloc_buf(fw, buf, size); + + return true; + } + } + + return false; +} + +static bool fw_is_builtin_firmware(const struct firmware *fw) +{ + struct builtin_fw *b_fw; + + for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) + if (fw->data == b_fw->data) + return true; + + return false; +} + +#else /* Module case - no builtin firmware support */ + +static inline bool fw_get_builtin_firmware(struct firmware *fw, + const char *name, void *buf, + size_t size) +{ + return false; +} + +static inline bool fw_is_builtin_firmware(const struct firmware *fw) +{ + return false; +} +#endif + +static void fw_state_init(struct fw_priv *fw_priv) +{ + struct fw_state *fw_st = &fw_priv->fw_st; + + init_completion(&fw_st->completion); + fw_st->status = FW_STATUS_UNKNOWN; +} + +static inline int fw_state_wait(struct fw_priv *fw_priv) +{ + return __fw_state_wait_common(fw_priv, MAX_SCHEDULE_TIMEOUT); +} + +static int fw_cache_piggyback_on_request(const char *name); + +static struct fw_priv *__allocate_fw_priv(const char *fw_name, + struct firmware_cache *fwc, + void *dbuf, size_t size) +{ + struct fw_priv *fw_priv; + + fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC); + if (!fw_priv) + return NULL; + + fw_priv->fw_name = kstrdup_const(fw_name, GFP_ATOMIC); + if (!fw_priv->fw_name) { + kfree(fw_priv); + return NULL; + } + + kref_init(&fw_priv->ref); + fw_priv->fwc = fwc; + fw_priv->data = dbuf; + fw_priv->allocated_size = size; + fw_state_init(fw_priv); +#ifdef CONFIG_FW_LOADER_USER_HELPER + INIT_LIST_HEAD(&fw_priv->pending_list); +#endif + + pr_debug("%s: fw-%s fw_priv=%p\n", __func__, fw_name, fw_priv); + + return fw_priv; +} + +static struct fw_priv *__lookup_fw_priv(const char *fw_name) +{ + struct fw_priv *tmp; + struct firmware_cache *fwc = &fw_cache; + + list_for_each_entry(tmp, &fwc->head, list) + if (!strcmp(tmp->fw_name, fw_name)) + return tmp; + return NULL; +} + +/* Returns 1 for batching firmware requests with the same name */ +static int alloc_lookup_fw_priv(const char *fw_name, + struct firmware_cache *fwc, + struct fw_priv **fw_priv, void *dbuf, + size_t size, enum fw_opt opt_flags) +{ + struct fw_priv *tmp; + + spin_lock(&fwc->lock); + if (!(opt_flags & FW_OPT_NOCACHE)) { + tmp = __lookup_fw_priv(fw_name); + if (tmp) { + kref_get(&tmp->ref); + spin_unlock(&fwc->lock); + *fw_priv = tmp; + pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n"); + return 1; + } + } + + tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size); +<<<<<<< + if (tmp && !(opt_flags & FW_OPT_NOCACHE)) + list_add(&tmp->list, &fwc->head); +======= + if (tmp) { + INIT_LIST_HEAD(&tmp->list); + if (!(opt_flags & FW_OPT_NOCACHE)) + list_add(&tmp->list, &fwc->head); + } +>>>>>>> + spin_unlock(&fwc->lock); + + *fw_priv = tmp; + + return tmp ? 0 : -ENOMEM; +} + +static void __free_fw_priv(struct kref *ref) + __releases(&fwc->lock) +{ + struct fw_priv *fw_priv = to_fw_priv(ref); + struct firmware_cache *fwc = fw_priv->fwc; + + pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n", + __func__, fw_priv->fw_name, fw_priv, fw_priv->data, + (unsigned int)fw_priv->size); + + list_del(&fw_priv->list); + spin_unlock(&fwc->lock); + +#ifdef CONFIG_FW_LOADER_USER_HELPER + if (fw_priv->is_paged_buf) { + int i; + vunmap(fw_priv->data); + for (i = 0; i < fw_priv->nr_pages; i++) + __free_page(fw_priv->pages[i]); + vfree(fw_priv->pages); + } else +#endif + if (!fw_priv->allocated_size) + vfree(fw_priv->data); + kfree_const(fw_priv->fw_name); + kfree(fw_priv); +} + +static void free_fw_priv(struct fw_priv *fw_priv) +{ + struct firmware_cache *fwc = fw_priv->fwc; + spin_lock(&fwc->lock); + if (!kref_put(&fw_priv->ref, __free_fw_priv)) + spin_unlock(&fwc->lock); +} + +/* direct firmware loading support */ +static char fw_path_para[256]; +static const char * const fw_path[] = { + fw_path_para, + "/lib/firmware/updates/" UTS_RELEASE, + "/lib/firmware/updates", + "/lib/firmware/" UTS_RELEASE, + "/lib/firmware" +}; + +/* + * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH' + * from kernel command line because firmware_class is generally built in + * kernel instead of module. + */ +module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); +MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); + +static int +fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv) +{ + loff_t size; + int i, len; + int rc = -ENOENT; + char *path; + enum kernel_read_file_id id = READING_FIRMWARE; + size_t msize = INT_MAX; + + /* Already populated data member means we're loading into a buffer */ + if (fw_priv->data) { + id = READING_FIRMWARE_PREALLOC_BUFFER; + msize = fw_priv->allocated_size; + } + + path = __getname(); + if (!path) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(fw_path); i++) { + /* skip the unset customized path */ + if (!fw_path[i][0]) + continue; + + len = snprintf(path, PATH_MAX, "%s/%s", + fw_path[i], fw_priv->fw_name); + if (len >= PATH_MAX) { + rc = -ENAMETOOLONG; + break; + } + + fw_priv->size = 0; + rc = kernel_read_file_from_path(path, &fw_priv->data, &size, + msize, id); + if (rc) { + if (rc == -ENOENT) + dev_dbg(device, "loading %s failed with error %d\n", + path, rc); + else + dev_warn(device, "loading %s failed with error %d\n", + path, rc); + continue; + } + dev_dbg(device, "direct-loading %s\n", fw_priv->fw_name); + fw_priv->size = size; + fw_state_done(fw_priv); + break; + } + __putname(path); + + return rc; +} + +/* firmware holds the ownership of pages */ +static void firmware_free_data(const struct firmware *fw) +{ + /* Loaded directly? */ + if (!fw->priv) { + vfree(fw->data); + return; + } + free_fw_priv(fw->priv); +} + +/* store the pages buffer info firmware from buf */ +static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw) +{ + fw->priv = fw_priv; +#ifdef CONFIG_FW_LOADER_USER_HELPER + fw->pages = fw_priv->pages; +#endif + fw->size = fw_priv->size; + fw->data = fw_priv->data; + + pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n", + __func__, fw_priv->fw_name, fw_priv, fw_priv->data, + (unsigned int)fw_priv->size); +} + +#ifdef CONFIG_PM_SLEEP +static void fw_name_devm_release(struct device *dev, void *res) +{ + struct fw_name_devm *fwn = res; + + if (fwn->magic == (unsigned long)&fw_cache) + pr_debug("%s: fw_name-%s devm-%p released\n", + __func__, fwn->name, res); + kfree_const(fwn->name); +} + +static int fw_devm_match(struct device *dev, void *res, + void *match_data) +{ + struct fw_name_devm *fwn = res; + + return (fwn->magic == (unsigned long)&fw_cache) && + !strcmp(fwn->name, match_data); +} + +static struct fw_name_devm *fw_find_devm_name(struct device *dev, + const char *name) +{ + struct fw_name_devm *fwn; + + fwn = devres_find(dev, fw_name_devm_release, + fw_devm_match, (void *)name); + return fwn; +} + +static bool fw_cache_is_setup(struct device *dev, const char *name) +{ + struct fw_name_devm *fwn; + + fwn = fw_find_devm_name(dev, name); + if (fwn) + return true; + + return false; +} + +/* add firmware name into devres list */ +static int fw_add_devm_name(struct device *dev, const char *name) +{ + struct fw_name_devm *fwn; + + if (fw_cache_is_setup(dev, name)) + return 0; + + fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm), + GFP_KERNEL); + if (!fwn) + return -ENOMEM; + fwn->name = kstrdup_const(name, GFP_KERNEL); + if (!fwn->name) { + devres_free(fwn); + return -ENOMEM; + } + + fwn->magic = (unsigned long)&fw_cache; + devres_add(dev, fwn); + + return 0; +} +#else +static bool fw_cache_is_setup(struct device *dev, const char *name) +{ + return false; +} + +static int fw_add_devm_name(struct device *dev, const char *name) +{ + return 0; +} +#endif + +int assign_fw(struct firmware *fw, struct device *device, + enum fw_opt opt_flags) +{ + struct fw_priv *fw_priv = fw->priv; + int ret; + + mutex_lock(&fw_lock); + if (!fw_priv->size || fw_state_is_aborted(fw_priv)) { + mutex_unlock(&fw_lock); + return -ENOENT; + } + + /* + * add firmware name into devres list so that we can auto cache + * and uncache firmware for device. + * + * device may has been deleted already, but the problem + * should be fixed in devres or driver core. + */ + /* don't cache firmware handled without uevent */ + if (device && (opt_flags & FW_OPT_UEVENT) && + !(opt_flags & FW_OPT_NOCACHE)) { + ret = fw_add_devm_name(device, fw_priv->fw_name); + if (ret) { + mutex_unlock(&fw_lock); + return ret; + } + } + + /* + * After caching firmware image is started, let it piggyback + * on request firmware. + */ + if (!(opt_flags & FW_OPT_NOCACHE) && + fw_priv->fwc->state == FW_LOADER_START_CACHE) { + if (fw_cache_piggyback_on_request(fw_priv->fw_name)) + kref_get(&fw_priv->ref); + } + + /* pass the pages buffer to driver at the last minute */ + fw_set_page_data(fw_priv, fw); + mutex_unlock(&fw_lock); + return 0; +} + +/* prepare firmware and firmware_buf structs; + * return 0 if a firmware is already assigned, 1 if need to load one, + * or a negative error code + */ +static int +_request_firmware_prepare(struct firmware **firmware_p, const char *name, + struct device *device, void *dbuf, size_t size, + enum fw_opt opt_flags) +{ + struct firmware *firmware; + struct fw_priv *fw_priv; + int ret; + + *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); + if (!firmware) { + dev_err(device, "%s: kmalloc(struct firmware) failed\n", + __func__); + return -ENOMEM; + } + + if (fw_get_builtin_firmware(firmware, name, dbuf, size)) { + dev_dbg(device, "using built-in %s\n", name); + return 0; /* assigned */ + } + + ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size, + opt_flags); + + /* + * bind with 'priv' now to avoid warning in failure path + * of requesting firmware. + */ + firmware->priv = fw_priv; + + if (ret > 0) { + ret = fw_state_wait(fw_priv); + if (!ret) { + fw_set_page_data(fw_priv, firmware); + return 0; /* assigned */ + } + } + + if (ret < 0) + return ret; + return 1; /* need to load */ +} + +/* + * Batched requests need only one wake, we need to do this step last due to the + * fallback mechanism. The buf is protected with kref_get(), and it won't be + * released until the last user calls release_firmware(). + * + * Failed batched requests are possible as well, in such cases we just share + * the struct fw_priv and won't release it until all requests are woken + * and have gone through this same path. + */ +static void fw_abort_batch_reqs(struct firmware *fw) +{ + struct fw_priv *fw_priv; + + /* Loaded directly? */ + if (!fw || !fw->priv) + return; + + fw_priv = fw->priv; + if (!fw_state_is_aborted(fw_priv)) + fw_state_aborted(fw_priv); +} + +/* called from request_firmware() and request_firmware_work_func() */ +static int +_request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device, void *buf, size_t size, + enum fw_opt opt_flags) +{ + struct firmware *fw = NULL; + int ret; + + if (!firmware_p) + return -EINVAL; + + if (!name || name[0] == '\0') { + ret = -EINVAL; + goto out; + } + + ret = _request_firmware_prepare(&fw, name, device, buf, size, + opt_flags); + if (ret <= 0) /* error or already assigned */ + goto out; + + ret = fw_get_filesystem_firmware(device, fw->priv); + if (ret) { + if (!(opt_flags & FW_OPT_NO_WARN)) + dev_warn(device, + "Direct firmware load for %s failed with error %d\n", + name, ret); + ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret); + } else + ret = assign_fw(fw, device, opt_flags); + + out: + if (ret < 0) { + fw_abort_batch_reqs(fw); + release_firmware(fw); + fw = NULL; + } + + *firmware_p = fw; + return ret; +} + +/** + * request_firmware() - send firmware request and wait for it + * @firmware_p: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded + * + * @firmware_p will be used to return a firmware image by the name + * of @name for device @device. + * + * Should be called from user context where sleeping is allowed. + * + * @name will be used as $FIRMWARE in the uevent environment and + * should be distinctive enough not to be confused with any other + * firmware image for this or any other device. + * + * Caller must hold the reference count of @device. + * + * The function can be called safely inside device's suspend and + * resume callback. + **/ +int +request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device) +{ + int ret; + + /* Need to pin this module until return */ + __module_get(THIS_MODULE); + ret = _request_firmware(firmware_p, name, device, NULL, 0, + FW_OPT_UEVENT); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL(request_firmware); + +/** + * firmware_request_nowarn() - request for an optional fw module + * @firmware: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded + * + * This function is similar in behaviour to request_firmware(), except + * it doesn't produce warning messages when the file is not found. + * The sysfs fallback mechanism is enabled if direct filesystem lookup fails, + * however, however failures to find the firmware file with it are still + * suppressed. It is therefore up to the driver to check for the return value + * of this call and to decide when to inform the users of errors. + **/ +int firmware_request_nowarn(const struct firmware **firmware, const char *name, + struct device *device) +{ + int ret; + + /* Need to pin this module until return */ + __module_get(THIS_MODULE); + ret = _request_firmware(firmware, name, device, NULL, 0, + FW_OPT_UEVENT | FW_OPT_NO_WARN); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL_GPL(firmware_request_nowarn); + +/** + * request_firmware_direct() - load firmware directly without usermode helper + * @firmware_p: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded + * + * This function works pretty much like request_firmware(), but this doesn't + * fall back to usermode helper even if the firmware couldn't be loaded + * directly from fs. Hence it's useful for loading optional firmwares, which + * aren't always present, without extra long timeouts of udev. + **/ +int request_firmware_direct(const struct firmware **firmware_p, + const char *name, struct device *device) +{ + int ret; + + __module_get(THIS_MODULE); + ret = _request_firmware(firmware_p, name, device, NULL, 0, + FW_OPT_UEVENT | FW_OPT_NO_WARN | + FW_OPT_NOFALLBACK); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL_GPL(request_firmware_direct); + +/** + * firmware_request_cache() - cache firmware for suspend so resume can use it + * @name: name of firmware file + * @device: device for which firmware should be cached for + * + * There are some devices with an optimization that enables the device to not + * require loading firmware on system reboot. This optimization may still + * require the firmware present on resume from suspend. This routine can be + * used to ensure the firmware is present on resume from suspend in these + * situations. This helper is not compatible with drivers which use + * request_firmware_into_buf() or request_firmware_nowait() with no uevent set. + **/ +int firmware_request_cache(struct device *device, const char *name) +{ + int ret; + + mutex_lock(&fw_lock); + ret = fw_add_devm_name(device, name); + mutex_unlock(&fw_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(firmware_request_cache); + +/** + * request_firmware_into_buf() - load firmware into a previously allocated buffer + * @firmware_p: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded and DMA region allocated + * @buf: address of buffer to load firmware into + * @size: size of buffer + * + * This function works pretty much like request_firmware(), but it doesn't + * allocate a buffer to hold the firmware data. Instead, the firmware + * is loaded directly into the buffer pointed to by @buf and the @firmware_p + * data member is pointed at @buf. + * + * This function doesn't cache firmware either. + */ +int +request_firmware_into_buf(const struct firmware **firmware_p, const char *name, + struct device *device, void *buf, size_t size) +{ + int ret; + + if (fw_cache_is_setup(device, name)) + return -EOPNOTSUPP; + + __module_get(THIS_MODULE); + ret = _request_firmware(firmware_p, name, device, buf, size, + FW_OPT_UEVENT | FW_OPT_NOCACHE); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL(request_firmware_into_buf); + +/** + * release_firmware() - release the resource associated with a firmware image + * @fw: firmware resource to release + **/ +void release_firmware(const struct firmware *fw) +{ + if (fw) { + if (!fw_is_builtin_firmware(fw)) + firmware_free_data(fw); + kfree(fw); + } +} +EXPORT_SYMBOL(release_firmware); + +/* Async support */ +struct firmware_work { + struct work_struct work; + struct module *module; + const char *name; + struct device *device; + void *context; + void (*cont)(const struct firmware *fw, void *context); + enum fw_opt opt_flags; +}; + +static void request_firmware_work_func(struct work_struct *work) +{ + struct firmware_work *fw_work; + const struct firmware *fw; + + fw_work = container_of(work, struct firmware_work, work); + + _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, + fw_work->opt_flags); + fw_work->cont(fw, fw_work->context); + put_device(fw_work->device); /* taken in request_firmware_nowait() */ + + module_put(fw_work->module); + kfree_const(fw_work->name); + kfree(fw_work); +} + +/** + * request_firmware_nowait() - asynchronous version of request_firmware + * @module: module requesting the firmware + * @uevent: sends uevent to copy the firmware image if this flag + * is non-zero else the firmware copy must be done manually. + * @name: name of firmware file + * @device: device for which firmware is being loaded + * @gfp: allocation flags + * @context: will be passed over to @cont, and + * @fw may be %NULL if firmware request fails. + * @cont: function will be called asynchronously when the firmware + * request is over. + * + * Caller must hold the reference count of @device. + * + * Asynchronous variant of request_firmware() for user contexts: + * - sleep for as small periods as possible since it may + * increase kernel boot time of built-in device drivers + * requesting firmware in their ->probe() methods, if + * @gfp is GFP_KERNEL. + * + * - can't sleep at all if @gfp is GFP_ATOMIC. + **/ +int +request_firmware_nowait( + struct module *module, bool uevent, + const char *name, struct device *device, gfp_t gfp, void *context, + void (*cont)(const struct firmware *fw, void *context)) +{ + struct firmware_work *fw_work; + + fw_work = kzalloc(sizeof(struct firmware_work), gfp); + if (!fw_work) + return -ENOMEM; + + fw_work->module = module; + fw_work->name = kstrdup_const(name, gfp); + if (!fw_work->name) { + kfree(fw_work); + return -ENOMEM; + } + fw_work->device = device; + fw_work->context = context; + fw_work->cont = cont; + fw_work->opt_flags = FW_OPT_NOWAIT | + (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER); + + if (!uevent && fw_cache_is_setup(device, name)) { + kfree_const(fw_work->name); + kfree(fw_work); + return -EOPNOTSUPP; + } + + if (!try_module_get(module)) { + kfree_const(fw_work->name); + kfree(fw_work); + return -EFAULT; + } + + get_device(fw_work->device); + INIT_WORK(&fw_work->work, request_firmware_work_func); + schedule_work(&fw_work->work); + return 0; +} +EXPORT_SYMBOL(request_firmware_nowait); + +#ifdef CONFIG_PM_SLEEP +static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain); + +/** + * cache_firmware() - cache one firmware image in kernel memory space + * @fw_name: the firmware image name + * + * Cache firmware in kernel memory so that drivers can use it when + * system isn't ready for them to request firmware image from userspace. + * Once it returns successfully, driver can use request_firmware or its + * nowait version to get the cached firmware without any interacting + * with userspace + * + * Return 0 if the firmware image has been cached successfully + * Return !0 otherwise + * + */ +static int cache_firmware(const char *fw_name) +{ + int ret; + const struct firmware *fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + ret = request_firmware(&fw, fw_name, NULL); + if (!ret) + kfree(fw); + + pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret); + + return ret; +} + +static struct fw_priv *lookup_fw_priv(const char *fw_name) +{ + struct fw_priv *tmp; + struct firmware_cache *fwc = &fw_cache; + + spin_lock(&fwc->lock); + tmp = __lookup_fw_priv(fw_name); + spin_unlock(&fwc->lock); + + return tmp; +} + +/** + * uncache_firmware() - remove one cached firmware image + * @fw_name: the firmware image name + * + * Uncache one firmware image which has been cached successfully + * before. + * + * Return 0 if the firmware cache has been removed successfully + * Return !0 otherwise + * + */ +static int uncache_firmware(const char *fw_name) +{ + struct fw_priv *fw_priv; + struct firmware fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0)) + return 0; + + fw_priv = lookup_fw_priv(fw_name); + if (fw_priv) { + free_fw_priv(fw_priv); + return 0; + } + + return -EINVAL; +} + +static struct fw_cache_entry *alloc_fw_cache_entry(const char *name) +{ + struct fw_cache_entry *fce; + + fce = kzalloc(sizeof(*fce), GFP_ATOMIC); + if (!fce) + goto exit; + + fce->name = kstrdup_const(name, GFP_ATOMIC); + if (!fce->name) { + kfree(fce); + fce = NULL; + goto exit; + } +exit: + return fce; +} + +static int __fw_entry_found(const char *name) +{ + struct firmware_cache *fwc = &fw_cache; + struct fw_cache_entry *fce; + + list_for_each_entry(fce, &fwc->fw_names, list) { + if (!strcmp(fce->name, name)) + return 1; + } + return 0; +} + +static int fw_cache_piggyback_on_request(const char *name) +{ + struct firmware_cache *fwc = &fw_cache; + struct fw_cache_entry *fce; + int ret = 0; + + spin_lock(&fwc->name_lock); + if (__fw_entry_found(name)) + goto found; + + fce = alloc_fw_cache_entry(name); + if (fce) { + ret = 1; + list_add(&fce->list, &fwc->fw_names); + pr_debug("%s: fw: %s\n", __func__, name); + } +found: + spin_unlock(&fwc->name_lock); + return ret; +} + +static void free_fw_cache_entry(struct fw_cache_entry *fce) +{ + kfree_const(fce->name); + kfree(fce); +} + +static void __async_dev_cache_fw_image(void *fw_entry, + async_cookie_t cookie) +{ + struct fw_cache_entry *fce = fw_entry; + struct firmware_cache *fwc = &fw_cache; + int ret; + + ret = cache_firmware(fce->name); + if (ret) { + spin_lock(&fwc->name_lock); + list_del(&fce->list); + spin_unlock(&fwc->name_lock); + + free_fw_cache_entry(fce); + } +} + +/* called with dev->devres_lock held */ +static void dev_create_fw_entry(struct device *dev, void *res, + void *data) +{ + struct fw_name_devm *fwn = res; + const char *fw_name = fwn->name; + struct list_head *head = data; + struct fw_cache_entry *fce; + + fce = alloc_fw_cache_entry(fw_name); + if (fce) + list_add(&fce->list, head); +} + +static int devm_name_match(struct device *dev, void *res, + void *match_data) +{ + struct fw_name_devm *fwn = res; + return (fwn->magic == (unsigned long)match_data); +} + +static void dev_cache_fw_image(struct device *dev, void *data) +{ + LIST_HEAD(todo); + struct fw_cache_entry *fce; + struct fw_cache_entry *fce_next; + struct firmware_cache *fwc = &fw_cache; + + devres_for_each_res(dev, fw_name_devm_release, + devm_name_match, &fw_cache, + dev_create_fw_entry, &todo); + + list_for_each_entry_safe(fce, fce_next, &todo, list) { + list_del(&fce->list); + + spin_lock(&fwc->name_lock); + /* only one cache entry for one firmware */ + if (!__fw_entry_found(fce->name)) { + list_add(&fce->list, &fwc->fw_names); + } else { + free_fw_cache_entry(fce); + fce = NULL; + } + spin_unlock(&fwc->name_lock); + + if (fce) + async_schedule_domain(__async_dev_cache_fw_image, + (void *)fce, + &fw_cache_domain); + } +} + +static void __device_uncache_fw_images(void) +{ + struct firmware_cache *fwc = &fw_cache; + struct fw_cache_entry *fce; + + spin_lock(&fwc->name_lock); + while (!list_empty(&fwc->fw_names)) { + fce = list_entry(fwc->fw_names.next, + struct fw_cache_entry, list); + list_del(&fce->list); + spin_unlock(&fwc->name_lock); + + uncache_firmware(fce->name); + free_fw_cache_entry(fce); + + spin_lock(&fwc->name_lock); + } + spin_unlock(&fwc->name_lock); +} + +/** + * device_cache_fw_images() - cache devices' firmware + * + * If one device called request_firmware or its nowait version + * successfully before, the firmware names are recored into the + * device's devres link list, so device_cache_fw_images can call + * cache_firmware() to cache these firmwares for the device, + * then the device driver can load its firmwares easily at + * time when system is not ready to complete loading firmware. + */ +static void device_cache_fw_images(void) +{ + struct firmware_cache *fwc = &fw_cache; + DEFINE_WAIT(wait); + + pr_debug("%s\n", __func__); + + /* cancel uncache work */ + cancel_delayed_work_sync(&fwc->work); + + fw_fallback_set_cache_timeout(); + + mutex_lock(&fw_lock); + fwc->state = FW_LOADER_START_CACHE; + dpm_for_each_dev(NULL, dev_cache_fw_image); + mutex_unlock(&fw_lock); + + /* wait for completion of caching firmware for all devices */ + async_synchronize_full_domain(&fw_cache_domain); + + fw_fallback_set_default_timeout(); +} + +/** + * device_uncache_fw_images() - uncache devices' firmware + * + * uncache all firmwares which have been cached successfully + * by device_uncache_fw_images earlier + */ +static void device_uncache_fw_images(void) +{ + pr_debug("%s\n", __func__); + __device_uncache_fw_images(); +} + +static void device_uncache_fw_images_work(struct work_struct *work) +{ + device_uncache_fw_images(); +} + +/** + * device_uncache_fw_images_delay() - uncache devices firmwares + * @delay: number of milliseconds to delay uncache device firmwares + * + * uncache all devices's firmwares which has been cached successfully + * by device_cache_fw_images after @delay milliseconds. + */ +static void device_uncache_fw_images_delay(unsigned long delay) +{ + queue_delayed_work(system_power_efficient_wq, &fw_cache.work, + msecs_to_jiffies(delay)); +} + +static int fw_pm_notify(struct notifier_block *notify_block, + unsigned long mode, void *unused) +{ + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: + /* + * kill pending fallback requests with a custom fallback + * to avoid stalling suspend. + */ + kill_pending_fw_fallback_reqs(true); + device_cache_fw_images(); + break; + + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + /* + * In case that system sleep failed and syscore_suspend is + * not called. + */ + mutex_lock(&fw_lock); + fw_cache.state = FW_LOADER_NO_CACHE; + mutex_unlock(&fw_lock); + + device_uncache_fw_images_delay(10 * MSEC_PER_SEC); + break; + } + + return 0; +} + +/* stop caching firmware once syscore_suspend is reached */ +static int fw_suspend(void) +{ + fw_cache.state = FW_LOADER_NO_CACHE; + return 0; +} + +static struct syscore_ops fw_syscore_ops = { + .suspend = fw_suspend, +}; + +static int __init register_fw_pm_ops(void) +{ + int ret; + + spin_lock_init(&fw_cache.name_lock); + INIT_LIST_HEAD(&fw_cache.fw_names); + + INIT_DELAYED_WORK(&fw_cache.work, + device_uncache_fw_images_work); + + fw_cache.pm_notify.notifier_call = fw_pm_notify; + ret = register_pm_notifier(&fw_cache.pm_notify); + if (ret) + return ret; + + register_syscore_ops(&fw_syscore_ops); + + return ret; +} + +static inline void unregister_fw_pm_ops(void) +{ + unregister_syscore_ops(&fw_syscore_ops); + unregister_pm_notifier(&fw_cache.pm_notify); +} +#else +static int fw_cache_piggyback_on_request(const char *name) +{ + return 0; +} +static inline int register_fw_pm_ops(void) +{ + return 0; +} +static inline void unregister_fw_pm_ops(void) +{ +} +#endif + +static void __init fw_cache_init(void) +{ + spin_lock_init(&fw_cache.lock); + INIT_LIST_HEAD(&fw_cache.head); + fw_cache.state = FW_LOADER_NO_CACHE; +} + +static int fw_shutdown_notify(struct notifier_block *unused1, + unsigned long unused2, void *unused3) +{ + /* + * Kill all pending fallback requests to avoid both stalling shutdown, + * and avoid a deadlock with the usermode_lock. + */ + kill_pending_fw_fallback_reqs(false); + + return NOTIFY_DONE; +} + +static struct notifier_block fw_shutdown_nb = { + .notifier_call = fw_shutdown_notify, +}; + +static int __init firmware_class_init(void) +{ + int ret; + + /* No need to unfold these on exit */ + fw_cache_init(); + + ret = register_fw_pm_ops(); + if (ret) + return ret; + + ret = register_reboot_notifier(&fw_shutdown_nb); + if (ret) + goto out; + + return register_sysfs_loader(); + +out: + unregister_fw_pm_ops(); + return ret; +} + +static void __exit firmware_class_exit(void) +{ + unregister_fw_pm_ops(); + unregister_reboot_notifier(&fw_shutdown_nb); + unregister_sysfs_loader(); +} + +fs_initcall(firmware_class_init); +module_exit(firmware_class_exit); diff --git a/rr-cache/4c17c8b289a6bfbb3811a8e73f904b12cc8266f6/thisimage b/rr-cache/4c17c8b289a6bfbb3811a8e73f904b12cc8266f6/thisimage new file mode 100644 index 0000000..31115f6 --- /dev/null +++ b/rr-cache/4c17c8b289a6bfbb3811a8e73f904b12cc8266f6/thisimage @@ -0,0 +1,650 @@ +/* + * Qualcomm SCM driver + * + * Copyright (c) 2010,2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_scm.h" + +static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT); +module_param(download_mode, bool, 0); + +#define SCM_HAS_CORE_CLK BIT(0) +#define SCM_HAS_IFACE_CLK BIT(1) +#define SCM_HAS_BUS_CLK BIT(2) + +struct qcom_scm { + struct device *dev; + struct clk *core_clk; + struct clk *iface_clk; + struct clk *bus_clk; + struct reset_controller_dev reset; + + u64 dload_mode_addr; +}; + +struct qcom_scm_current_perm_info { + __le32 vmid; + __le32 perm; + __le64 ctx; + __le32 ctx_size; + __le32 unused; +}; + +struct qcom_scm_mem_map_info { + __le64 mem_addr; + __le64 mem_size; +}; + +static struct qcom_scm *__scm; + +static int qcom_scm_clk_enable(void) +{ + int ret; + + ret = clk_prepare_enable(__scm->core_clk); + if (ret) + goto bail; + + ret = clk_prepare_enable(__scm->iface_clk); + if (ret) + goto disable_core; + + ret = clk_prepare_enable(__scm->bus_clk); + if (ret) + goto disable_iface; + + return 0; + +disable_iface: + clk_disable_unprepare(__scm->iface_clk); +disable_core: + clk_disable_unprepare(__scm->core_clk); +bail: + return ret; +} + +static void qcom_scm_clk_disable(void) +{ + clk_disable_unprepare(__scm->core_clk); + clk_disable_unprepare(__scm->iface_clk); + clk_disable_unprepare(__scm->bus_clk); +} + +/** + * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the cold boot address of the cpus. Any cpu outside the supported + * range would be removed from the cpu present mask. + */ +int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) +{ + return __qcom_scm_set_cold_boot_addr(entry, cpus); +} +EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); + +/** + * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the Linux entry point for the SCM to transfer control to when coming + * out of a power down. CPU power down may be executed on cpuidle or hotplug. + */ +int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) +{ + return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus); +} +EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); + +/** + * qcom_scm_cpu_power_down() - Power down the cpu + * @flags - Flags to flush cache + * + * This is an end point to power down cpu. If there was a pending interrupt, + * the control would return from this function, otherwise, the cpu jumps to the + * warm boot entry point set for this cpu upon reset. + */ +void qcom_scm_cpu_power_down(u32 flags) +{ + __qcom_scm_cpu_power_down(flags); +} +EXPORT_SYMBOL(qcom_scm_cpu_power_down); + +/** + * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. + * + * Return true if HDCP is supported, false if not. + */ +bool qcom_scm_hdcp_available(void) +{ + int ret = qcom_scm_clk_enable(); + + if (ret) + return ret; + + ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, + QCOM_SCM_CMD_HDCP); + + qcom_scm_clk_disable(); + + return ret > 0 ? true : false; +} +EXPORT_SYMBOL(qcom_scm_hdcp_available); + +/** + * qcom_scm_hdcp_req() - Send HDCP request. + * @req: HDCP request array + * @req_cnt: HDCP request array count + * @resp: response buffer passed to SCM + * + * Write HDCP register(s) through SCM. + */ +int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) +{ + int ret = qcom_scm_clk_enable(); + + if (ret) + return ret; + + ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp); + qcom_scm_clk_disable(); + return ret; +} +EXPORT_SYMBOL(qcom_scm_hdcp_req); + +/** + * qcom_scm_pas_supported() - Check if the peripheral authentication service is + * available for the given peripherial + * @peripheral: peripheral id + * + * Returns true if PAS is supported for this peripheral, otherwise false. + */ +bool qcom_scm_pas_supported(u32 peripheral) +{ + int ret; + + ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_IS_SUPPORTED_CMD); + if (ret <= 0) + return false; + + return __qcom_scm_pas_supported(__scm->dev, peripheral); +} +EXPORT_SYMBOL(qcom_scm_pas_supported); + +/** + * qcom_scm_pas_init_image() - Initialize peripheral authentication service + * state machine for a given peripheral, using the + * metadata + * @peripheral: peripheral id + * @metadata: pointer to memory containing ELF header, program header table + * and optional blob of data used for authenticating the metadata + * and the rest of the firmware + * @size: size of the metadata + * + * Returns 0 on success. + */ +int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size) +{ + dma_addr_t mdata_phys; + void *mdata_buf; + int ret; + + /* + * During the scm call memory protection will be enabled for the meta + * data blob, so make sure it's physically contiguous, 4K aligned and + * non-cachable to avoid XPU violations. + */ + mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, + GFP_KERNEL); + if (!mdata_buf) { + dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); + return -ENOMEM; + } + memcpy(mdata_buf, metadata, size); + + ret = qcom_scm_clk_enable(); + if (ret) + goto free_metadata; + + ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys); + + qcom_scm_clk_disable(); + +free_metadata: + dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_init_image); + +/** + * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral + * for firmware loading + * @peripheral: peripheral id + * @addr: start address of memory area to prepare + * @size: size of the memory area to prepare + * + * Returns 0 on success. + */ +int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) +{ + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size); + qcom_scm_clk_disable(); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_mem_setup); + +/** + * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware + * and reset the remote processor + * @peripheral: peripheral id + * + * Return 0 on success. + */ +int qcom_scm_pas_auth_and_reset(u32 peripheral) +{ + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral); + qcom_scm_clk_disable(); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); + +/** + * qcom_scm_pas_shutdown() - Shut down the remote processor + * @peripheral: peripheral id + * + * Returns 0 on success. + */ +int qcom_scm_pas_shutdown(u32 peripheral) +{ + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral); + qcom_scm_clk_disable(); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_shutdown); + +static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + if (idx != 0) + return -EINVAL; + + return __qcom_scm_pas_mss_reset(__scm->dev, 1); +} + +static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + if (idx != 0) + return -EINVAL; + + return __qcom_scm_pas_mss_reset(__scm->dev, 0); +} + +static const struct reset_control_ops qcom_scm_pas_reset_ops = { + .assert = qcom_scm_pas_reset_assert, + .deassert = qcom_scm_pas_reset_deassert, +}; + +int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) +{ + return __qcom_scm_restore_sec_cfg(__scm->dev, device_id, spare); +} +EXPORT_SYMBOL(qcom_scm_restore_sec_cfg); + +int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) +{ + return __qcom_scm_iommu_secure_ptbl_size(__scm->dev, spare, size); +} +EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_size); + +int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) +{ + return __qcom_scm_iommu_secure_ptbl_init(__scm->dev, addr, size, spare); +} +EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init); + +int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) +{ + return __qcom_scm_io_readl(__scm->dev, addr, val); +} +EXPORT_SYMBOL(qcom_scm_io_readl); + +int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) +{ + return __qcom_scm_io_writel(__scm->dev, addr, val); +} +EXPORT_SYMBOL(qcom_scm_io_writel); + +static void qcom_scm_set_download_mode(bool enable) +{ + bool avail; + int ret = 0; + + avail = __qcom_scm_is_call_available(__scm->dev, + QCOM_SCM_SVC_BOOT, + QCOM_SCM_SET_DLOAD_MODE); + if (avail) { + ret = __qcom_scm_set_dload_mode(__scm->dev, enable); + } else if (__scm->dload_mode_addr) { + ret = __qcom_scm_io_writel(__scm->dev, __scm->dload_mode_addr, + enable ? QCOM_SCM_SET_DLOAD_MODE : 0); + } else { + dev_err(__scm->dev, + "No available mechanism for setting download mode\n"); + } + + if (ret) + dev_err(__scm->dev, "failed to set download mode: %d\n", ret); +} + +static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) +{ + struct device_node *tcsr; + struct device_node *np = dev->of_node; + struct resource res; + u32 offset; + int ret; + + tcsr = of_parse_phandle(np, "qcom,dload-mode", 0); + if (!tcsr) + return 0; + + ret = of_address_to_resource(tcsr, 0, &res); + of_node_put(tcsr); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset); + if (ret < 0) + return ret; + + *addr = res.start + offset; + + return 0; +} + +/** + * qcom_scm_is_available() - Checks if SCM is available + */ +bool qcom_scm_is_available(void) +{ + return !!__scm; +} +EXPORT_SYMBOL(qcom_scm_is_available); + +int qcom_scm_set_remote_state(u32 state, u32 id) +{ + return __qcom_scm_set_remote_state(__scm->dev, state, id); +} +EXPORT_SYMBOL(qcom_scm_set_remote_state); + +/** + * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership + * @mem_addr: mem region whose ownership need to be reassigned + * @mem_sz: size of the region. + * @srcvm: vmid for current set of owners, each set bit in + * flag indicate a unique owner + * @newvm: array having new owners and corrsponding permission + * flags + * @dest_cnt: number of owners in next set. + * + * Return negative errno on failure, 0 on success, with @srcvm updated. + */ +int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, + unsigned int *srcvm, + struct qcom_scm_vmperm *newvm, int dest_cnt) +{ + struct qcom_scm_current_perm_info *destvm; + struct qcom_scm_mem_map_info *mem_to_map; + phys_addr_t mem_to_map_phys; + phys_addr_t dest_phys; + phys_addr_t ptr_phys; + size_t mem_to_map_sz; + size_t dest_sz; + size_t src_sz; + size_t ptr_sz; + int next_vm; + __le32 *src; + void *ptr; + int ret; + int len; + int i; + + src_sz = hweight_long(*srcvm) * sizeof(*src); + mem_to_map_sz = sizeof(*mem_to_map); + dest_sz = dest_cnt * sizeof(*destvm); + ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + + ALIGN(dest_sz, SZ_64); + + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + /* Fill source vmid detail */ + src = ptr; + len = hweight_long(*srcvm); + for (i = 0; i < len; i++) { + src[i] = cpu_to_le32(ffs(*srcvm) - 1); + *srcvm ^= 1 << (ffs(*srcvm) - 1); + } + + /* Fill details of mem buff to map */ + mem_to_map = ptr + ALIGN(src_sz, SZ_64); + mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); + mem_to_map[0].mem_addr = cpu_to_le64(mem_addr); + mem_to_map[0].mem_size = cpu_to_le64(mem_sz); + + next_vm = 0; + /* Fill details of next vmid detail */ + destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + for (i = 0; i < dest_cnt; i++) { + destvm[i].vmid = cpu_to_le32(newvm[i].vmid); + destvm[i].perm = cpu_to_le32(newvm[i].perm); + destvm[i].ctx = 0; + destvm[i].ctx_size = 0; + next_vm |= BIT(newvm[i].vmid); + } + + ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, + ptr_phys, src_sz, dest_phys, dest_sz); + dma_free_coherent(__scm->dev, ALIGN(ptr_sz, SZ_64), ptr, ptr_phys); + if (ret) { + dev_err(__scm->dev, + "Assign memory protection call failed %d.\n", ret); + return -EINVAL; + } + + *srcvm = next_vm; + return 0; +} +EXPORT_SYMBOL(qcom_scm_assign_mem); + +static int qcom_scm_probe(struct platform_device *pdev) +{ + struct qcom_scm *scm; + unsigned long clks; + int ret; + + scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); + if (!scm) + return -ENOMEM; + + ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr); + if (ret < 0) + return ret; + + clks = (unsigned long)of_device_get_match_data(&pdev->dev); + + scm->core_clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(scm->core_clk)) { + if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER) + return PTR_ERR(scm->core_clk); + + if (clks & SCM_HAS_CORE_CLK) { + dev_err(&pdev->dev, "failed to acquire core clk\n"); + return PTR_ERR(scm->core_clk); + } + + scm->core_clk = NULL; + } + + scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(scm->iface_clk)) { + if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER) + return PTR_ERR(scm->iface_clk); + + if (clks & SCM_HAS_IFACE_CLK) { + dev_err(&pdev->dev, "failed to acquire iface clk\n"); + return PTR_ERR(scm->iface_clk); + } + + scm->iface_clk = NULL; + } + + scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(scm->bus_clk)) { + if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER) + return PTR_ERR(scm->bus_clk); + + if (clks & SCM_HAS_BUS_CLK) { + dev_err(&pdev->dev, "failed to acquire bus clk\n"); + return PTR_ERR(scm->bus_clk); + } + + scm->bus_clk = NULL; + } + + scm->reset.ops = &qcom_scm_pas_reset_ops; + scm->reset.nr_resets = 1; + scm->reset.of_node = pdev->dev.of_node; + ret = devm_reset_controller_register(&pdev->dev, &scm->reset); + if (ret) + return ret; + + /* vote for max clk rate for highest performance */ + ret = clk_set_rate(scm->core_clk, INT_MAX); + if (ret) + return ret; + + __scm = scm; + __scm->dev = &pdev->dev; + + __qcom_scm_init(); + + /* + * If requested enable "download mode", from this point on warmboot + * will cause the the boot stages to enter download mode, unless + * disabled below by a clean shutdown/reboot. + */ + if (download_mode) + qcom_scm_set_download_mode(true); + + return 0; +} + +static void qcom_scm_shutdown(struct platform_device *pdev) +{ + /* Clean shutdown, disable download mode to allow normal restart */ + if (download_mode) + qcom_scm_set_download_mode(false); +} + +static const struct of_device_id qcom_scm_dt_match[] = { + { .compatible = "qcom,scm-apq8064", + /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */ + }, + { .compatible = "qcom,scm-apq8084", .data = (void *)(SCM_HAS_CORE_CLK | + SCM_HAS_IFACE_CLK | + SCM_HAS_BUS_CLK) + }, + { .compatible = "qcom,scm-ipq4019" }, + { .compatible = "qcom,scm-msm8660", .data = (void *) SCM_HAS_CORE_CLK }, + { .compatible = "qcom,scm-msm8960", .data = (void *) SCM_HAS_CORE_CLK }, + { .compatible = "qcom,scm-msm8916", .data = (void *)(SCM_HAS_CORE_CLK | + SCM_HAS_IFACE_CLK | + SCM_HAS_BUS_CLK) + }, +<<<<<<< + { .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK | + SCM_HAS_IFACE_CLK | + SCM_HAS_BUS_CLK) +======= + { .compatible = "qcom,scm-sdm845", + .data = NULL /* no clocks */ + }, + { .compatible = "qcom,scm", + .data = (void *)(SCM_HAS_CORE_CLK + | SCM_HAS_IFACE_CLK + | SCM_HAS_BUS_CLK), +>>>>>>> + }, + { .compatible = "qcom,scm-msm8996" }, + { .compatible = "qcom,scm" }, + {} +}; + +static struct platform_driver qcom_scm_driver = { + .driver = { + .name = "qcom_scm", + .of_match_table = qcom_scm_dt_match, + }, + .probe = qcom_scm_probe, + .shutdown = qcom_scm_shutdown, +}; + +static int __init qcom_scm_init(void) +{ + return platform_driver_register(&qcom_scm_driver); +} +subsys_initcall(qcom_scm_init); diff --git a/rr-cache/4f2f9c338bac6803576b99dbedbc648d8d33dd33/thisimage b/rr-cache/4f2f9c338bac6803576b99dbedbc648d8d33dd33/thisimage new file mode 100644 index 0000000..14f6004 --- /dev/null +++ b/rr-cache/4f2f9c338bac6803576b99dbedbc648d8d33dd33/thisimage @@ -0,0 +1,142 @@ +Qualcomm ADSP Peripheral Image Loader + +This document defines the binding for a component that loads and boots firmware +on the Qualcomm ADSP Hexagon core. + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,msm8974-adsp-pil" + "qcom,msm8996-adsp-pil" + "qcom,msm8996-slpi-pil" +<<<<<<< + "qcom,qcs404-adsp-pas" + "qcom,qcs404-cdsp-pas" + "qcom,qcs404-wcss-pas" +======= + "qcom,sdm845-adsp-pas" + "qcom,sdm845-cdsp-pas" +>>>>>>> + +- interrupts-extended: + Usage: required + Value type: + Definition: must list the watchdog, fatal IRQs ready, handover and + stop-ack IRQs + +- interrupt-names: + Usage: required + Value type: + Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" + +- clocks: + Usage: required + Value type: + Definition: reference to the xo clock and optionally aggre2 clock to be + held on behalf of the booting Hexagon core + +- clock-names: + Usage: required + Value type: + Definition: must be "xo" and optionally include "aggre2" + +- cx-supply: + Usage: required + Value type: + Definition: reference to the regulator to be held on behalf of the + booting Hexagon core + +- px-supply: + Usage: required + Value type: + Definition: reference to the px regulator to be held on behalf of the + booting Hexagon core + +- memory-region: + Usage: required + Value type: + Definition: reference to the reserved-memory for the ADSP + +- qcom,smem-states: + Usage: required + Value type: + Definition: reference to the smem state for requesting the ADSP to + shut down + +- qcom,smem-state-names: + Usage: required + Value type: + Definition: must be "stop" + + += SUBNODES +The adsp node may have an subnode named either "smd-edge" or "glink-edge" that +describes the communication edge, channels and devices related to the ADSP. +See ../soc/qcom/qcom,smd.txt and ../soc/qcom/qcom,glink.txt for details on how +to describe these. + + += EXAMPLE +The following example describes the resources needed to boot control the +ADSP, as it is found on MSM8974 boards. + + adsp { + compatible = "qcom,msm8974-adsp-pil"; + + interrupts-extended = <&intc 0 162 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 3 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", + "fatal", + "ready", + "handover", + "stop-ack"; + + clocks = <&rpmcc RPM_CXO_CLK>; + clock-names = "xo"; + + cx-supply = <&pm8841_s2>; + + memory-region = <&adsp_region>; + + qcom,smem-states = <&adsp_smp2p_out 0>; + qcom,smem-state-names = "stop"; + + smd-edge { + interrupts = <0 156 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&apcs 8 8>; + qcom,smd-edge = <1>; + }; + }; + +The following example describes the resources needed to boot control the +SLPI, as it is found on MSM8996 boards. + + slpi { + compatible = "qcom,msm8996-slpi-pil"; + interrupts-extended = <&intc 0 390 IRQ_TYPE_EDGE_RISING>, + <&slpi_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&slpi_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&slpi_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&slpi_smp2p_in 3 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", + "fatal", + "ready", + "handover", + "stop-ack"; + + clocks = <&rpmcc MSM8996_RPM_SMD_XO_CLK_SRC>, + <&rpmcc MSM8996_RPM_SMD_AGGR2_NOC_CLK>; + clock-names = "xo", "aggre2"; + + cx-supply = <&pm8994_l26>; + px-supply = <&pm8994_lvs2>; + + memory-region = <&slpi_region>; + qcom,smem-states = <&slpi_smp2p_out 0>; + qcom,smem-state-names = "stop"; + }; diff --git a/rr-cache/9a750d358b41d2247d9104afed62b9810c183004/thisimage b/rr-cache/9a750d358b41d2247d9104afed62b9810c183004/thisimage new file mode 100644 index 0000000..c5477b8 --- /dev/null +++ b/rr-cache/9a750d358b41d2247d9104afed62b9810c183004/thisimage @@ -0,0 +1,795 @@ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_NUMA_BALANCING=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_ARCH_SUNXI=y +CONFIG_ARCH_ALPINE=y +CONFIG_ARCH_BCM2835=y +CONFIG_ARCH_BCM_IPROC=y +CONFIG_ARCH_BERLIN=y +CONFIG_ARCH_BRCMSTB=y +CONFIG_ARCH_EXYNOS=y +CONFIG_ARCH_K3=y +CONFIG_ARCH_LAYERSCAPE=y +CONFIG_ARCH_LG1K=y +CONFIG_ARCH_HISI=y +CONFIG_ARCH_MEDIATEK=y +CONFIG_ARCH_MESON=y +CONFIG_ARCH_MVEBU=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_ROCKCHIP=y +CONFIG_ARCH_SEATTLE=y +CONFIG_ARCH_SYNQUACER=y +CONFIG_ARCH_RENESAS=y +CONFIG_ARCH_R8A7795=y +CONFIG_ARCH_R8A7796=y +CONFIG_ARCH_R8A77965=y +CONFIG_ARCH_R8A77970=y +CONFIG_ARCH_R8A77980=y +CONFIG_ARCH_R8A77990=y +CONFIG_ARCH_R8A77995=y +CONFIG_ARCH_STRATIX10=y +CONFIG_ARCH_TEGRA=y +CONFIG_ARCH_SPRD=y +CONFIG_ARCH_THUNDER=y +CONFIG_ARCH_THUNDER2=y +CONFIG_ARCH_UNIPHIER=y +CONFIG_ARCH_VEXPRESS=y +CONFIG_ARCH_XGENE=y +CONFIG_ARCH_ZX=y +CONFIG_ARCH_ZYNQMP=y +CONFIG_PCI=y +CONFIG_PCI_IOV=y +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_PCI_AARDVARK=y +CONFIG_PCI_TEGRA=y +CONFIG_PCIE_RCAR=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_XGENE=y +CONFIG_PCI_HOST_THUNDER_PEM=y +CONFIG_PCI_HOST_THUNDER_ECAM=y +CONFIG_PCIE_ROCKCHIP_HOST=m +CONFIG_PCI_LAYERSCAPE=y +CONFIG_PCI_HISI=y +CONFIG_PCIE_QCOM=y +CONFIG_PCIE_ARMADA_8K=y +CONFIG_PCIE_KIRIN=y +CONFIG_PCIE_HISI_STB=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_SCHED_MC=y +CONFIG_NUMA=y +CONFIG_PREEMPT=y +CONFIG_KSM=y +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_CMA=y +CONFIG_SECCOMP=y +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +CONFIG_XEN=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_HIBERNATION=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPUFREQ_DT=y +CONFIG_ACPI_CPPC_CPUFREQ=m +CONFIG_ARM_ARMADA_37XX_CPUFREQ=y +CONFIG_ARM_BIG_LITTLE_CPUFREQ=y +CONFIG_ARM_QCOM_CPUFREQ_KRYO=y +CONFIG_ARM_SCPI_CPUFREQ=y +CONFIG_ARM_TEGRA186_CPUFREQ=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IPV6=m +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_MANGLE=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_BRIDGE=m +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +<<<<<<< +CONFIG_QRTR=m +CONFIG_QRTR_SMD=m +CONFIG_QRTR_TUN=m +======= +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y +CONFIG_QRTR_TUN=y +>>>>>>> +CONFIG_BPF_JIT=y +CONFIG_BT=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m +# CONFIG_BT_HS is not set +# CONFIG_BT_LE is not set +CONFIG_BT_LEDS=y +# CONFIG_BT_DEBUGFS is not set +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_BT_HCIUART_BCM=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_RFKILL=y +CONFIG_WCN36XX=m +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DMA_CMA=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_NAND_MARVELL=y +CONFIG_MTD_NAND_QCOM=y +CONFIG_MTD_SPI_NOR=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=m +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_NVME=m +CONFIG_QCOM_COINCELL=m +CONFIG_SRAM=y +CONFIG_EEPROM_AT25=m +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_HISI_SAS=y +CONFIG_SCSI_HISI_SAS_PCI=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_HISI=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_CEVA=y +CONFIG_AHCI_MVEBU=y +CONFIG_AHCI_XGENE=y +CONFIG_AHCI_QORIQ=y +CONFIG_SATA_SIL24=y +CONFIG_SATA_RCAR=y +CONFIG_PATA_PLATFORM=y +CONFIG_PATA_OF_PLATFORM=y +CONFIG_NETDEVICES=y +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_TUN=y +CONFIG_VETH=m +CONFIG_VIRTIO_NET=y +CONFIG_AMD_XGBE=y +CONFIG_NET_XGENE=y +CONFIG_ATL1C=y +CONFIG_MACB=y +CONFIG_THUNDER_NIC_PF=y +CONFIG_HIX5HD2_GMAC=y +CONFIG_HNS_DSAF=y +CONFIG_HNS_ENET=y +CONFIG_E1000E=y +CONFIG_IGB=y +CONFIG_IGBVF=y +CONFIG_MVNETA=y +CONFIG_MVPP2=y +CONFIG_SKY2=y +CONFIG_QCOM_EMAC=m +CONFIG_RAVB=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_SNI_AVE=y +CONFIG_SNI_NETSEC=y +CONFIG_STMMAC_ETH=m +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_AT803X_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_MARVELL_10G_PHY=m +CONFIG_MESON_GXL_PHY=m +CONFIG_MICREL_PHY=y +CONFIG_REALTEK_PHY=m +CONFIG_ROCKCHIP_PHY=y +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9800=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_ATH10K=y +CONFIG_ATH10K_PCI=y +CONFIG_BRCMFMAC=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_PCIE=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SDIO=m +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_ADC=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_CROS_EC=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PM8941_PWRKEY=y +CONFIG_INPUT_HISI_POWERKEY=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_AMBAKMI=y +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_BCM2835AUX=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_8250_MT6577=y +CONFIG_SERIAL_8250_UNIPHIER=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_MESON=y +CONFIG_SERIAL_MESON_CONSOLE=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_QCOM_GENI=y +CONFIG_SERIAL_QCOM_GENI_CONSOLE=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_MVEBU_UART=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_BCM2835=m +CONFIG_I2C_DESIGNWARE_PLATFORM=y +CONFIG_I2C_IMX=y +CONFIG_I2C_MESON=y +CONFIG_I2C_MV64XXX=y +CONFIG_I2C_PXA=y +CONFIG_I2C_QUP=y +CONFIG_I2C_RK3X=y +CONFIG_I2C_SH_MOBILE=y +CONFIG_I2C_TEGRA=y +CONFIG_I2C_UNIPHIER_F=y +CONFIG_I2C_RCAR=y +CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_SPI=y +CONFIG_SPI_ARMADA_3700=y +CONFIG_SPI_BCM2835=m +CONFIG_SPI_BCM2835AUX=m +CONFIG_SPI_MESON_SPICC=m +CONFIG_SPI_MESON_SPIFC=m +CONFIG_SPI_ORION=y +CONFIG_SPI_PL022=y +CONFIG_SPI_ROCKCHIP=y +CONFIG_SPI_QUP=y +CONFIG_SPI_S3C64XX=y +CONFIG_SPI_SPIDEV=m +CONFIG_SPMI=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_MAX77620=y +CONFIG_PINCTRL_IPQ8074=y +CONFIG_PINCTRL_MSM8916=y +CONFIG_PINCTRL_MSM8994=y +CONFIG_PINCTRL_MSM8996=y +<<<<<<< +CONFIG_PINCTRL_QCS404=y +======= +CONFIG_PINCTRL_SDM845=y +>>>>>>> +CONFIG_PINCTRL_QDF2XXX=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_MT7622=y +CONFIG_GPIO_DWAPB=y +CONFIG_GPIO_MB86S7X=y +CONFIG_GPIO_PL061=y +CONFIG_GPIO_RCAR=y +CONFIG_GPIO_UNIPHIER=y +CONFIG_GPIO_XGENE=y +CONFIG_GPIO_XGENE_SB=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_MAX77620=y +CONFIG_POWER_AVS=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_QCOM_CPR=y +CONFIG_POWER_RESET_MSM=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_BATTERY_SBS=m +CONFIG_BATTERY_BQ27XXX=y +CONFIG_SENSORS_ARM_SCPI=y +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_RASPBERRYPI_HWMON=m +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_ROCKCHIP_THERMAL=m +CONFIG_RCAR_GEN3_THERMAL=y +CONFIG_ARMADA_THERMAL=y +CONFIG_BRCMSTB_THERMAL=m +CONFIG_EXYNOS_THERMAL=y +CONFIG_TEGRA_BPMP_THERMAL=m +CONFIG_QCOM_TSENS=y +CONFIG_QCOM_SPMI_TEMP_ALARM=m +CONFIG_UNIPHIER_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_ARM_SP805_WATCHDOG=y +CONFIG_S3C2410_WATCHDOG=y +CONFIG_QCOM_WDT=m +CONFIG_MESON_GXBB_WATCHDOG=m +CONFIG_MESON_WATCHDOG=m +CONFIG_RENESAS_WDT=y +CONFIG_UNIPHIER_WATCHDOG=y +CONFIG_BCM2835_WDT=y +CONFIG_MFD_BD9571MWV=y +CONFIG_MFD_AXP20X_RSB=y +CONFIG_MFD_CROS_EC=y +CONFIG_CROS_EC_I2C=y +CONFIG_CROS_EC_SPI=y +CONFIG_MFD_CROS_EC_CHARDEV=m +CONFIG_MFD_EXYNOS_LPASS=m +CONFIG_MFD_HI6421_PMIC=y +CONFIG_MFD_HI655X_PMIC=y +CONFIG_MFD_MAX77620=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_RK808=y +CONFIG_MFD_SEC_CORE=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_BD9571MWV=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_HI6421V530=y +CONFIG_REGULATOR_HI655X=y +CONFIG_REGULATOR_MAX77620=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_QCOM_RPMH=y +CONFIG_REGULATOR_QCOM_SMD_RPM=y +CONFIG_REGULATOR_QCOM_SPMI=y +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_S2MPS11=y +CONFIG_REGULATOR_VCTRL=m +CONFIG_RC_CORE=m +CONFIG_RC_DECODERS=y +CONFIG_RC_DEVICES=y +CONFIG_IR_MESON=m +CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +# CONFIG_DVB_NET is not set +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_VIDEO_SAMSUNG_S5P_JPEG=m +CONFIG_VIDEO_SAMSUNG_S5P_MFC=m +CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m +CONFIG_VIDEO_RENESAS_FCP=m +CONFIG_VIDEO_RENESAS_VSP1=m +CONFIG_DRM=y +CONFIG_DRM_NOUVEAU=m +CONFIG_DRM_EXYNOS=m +CONFIG_DRM_EXYNOS5433_DECON=y +CONFIG_DRM_EXYNOS7_DECON=y +CONFIG_DRM_EXYNOS_DSI=y +# CONFIG_DRM_EXYNOS_DP is not set +CONFIG_DRM_EXYNOS_HDMI=y +CONFIG_DRM_EXYNOS_MIC=y +CONFIG_DRM_ROCKCHIP=m +CONFIG_ROCKCHIP_ANALOGIX_DP=y +CONFIG_ROCKCHIP_CDN_DP=y +CONFIG_ROCKCHIP_DW_HDMI=y +CONFIG_ROCKCHIP_DW_MIPI_DSI=y +CONFIG_ROCKCHIP_INNO_HDMI=y +CONFIG_DRM_RCAR_DU=m +CONFIG_DRM_RCAR_LVDS=m +CONFIG_DRM_TEGRA=m +CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_I2C_ADV7511=m +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_VC4=m +CONFIG_DRM_HISI_HIBMC=m +CONFIG_DRM_HISI_KIRIN=m +CONFIG_DRM_MESON=m +CONFIG_FB=y +CONFIG_FB_ARMCLCD=y +CONFIG_BACKLIGHT_GENERIC=m +CONFIG_BACKLIGHT_PWM=m +CONFIG_BACKLIGHT_LP855X=m +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_BCM2835_SOC_I2S=m +CONFIG_SND_SOC_ROCKCHIP=m +CONFIG_SND_SOC_ROCKCHIP_I2S=m +CONFIG_SND_SOC_ROCKCHIP_SPDIF=m +CONFIG_SND_SOC_ROCKCHIP_RT5645=m +CONFIG_SND_SOC_RK3399_GRU_SOUND=m +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_SND_SOC_RCAR=m +CONFIG_SND_SOC_AK4613=m +CONFIG_SND_SOC_DA7219=m +CONFIG_SND_SOC_MAX98357A=m +CONFIG_SND_SOC_RL6231=m +CONFIG_SND_SOC_RT5514=m +CONFIG_SND_SOC_RT5514_SPI=m +CONFIG_SND_SOC_RT5645=m +CONFIG_SND_SIMPLE_CARD=y +CONFIG_SND_AUDIO_GRAPH_CARD=m +CONFIG_I2C_HID=m +CONFIG_SND_SOC_QCOM=y +CONFIG_SND_SOC_APQ8016_SBC=y +CONFIG_SND_SOC_QDSP6=y +CONFIG_SND_SOC_MSM8996=y +CONFIG_SND_SOC_MSM8916_WCD_ANALOG=y +CONFIG_SND_SOC_MSM8916_WCD_DIGITAL=y +CONFIG_USB=y +CONFIG_USB_OTG=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_TEGRA=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EXYNOS=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_RENESAS_USBHS=m +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SUNXI=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_CHIPIDEA_ULPI=y +CONFIG_USB_ISP1760=y +CONFIG_USB_HSIC_USB3503=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_USB_RENESAS_USBHS_UDC=m +CONFIG_USB_RENESAS_USB3=m +CONFIG_USB_ULPI_BUS=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_ESDHC=y +CONFIG_MMC_SDHCI_CADENCE=y +CONFIG_MMC_SDHCI_TEGRA=y +CONFIG_MMC_SDHCI_F_SDH30=y +CONFIG_MMC_MESON_GX=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SPI=y +CONFIG_MMC_SDHI=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_DW_HI3798CV200=y +CONFIG_MMC_DW_K3=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_SUNXI=y +CONFIG_MMC_BCM2835=y +CONFIG_MMC_SDHCI_XENON=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_EDAC=y +CONFIG_EDAC_GHES=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_MAX77686=y +CONFIG_RTC_DRV_RK808=m +CONFIG_RTC_DRV_S5M=y +CONFIG_RTC_DRV_DS3232=y +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_CROS_EC=y +CONFIG_RTC_DRV_S3C=y +CONFIG_RTC_DRV_PL031=y +CONFIG_RTC_DRV_SUN6I=y +CONFIG_RTC_DRV_ARMADA38X=y +CONFIG_RTC_DRV_PM8XXX=m +CONFIG_RTC_DRV_TEGRA=y +CONFIG_RTC_DRV_XGENE=y +CONFIG_DMADEVICES=y +CONFIG_DMA_BCM2835=m +CONFIG_K3_DMA=y +CONFIG_MV_XOR_V2=y +CONFIG_PL330_DMA=y +CONFIG_TEGRA20_APB_DMA=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_HIDMA_MGMT=y +CONFIG_QCOM_HIDMA=y +CONFIG_RCAR_DMAC=y +CONFIG_RENESAS_USB_DMAC=m +CONFIG_VFIO=y +CONFIG_VFIO_PCI=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +CONFIG_XEN_GNTDEV=y +CONFIG_XEN_GRANT_DEV_ALLOC=y +CONFIG_COMMON_CLK_RK808=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_COMMON_CLK_CS2000_CP=y +CONFIG_COMMON_CLK_S2MPS11=y +CONFIG_CLK_QORIQ=y +CONFIG_COMMON_CLK_PWM=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_QCOM_CLK_SMD_RPM=y +CONFIG_QCOM_CLK_RPMH=y +CONFIG_IPQ_GCC_8074=y +CONFIG_MSM_CLK_RPMH=y +CONFIG_MSM_GCC_8916=y +CONFIG_MSM_GCC_8994=y +CONFIG_MSM_MMCC_8996=y +<<<<<<< +CONFIG_QCOM_CLK_APCC_MSM8996=y +======= +CONFIG_QCS_GCC_404=y +>>>>>>> +CONFIG_HWSPINLOCK=y +CONFIG_SDM_GCC_845=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_ARM_MHU=y +CONFIG_PLATFORM_MHU=y +CONFIG_BCM2835_MBOX=y +CONFIG_QCOM_APCS_IPC=y +CONFIG_ROCKCHIP_IOMMU=y +CONFIG_TEGRA_IOMMU_SMMU=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_QCOM_IOMMU=y +<<<<<<< +CONFIG_REMOTEPROC=m +CONFIG_QCOM_ADSP_PIL=m +CONFIG_QCOM_Q6V5_PIL=m +CONFIG_QCOM_SYSMON=m +======= +CONFIG_REMOTEPROC=y +CONFIG_QCOM_ADSP_PIL=y +CONFIG_QCOM_Q6V5_PIL=y +CONFIG_QCOM_SYSMON=y +CONFIG_QCOM_WCNSS_PIL=y +>>>>>>> +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_SMD=y +CONFIG_RASPBERRYPI_POWER=y +<<<<<<< +CONFIG_QCOM_GLINK_SSR=m +======= +CONFIG_QCOM_RPMH=y +>>>>>>> +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_SMD_RPM=y +CONFIG_QCOM_SMP2P=y +CONFIG_QCOM_SMSM=y +CONFIG_QCOM_WCNSS_CTRL=y +CONFIG_QCOM_APR=y +CONFIG_ROCKCHIP_PM_DOMAINS=y +CONFIG_ARCH_TEGRA_132_SOC=y +CONFIG_ARCH_TEGRA_210_SOC=y +CONFIG_ARCH_TEGRA_186_SOC=y +CONFIG_ARCH_TEGRA_194_SOC=y +CONFIG_ARCH_K3_AM6_SOC=y +CONFIG_SOC_TI=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_EXTCON_USBC_CROS_EC=y +CONFIG_MEMORY=y +CONFIG_IIO=y +CONFIG_EXYNOS_ADC=y +CONFIG_ROCKCHIP_SARADC=m +CONFIG_IIO_CROS_EC_SENSORS_CORE=m +CONFIG_IIO_CROS_EC_SENSORS=m +CONFIG_IIO_CROS_EC_LIGHT_PROX=m +CONFIG_IIO_CROS_EC_BARO=m +CONFIG_PWM=y +CONFIG_PWM_BCM2835=m +CONFIG_PWM_CROS_EC=m +CONFIG_PWM_MESON=m +CONFIG_PWM_RCAR=m +CONFIG_PWM_ROCKCHIP=y +CONFIG_PWM_SAMSUNG=y +CONFIG_PWM_TEGRA=m +CONFIG_PHY_XGENE=y +CONFIG_PHY_SUN4I_USB=y +CONFIG_PHY_HI6220_USB=y +CONFIG_PHY_HISTB_COMBPHY=y +CONFIG_PHY_HISI_INNO_USB2=y +CONFIG_PHY_MVEBU_CP110_COMPHY=y +CONFIG_PHY_QCOM_QMP=y +CONFIG_PHY_QCOM_USB_HS=y +CONFIG_PHY_RCAR_GEN3_USB2=y +CONFIG_PHY_RCAR_GEN3_USB3=m +CONFIG_PHY_ROCKCHIP_EMMC=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_PCIE=m +CONFIG_PHY_ROCKCHIP_TYPEC=y +CONFIG_PHY_TEGRA_XUSB=y +CONFIG_HISI_PMU=y +CONFIG_QCOM_L2_PMU=y +CONFIG_QCOM_L3_PMU=y +CONFIG_QCOM_QFPROM=y +CONFIG_ROCKCHIP_EFUSE=y +CONFIG_UNIPHIER_EFUSE=y +CONFIG_MESON_EFUSE=m +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_INTERCONNECT=y +CONFIG_INTERCONNECT_QCOM=y +CONFIG_INTERCONNECT_QCOM_MSM8916=y +CONFIG_INTERCONNECT_QCOM_MSM8996=y +CONFIG_INTERCONNECT_QCOM_SDM845=y +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_RASPBERRYPI_FIRMWARE=y +CONFIG_EFI_CAPSULE_LOADER=y +CONFIG_ACPI=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_EINJ=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=m +CONFIG_CUSE=m +CONFIG_OVERLAY_FS=m +CONFIG_VFAT_FS=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +CONFIG_EFIVAR_FS=y +CONFIG_SQUASHFS=y +CONFIG_UFS_FS=y +CONFIG_UFS_FS_WRITE=y +CONFIG_NFS_FS=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_9P_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_FS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_FTRACE is not set +CONFIG_MEMTEST=y +CONFIG_SECURITY=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512_ARM64_CE=m +CONFIG_CRYPTO_SHA3_ARM64=m +CONFIG_CRYPTO_SM3_ARM64_CE=m +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m +CONFIG_CRYPTO_CRC32_ARM64_CE=m +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_CHACHA20_NEON=m +CONFIG_CRYPTO_AES_ARM64_BS=m +CONFIG_QTI_RPMH_MBOX=y +CONFIG_QTI_RPMH_API=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_REGULATOR_RPMH=y +CONFIG_QCOM_GENI_SE=y +CONFIG_QCOM_GLINK_SSR=y +CONFIG_QCOM_RMTFS_MEM=y +CONFIG_RESET_QCOM_AOSS=y diff --git a/rr-cache/d5ab1173612d441f2265071527c6ed2b444e753b/thisimage b/rr-cache/d5ab1173612d441f2265071527c6ed2b444e753b/thisimage new file mode 100644 index 0000000..dab4cf6 --- /dev/null +++ b/rr-cache/d5ab1173612d441f2265071527c6ed2b444e753b/thisimage @@ -0,0 +1,1113 @@ +/* + * Copyright (c) 2013, Sony Mobile Communications AB. + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../pinconf.h" +#include "pinctrl-msm.h" +#include "../pinctrl-utils.h" + +#define MAX_NR_GPIO 300 +#define MAX_NR_TILES 4 +#define PS_HOLD_OFFSET 0x820 + +/** + * struct msm_pinctrl - state for a pinctrl-msm device + * @dev: device handle. + * @pctrl: pinctrl handle. + * @chip: gpiochip handle. + * @restart_nb: restart notifier block. + * @irq: parent irq for the TLMM irq_chip. + * @lock: Spinlock to protect register resources as well + * as msm_pinctrl data structures. + * @enabled_irqs: Bitmap of currently enabled irqs. + * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge + * detection. + * @soc; Reference to soc_data of platform specific data. + * @regs: Base addresses for the TLMM tiles. + */ +struct msm_pinctrl { + struct device *dev; + struct pinctrl_dev *pctrl; + struct gpio_chip chip; + struct pinctrl_desc desc; + struct notifier_block restart_nb; + + struct irq_chip irq_chip; + int irq; + + raw_spinlock_t lock; + + DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); + DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); + + const struct msm_pinctrl_soc_data *soc; + void __iomem *regs[MAX_NR_TILES]; +}; + +#define MSM_ACCESSOR(name) \ +static u32 msm_readl_##name(struct msm_pinctrl *pctrl, \ + const struct msm_pingroup *g) \ +{ \ + return readl(pctrl->regs[g->tile] + g->name##_reg); \ +} \ +static void msm_writel_##name(u32 val, struct msm_pinctrl *pctrl, \ + const struct msm_pingroup *g) \ +{ \ + writel(val, pctrl->regs[g->tile] + g->name##_reg); \ +} + +MSM_ACCESSOR(ctl) +MSM_ACCESSOR(io) +MSM_ACCESSOR(intr_cfg) +MSM_ACCESSOR(intr_status) +MSM_ACCESSOR(intr_target) + +static int msm_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->ngroups; +} + +static const char *msm_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->groups[group].name; +} + +static int msm_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *pins = pctrl->soc->groups[group].pins; + *num_pins = pctrl->soc->groups[group].npins; + return 0; +} + +static const struct pinctrl_ops msm_pinctrl_ops = { + .get_groups_count = msm_get_groups_count, + .get_group_name = msm_get_group_name, + .get_group_pins = msm_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int msm_pinmux_request(struct pinctrl_dev *pctldev, unsigned offset) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct gpio_chip *chip = &pctrl->chip; + + return gpiochip_line_is_valid(chip, offset) ? 0 : -EINVAL; +} + +static int msm_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->nfunctions; +} + +static const char *msm_get_function_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->functions[function].name; +} + +static int msm_get_function_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const num_groups) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctrl->soc->functions[function].groups; + *num_groups = pctrl->soc->functions[function].ngroups; + return 0; +} + +static int msm_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned function, + unsigned group) +{ + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct msm_pingroup *g; + unsigned long flags; + u32 val, mask; + int i; + + g = &pctrl->soc->groups[group]; + mask = GENMASK(g->mux_bit + order_base_2(g->nfuncs) - 1, g->mux_bit); + + for (i = 0; i < g->nfuncs; i++) { + if (g->funcs[i] == function) + break; + } + + if (WARN_ON(i == g->nfuncs)) + return -EINVAL; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + val = msm_readl_ctl(pctrl, g); + val &= ~mask; + val |= i << g->mux_bit; + msm_writel_ctl(val, pctrl, g); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static const struct pinmux_ops msm_pinmux_ops = { + .request = msm_pinmux_request, + .get_functions_count = msm_get_functions_count, + .get_function_name = msm_get_function_name, + .get_function_groups = msm_get_function_groups, + .set_mux = msm_pinmux_set_mux, +}; + +static int msm_config_reg(struct msm_pinctrl *pctrl, + const struct msm_pingroup *g, + unsigned param, + unsigned *mask, + unsigned *bit) +{ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_BUS_HOLD: + case PIN_CONFIG_BIAS_PULL_UP: + *bit = g->pull_bit; + *mask = 3; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + *bit = g->drv_bit; + *mask = 7; + break; + case PIN_CONFIG_OUTPUT: + case PIN_CONFIG_INPUT_ENABLE: + *bit = g->oe_bit; + *mask = 1; + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +#define MSM_NO_PULL 0 +#define MSM_PULL_DOWN 1 +#define MSM_KEEPER 2 +#define MSM_PULL_UP_NO_KEEPER 2 +#define MSM_PULL_UP 3 + +static unsigned msm_regval_to_drive(u32 val) +{ + return (val + 1) * 2; +} + +static int msm_config_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + unsigned param = pinconf_to_config_param(*config); + unsigned mask; + unsigned arg; + unsigned bit; + int ret; + u32 val; + + g = &pctrl->soc->groups[group]; + + ret = msm_config_reg(pctrl, g, param, &mask, &bit); + if (ret < 0) + return ret; + + val = msm_readl_ctl(pctrl, g); + arg = (val >> bit) & mask; + + /* Convert register value to pinconf value */ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (arg != MSM_NO_PULL) + return -EINVAL; + arg = 1; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (arg != MSM_PULL_DOWN) + return -EINVAL; + arg = 1; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + if (pctrl->soc->pull_no_keeper) + return -ENOTSUPP; + + if (arg != MSM_KEEPER) + return -EINVAL; + arg = 1; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (pctrl->soc->pull_no_keeper) + arg = arg == MSM_PULL_UP_NO_KEEPER; + else + arg = arg == MSM_PULL_UP; + if (!arg) + return -EINVAL; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + arg = msm_regval_to_drive(arg); + break; + case PIN_CONFIG_OUTPUT: + /* Pin is not output */ + if (!arg) + return -EINVAL; + + val = msm_readl_io(pctrl, g); + arg = !!(val & BIT(g->in_bit)); + break; + case PIN_CONFIG_INPUT_ENABLE: + /* Pin is output */ + if (arg) + return -EINVAL; + arg = 1; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int msm_config_group_set(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long *configs, + unsigned num_configs) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + unsigned long flags; + unsigned param; + unsigned mask; + unsigned arg; + unsigned bit; + int ret; + u32 val; + int i; + + g = &pctrl->soc->groups[group]; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + ret = msm_config_reg(pctrl, g, param, &mask, &bit); + if (ret < 0) + return ret; + + /* Convert pinconf values to register values */ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + arg = MSM_NO_PULL; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = MSM_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + if (pctrl->soc->pull_no_keeper) + return -ENOTSUPP; + + arg = MSM_KEEPER; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (pctrl->soc->pull_no_keeper) + arg = MSM_PULL_UP_NO_KEEPER; + else + arg = MSM_PULL_UP; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* Check for invalid values */ + if (arg > 16 || arg < 2 || (arg % 2) != 0) + arg = -1; + else + arg = (arg / 2) - 1; + break; + case PIN_CONFIG_OUTPUT: + /* set output value */ + raw_spin_lock_irqsave(&pctrl->lock, flags); + val = msm_readl_io(pctrl, g); + if (arg) + val |= BIT(g->out_bit); + else + val &= ~BIT(g->out_bit); + msm_writel_io(val, pctrl, g); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + /* enable output */ + arg = 1; + break; + case PIN_CONFIG_INPUT_ENABLE: + /* disable output */ + arg = 0; + break; + default: + dev_err(pctrl->dev, "Unsupported config parameter: %x\n", + param); + return -EINVAL; + } + + /* Range-check user-supplied value */ + if (arg & ~mask) { + dev_err(pctrl->dev, "config %x: %x is invalid\n", param, arg); + return -EINVAL; + } + + raw_spin_lock_irqsave(&pctrl->lock, flags); + val = msm_readl_ctl(pctrl, g); + val &= ~(mask << bit); + val |= arg << bit; + msm_writel_ctl(val, pctrl, g); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + } + + return 0; +} + +static const struct pinconf_ops msm_pinconf_ops = { + .is_generic = true, + .pin_config_group_get = msm_config_group_get, + .pin_config_group_set = msm_config_group_set, +}; + +static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[offset]; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + val = msm_readl_ctl(pctrl, g); + val &= ~BIT(g->oe_bit); + msm_writel_ctl(val, pctrl, g); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[offset]; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + val = msm_readl_io(pctrl, g); + if (value) + val |= BIT(g->out_bit); + else + val &= ~BIT(g->out_bit); + msm_writel_io(val, pctrl, g); + + val = msm_readl_ctl(pctrl, g); + val |= BIT(g->oe_bit); + msm_writel_ctl(val, pctrl, g); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static int msm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + const struct msm_pingroup *g; + u32 val; + + g = &pctrl->soc->groups[offset]; + + val = msm_readl_ctl(pctrl, g); + + /* 0 = output, 1 = input */ + return val & BIT(g->oe_bit) ? 0 : 1; +} + +static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + u32 val; + + g = &pctrl->soc->groups[offset]; + + val = msm_readl_io(pctrl, g); + return !!(val & BIT(g->in_bit)); +} + +static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[offset]; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + val = msm_readl_io(pctrl, g); + if (value) + val |= BIT(g->out_bit); + else + val &= ~BIT(g->out_bit); + msm_writel_io(val, pctrl, g); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +#ifdef CONFIG_DEBUG_FS +#include + +static void msm_gpio_dbg_show_one(struct seq_file *s, + struct pinctrl_dev *pctldev, + struct gpio_chip *chip, + unsigned offset, + unsigned gpio) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + unsigned func; + int is_out; + int drive; + int pull; + int val; + u32 ctl_reg, io_reg; + + static const char * const pulls_keeper[] = { + "no pull", + "pull down", + "keeper", + "pull up" + }; + + static const char * const pulls_no_keeper[] = { + "no pull", + "pull down", + "pull up", + }; + + if (!gpiochip_line_is_valid(chip, offset)) + return; + + g = &pctrl->soc->groups[offset]; + ctl_reg = msm_readl_ctl(pctrl, g); + io_reg = msm_readl_io(pctrl, g); + + is_out = !!(ctl_reg & BIT(g->oe_bit)); + func = (ctl_reg >> g->mux_bit) & 7; + drive = (ctl_reg >> g->drv_bit) & 7; + pull = (ctl_reg >> g->pull_bit) & 3; + + if (is_out) + val = !!(io_reg & BIT(g->out_bit)); + else + val = !!(io_reg & BIT(g->in_bit)); + + seq_printf(s, " %-8s: %-3s", g->name, is_out ? "out" : "in"); + seq_printf(s, " %-4s func%d", val ? "high" : "low", func); + seq_printf(s, " %dmA", msm_regval_to_drive(drive)); + if (pctrl->soc->pull_no_keeper) + seq_printf(s, " %s", pulls_no_keeper[pull]); + else + seq_printf(s, " %s", pulls_keeper[pull]); + seq_puts(s, "\n"); +} + +static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + unsigned gpio = chip->base; + unsigned i; + + for (i = 0; i < chip->ngpio; i++, gpio++) + msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); +} + +#else +#define msm_gpio_dbg_show NULL +#endif + +static const struct gpio_chip msm_gpio_template = { + .direction_input = msm_gpio_direction_input, + .direction_output = msm_gpio_direction_output, + .get_direction = msm_gpio_get_direction, + .get = msm_gpio_get, + .set = msm_gpio_set, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .dbg_show = msm_gpio_dbg_show, +}; + +/* For dual-edge interrupts in software, since some hardware has no + * such support: + * + * At appropriate moments, this function may be called to flip the polarity + * settings of both-edge irq lines to try and catch the next edge. + * + * The attempt is considered successful if: + * - the status bit goes high, indicating that an edge was caught, or + * - the input value of the gpio doesn't change during the attempt. + * If the value changes twice during the process, that would cause the first + * test to fail but would force the second, as two opposite + * transitions would cause a detection no matter the polarity setting. + * + * The do-loop tries to sledge-hammer closed the timing hole between + * the initial value-read and the polarity-write - if the line value changes + * during that window, an interrupt is lost, the new polarity setting is + * incorrect, and the first success test will fail, causing a retry. + * + * Algorithm comes from Google's msmgpio driver. + */ +static void msm_gpio_update_dual_edge_pos(struct msm_pinctrl *pctrl, + const struct msm_pingroup *g, + struct irq_data *d) +{ + int loop_limit = 100; + unsigned val, val2, intstat; + unsigned pol; + + do { + val = msm_readl_io(pctrl, g) & BIT(g->in_bit); + + pol = msm_readl_intr_cfg(pctrl, g); + pol ^= BIT(g->intr_polarity_bit); + msm_writel_intr_cfg(val, pctrl, g); + + val2 = msm_readl_io(pctrl, g) & BIT(g->in_bit); + intstat = msm_readl_intr_status(pctrl, g); + if (intstat || (val == val2)) + return; + } while (loop_limit-- > 0); + dev_err(pctrl->dev, "dual-edge irq failed to stabilize, %#08x != %#08x\n", + val, val2); +} + +static void msm_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + const struct msm_pingroup *g; + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[d->hwirq]; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + +<<<<<<< + val = msm_readl_intr_cfg(pctrl, g); +======= + val = readl(pctrl->regs + g->intr_cfg_reg); +>>>>>>> + /* + * There are two bits that control interrupt forwarding to the CPU. The + * RAW_STATUS_EN bit causes the level or edge sensed on the line to be + * latched into the interrupt status register when the hardware detects + * an irq that it's configured for (either edge for edge type or level + * for level type irq). The 'non-raw' status enable bit causes the + * hardware to assert the summary interrupt to the CPU if the latched + * status bit is set. There's a bug though, the edge detection logic + * seems to have a problem where toggling the RAW_STATUS_EN bit may + * cause the status bit to latch spuriously when there isn't any edge + * so we can't touch that bit for edge type irqs and we have to keep + * the bit set anyway so that edges are latched while the line is masked. + * + * To make matters more complicated, leaving the RAW_STATUS_EN bit + * enabled all the time causes level interrupts to re-latch into the + * status register because the level is still present on the line after + * we ack it. We clear the raw status enable bit during mask here and + * set the bit on unmask so the interrupt can't latch into the hardware + * while it's masked. + */ + if (irqd_get_trigger_type(d) & IRQ_TYPE_LEVEL_MASK) + val &= ~BIT(g->intr_raw_status_bit); + + val &= ~BIT(g->intr_enable_bit); + msm_writel_intr_cfg(val, pctrl, g); + + clear_bit(d->hwirq, pctrl->enabled_irqs); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static void msm_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + const struct msm_pingroup *g; + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[d->hwirq]; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + +<<<<<<< + val = msm_readl_intr_cfg(pctrl, g); +======= + val = readl(pctrl->regs + g->intr_cfg_reg); +>>>>>>> + val |= BIT(g->intr_raw_status_bit); + val |= BIT(g->intr_enable_bit); + msm_writel_intr_cfg(val, pctrl, g); + + set_bit(d->hwirq, pctrl->enabled_irqs); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static void msm_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + const struct msm_pingroup *g; + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[d->hwirq]; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + val = msm_readl_intr_status(pctrl, g); + if (g->intr_ack_high) + val |= BIT(g->intr_status_bit); + else + val &= ~BIT(g->intr_status_bit); + msm_writel_intr_status(val, pctrl, g); + + if (test_bit(d->hwirq, pctrl->dual_edge_irqs)) + msm_gpio_update_dual_edge_pos(pctrl, g, d); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + const struct msm_pingroup *g; + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[d->hwirq]; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + /* + * For hw without possibility of detecting both edges + */ + if (g->intr_detection_width == 1 && type == IRQ_TYPE_EDGE_BOTH) + set_bit(d->hwirq, pctrl->dual_edge_irqs); + else + clear_bit(d->hwirq, pctrl->dual_edge_irqs); + + /* Route interrupts to application cpu */ + val = msm_readl_intr_target(pctrl, g); + val &= ~(7 << g->intr_target_bit); + val |= g->intr_target_kpss_val << g->intr_target_bit; + msm_writel_intr_target(val, pctrl, g); + + /* Update configuration for gpio. + * RAW_STATUS_EN is left on for all gpio irqs. Due to the + * internal circuitry of TLMM, toggling the RAW_STATUS + * could cause the INTR_STATUS to be set for EDGE interrupts. + */ + val = msm_readl_intr_cfg(pctrl, g); + val |= BIT(g->intr_raw_status_bit); + if (g->intr_detection_width == 2) { + val &= ~(3 << g->intr_detection_bit); + val &= ~(1 << g->intr_polarity_bit); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + val |= 1 << g->intr_detection_bit; + val |= BIT(g->intr_polarity_bit); + break; + case IRQ_TYPE_EDGE_FALLING: + val |= 2 << g->intr_detection_bit; + val |= BIT(g->intr_polarity_bit); + break; + case IRQ_TYPE_EDGE_BOTH: + val |= 3 << g->intr_detection_bit; + val |= BIT(g->intr_polarity_bit); + break; + case IRQ_TYPE_LEVEL_LOW: + break; + case IRQ_TYPE_LEVEL_HIGH: + val |= BIT(g->intr_polarity_bit); + break; + } + } else if (g->intr_detection_width == 1) { + val &= ~(1 << g->intr_detection_bit); + val &= ~(1 << g->intr_polarity_bit); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + val |= BIT(g->intr_detection_bit); + val |= BIT(g->intr_polarity_bit); + break; + case IRQ_TYPE_EDGE_FALLING: + val |= BIT(g->intr_detection_bit); + break; + case IRQ_TYPE_EDGE_BOTH: + val |= BIT(g->intr_detection_bit); + val |= BIT(g->intr_polarity_bit); + break; + case IRQ_TYPE_LEVEL_LOW: + break; + case IRQ_TYPE_LEVEL_HIGH: + val |= BIT(g->intr_polarity_bit); + break; + } + } else { + BUG(); + } + msm_writel_intr_cfg(val, pctrl, g); + + if (test_bit(d->hwirq, pctrl->dual_edge_irqs)) + msm_gpio_update_dual_edge_pos(pctrl, g, d); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + irq_set_handler_locked(d, handle_level_irq); + else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + irq_set_handler_locked(d, handle_edge_irq); + + return 0; +} + +static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + irq_set_irq_wake(pctrl->irq, on); + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static void msm_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int irq_pin; + int handled = 0; + u32 val; + int i; + + chained_irq_enter(chip, desc); + + /* + * Each pin has it's own IRQ status register, so use + * enabled_irq bitmap to limit the number of reads. + */ + for_each_set_bit(i, pctrl->enabled_irqs, pctrl->chip.ngpio) { + g = &pctrl->soc->groups[i]; + val = msm_readl_intr_status(pctrl, g); + if (val & BIT(g->intr_status_bit)) { + irq_pin = irq_find_mapping(gc->irq.domain, i); + generic_handle_irq(irq_pin); + handled++; + } + } + + /* No interrupts were flagged */ + if (handled == 0) + handle_bad_irq(desc); + + chained_irq_exit(chip, desc); +} + +static int msm_gpio_init_valid_mask(struct gpio_chip *chip, + struct msm_pinctrl *pctrl) +{ + int ret; + unsigned int len, i; + unsigned int max_gpios = pctrl->soc->ngpios; + u16 *tmp; + + /* The number of GPIOs in the ACPI tables */ + len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0); + if (ret < 0) + return 0; + + if (ret > max_gpios) + return -EINVAL; + + tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len); + if (ret < 0) { + dev_err(pctrl->dev, "could not read list of GPIOs\n"); + goto out; + } + + bitmap_zero(chip->valid_mask, max_gpios); + for (i = 0; i < len; i++) + set_bit(tmp[i], chip->valid_mask); + +out: + kfree(tmp); + return ret; +} + +static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl) +{ + return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0; +} + +static int msm_gpio_init(struct msm_pinctrl *pctrl) +{ + struct gpio_chip *chip; + int ret; + unsigned ngpio = pctrl->soc->ngpios; + + if (WARN_ON(ngpio > MAX_NR_GPIO)) + return -EINVAL; + + chip = &pctrl->chip; + chip->base = -1; + chip->ngpio = ngpio; + chip->label = dev_name(pctrl->dev); + chip->parent = pctrl->dev; + chip->owner = THIS_MODULE; + chip->of_node = pctrl->dev->of_node; + chip->need_valid_mask = msm_gpio_needs_valid_mask(pctrl); + + pctrl->irq_chip.name = "msmgpio"; + pctrl->irq_chip.irq_mask = msm_gpio_irq_mask; + pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask; + pctrl->irq_chip.irq_ack = msm_gpio_irq_ack; + pctrl->irq_chip.irq_set_type = msm_gpio_irq_set_type; + pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake; + + ret = gpiochip_add_data(&pctrl->chip, pctrl); + if (ret) { + dev_err(pctrl->dev, "Failed register gpiochip\n"); + return ret; + } + + ret = msm_gpio_init_valid_mask(chip, pctrl); + if (ret) { + dev_err(pctrl->dev, "Failed to setup irq valid bits\n"); + gpiochip_remove(&pctrl->chip); + return ret; + } + + /* + * For DeviceTree-supported systems, the gpio core checks the + * pinctrl's device node for the "gpio-ranges" property. + * If it is present, it takes care of adding the pin ranges + * for the driver. In this case the driver can skip ahead. + * + * In order to remain compatible with older, existing DeviceTree + * files which don't set the "gpio-ranges" property or systems that + * utilize ACPI the driver has to call gpiochip_add_pin_range(). + */ + if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) { + ret = gpiochip_add_pin_range(&pctrl->chip, + dev_name(pctrl->dev), 0, 0, chip->ngpio); + if (ret) { + dev_err(pctrl->dev, "Failed to add pin range\n"); + gpiochip_remove(&pctrl->chip); + return ret; + } + } + + ret = gpiochip_irqchip_add(chip, + &pctrl->irq_chip, + 0, + handle_edge_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(pctrl->dev, "Failed to add irqchip to gpiochip\n"); + gpiochip_remove(&pctrl->chip); + return -ENOSYS; + } + + gpiochip_set_chained_irqchip(chip, &pctrl->irq_chip, pctrl->irq, + msm_gpio_irq_handler); + + return 0; +} + +static int msm_ps_hold_restart(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct msm_pinctrl *pctrl = container_of(nb, struct msm_pinctrl, restart_nb); + + writel(0, pctrl->regs[0] + PS_HOLD_OFFSET); + mdelay(1000); + return NOTIFY_DONE; +} + +static struct msm_pinctrl *poweroff_pctrl; + +static void msm_ps_hold_poweroff(void) +{ + msm_ps_hold_restart(&poweroff_pctrl->restart_nb, 0, NULL); +} + +static void msm_pinctrl_setup_pm_reset(struct msm_pinctrl *pctrl) +{ + int i; + const struct msm_function *func = pctrl->soc->functions; + + for (i = 0; i < pctrl->soc->nfunctions; i++) + if (!strcmp(func[i].name, "ps_hold")) { + pctrl->restart_nb.notifier_call = msm_ps_hold_restart; + pctrl->restart_nb.priority = 128; + if (register_restart_handler(&pctrl->restart_nb)) + dev_err(pctrl->dev, + "failed to setup restart handler.\n"); + poweroff_pctrl = pctrl; + pm_power_off = msm_ps_hold_poweroff; + break; + } +} + +int msm_pinctrl_probe(struct platform_device *pdev, + const struct msm_pinctrl_soc_data *soc_data) +{ + struct msm_pinctrl *pctrl; + struct resource *res; + int ret; + int i; + + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->dev = &pdev->dev; + pctrl->soc = soc_data; + pctrl->chip = msm_gpio_template; + + raw_spin_lock_init(&pctrl->lock); + + if (soc_data->tiles) { + for (i = 0; i < soc_data->ntiles; i++) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + soc_data->tiles[i]); + pctrl->regs[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pctrl->regs[i])) + return PTR_ERR(pctrl->regs[i]); + } + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pctrl->regs[0] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pctrl->regs[0])) + return PTR_ERR(pctrl->regs[0]); + } + + msm_pinctrl_setup_pm_reset(pctrl); + + pctrl->irq = platform_get_irq(pdev, 0); + if (pctrl->irq < 0) { + dev_err(&pdev->dev, "No interrupt defined for msmgpio\n"); + return pctrl->irq; + } + + pctrl->desc.owner = THIS_MODULE; + pctrl->desc.pctlops = &msm_pinctrl_ops; + pctrl->desc.pmxops = &msm_pinmux_ops; + pctrl->desc.confops = &msm_pinconf_ops; + pctrl->desc.name = dev_name(&pdev->dev); + pctrl->desc.pins = pctrl->soc->pins; + pctrl->desc.npins = pctrl->soc->npins; + + pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl); + if (IS_ERR(pctrl->pctrl)) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return PTR_ERR(pctrl->pctrl); + } + + ret = msm_gpio_init(pctrl); + if (ret) + return ret; + + platform_set_drvdata(pdev, pctrl); + + dev_dbg(&pdev->dev, "Probed Qualcomm pinctrl driver\n"); + + return 0; +} +EXPORT_SYMBOL(msm_pinctrl_probe); + +int msm_pinctrl_remove(struct platform_device *pdev) +{ + struct msm_pinctrl *pctrl = platform_get_drvdata(pdev); + + gpiochip_remove(&pctrl->chip); + + unregister_restart_handler(&pctrl->restart_nb); + + return 0; +} +EXPORT_SYMBOL(msm_pinctrl_remove); + -- cgit v1.2.3