mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-11 07:03:09 -04:00
Merge "soc: qcom: add snapshot of MBA based modem PIL"
This commit is contained in:
commit
7fb23e31b4
7 changed files with 2807 additions and 0 deletions
|
|
@ -321,6 +321,15 @@ config MSM_PIL_SSR_GENERIC
|
|||
or a fatal error. Subsystems include LPASS, Venus, VPU, WCNSS and
|
||||
BCSS.
|
||||
|
||||
config MSM_PIL_MSS_QDSP6V5
|
||||
tristate "MSS QDSP6v5 (Hexagon) Boot Support"
|
||||
depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
|
||||
help
|
||||
Support for booting and shutting down QDSP6v5 (Hexagon) processors
|
||||
in modem subsystems. If you would like to make or receive phone
|
||||
calls then say Y here.
|
||||
If unsure, say N.
|
||||
|
||||
config MSM_SERVICE_LOCATOR
|
||||
tristate "Service Locator"
|
||||
select QCOM_QMI_HELPERS
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ obj-$(CONFIG_MSM_SUBSYSTEM_RESTART) += subsystem_restart.o
|
|||
obj-$(CONFIG_MSM_CDSP_LOADER) += qdsp6v2/
|
||||
subsystem_restart-y := msm_subsystem_restart.o subsystem_notif.o ramdump.o sysmon-qmi.o
|
||||
obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
|
||||
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o
|
||||
obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o
|
||||
obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o
|
||||
obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
|
||||
|
|
|
|||
1008
drivers/soc/qcom/pil-msa.c
Normal file
1008
drivers/soc/qcom/pil-msa.c
Normal file
File diff suppressed because it is too large
Load diff
56
drivers/soc/qcom/pil-msa.h
Normal file
56
drivers/soc/qcom/pil-msa.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_PIL_MSA_H
|
||||
#define __MSM_PIL_MSA_H
|
||||
|
||||
#include <soc/qcom/subsystem_restart.h>
|
||||
|
||||
#include "peripheral-loader.h"
|
||||
|
||||
struct modem_data {
|
||||
struct device *dev;
|
||||
struct q6v5_data *q6;
|
||||
struct subsys_device *subsys;
|
||||
struct subsys_desc subsys_desc;
|
||||
void *ramdump_dev;
|
||||
void *minidump_dev;
|
||||
bool crash_shutdown;
|
||||
u32 pas_id;
|
||||
bool ignore_errors;
|
||||
struct completion err_ready;
|
||||
struct completion stop_ack;
|
||||
void __iomem *rmb_base;
|
||||
struct clk *xo;
|
||||
struct pil_desc desc;
|
||||
struct device mba_mem_dev;
|
||||
struct device *mba_mem_dev_fixed;
|
||||
unsigned long attrs_dma;
|
||||
int is_not_loadable;
|
||||
unsigned int err_fatal_irq;
|
||||
unsigned int err_ready_irq;
|
||||
unsigned int stop_ack_irq;
|
||||
unsigned int wdog_bite_irq;
|
||||
unsigned int generic_irq;
|
||||
int ramdump_disable_irq;
|
||||
int shutdown_ack_irq;
|
||||
int force_stop_bit;
|
||||
struct qcom_smem_state *state;
|
||||
};
|
||||
|
||||
extern struct pil_reset_ops pil_msa_mss_ops;
|
||||
extern struct pil_reset_ops pil_msa_mss_ops_selfauth;
|
||||
extern struct pil_reset_ops pil_msa_femto_mba_ops;
|
||||
|
||||
int pil_mss_reset_load_mba(struct pil_desc *pil);
|
||||
int pil_mss_make_proxy_votes(struct pil_desc *pil);
|
||||
void pil_mss_remove_proxy_votes(struct pil_desc *pil);
|
||||
int pil_mss_shutdown(struct pil_desc *pil);
|
||||
int pil_mss_deinit_image(struct pil_desc *pil);
|
||||
int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path);
|
||||
int pil_mss_assert_resets(struct q6v5_data *drv);
|
||||
int pil_mss_deassert_resets(struct q6v5_data *drv);
|
||||
int pil_mss_debug_reset(struct pil_desc *pil);
|
||||
#endif
|
||||
810
drivers/soc/qcom/pil-q6v5-mss.c
Normal file
810
drivers/soc/qcom/pil-q6v5-mss.c
Normal file
|
|
@ -0,0 +1,810 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <soc/qcom/subsystem_restart.h>
|
||||
#include <soc/qcom/ramdump.h>
|
||||
#include <linux/soc/qcom/smem.h>
|
||||
#include <linux/soc/qcom/smem_state.h>
|
||||
|
||||
#include "peripheral-loader.h"
|
||||
#include "pil-q6v5.h"
|
||||
#include "pil-msa.h"
|
||||
|
||||
#define PROXY_TIMEOUT_MS 10000
|
||||
#define MAX_SSR_REASON_LEN 256U
|
||||
#define STOP_ACK_TIMEOUT_MS 1000
|
||||
|
||||
static int enable_debug;
|
||||
module_param(enable_debug, int, 0644);
|
||||
|
||||
#define subsys_to_drv(d) container_of(d, struct modem_data, subsys_desc)
|
||||
|
||||
static void pil_enable_all_irqs(struct modem_data *drv);
|
||||
static void pil_disable_all_irqs(struct modem_data *drv);
|
||||
|
||||
static int wait_for_err_ready(struct modem_data *drv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If subsys is using generic_irq in which case err_ready_irq will be 0,
|
||||
* don't return.
|
||||
*/
|
||||
if ((drv->generic_irq <= 0 && !drv->err_ready_irq) ||
|
||||
enable_debug == 1 || pil_is_timeout_disabled())
|
||||
return 0;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(&drv->err_ready,
|
||||
msecs_to_jiffies(10000));
|
||||
if (!ret) {
|
||||
pr_err("[%s]: Error ready timed out\n", drv->desc.name);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_modem_sfr(struct modem_data *drv)
|
||||
{
|
||||
size_t size;
|
||||
char *smem_reason, reason[MAX_SSR_REASON_LEN];
|
||||
|
||||
if (drv->q6->smem_id == -1)
|
||||
return;
|
||||
|
||||
smem_reason = qcom_smem_get(QCOM_SMEM_HOST_ANY, drv->q6->smem_id,
|
||||
&size);
|
||||
if (IS_ERR(smem_reason) || !size) {
|
||||
pr_err("modem SFR: (unknown, qcom_smem_get failed).\n");
|
||||
return;
|
||||
}
|
||||
if (!smem_reason[0]) {
|
||||
pr_err("modem SFR: (unknown, empty string found).\n");
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(reason, smem_reason, min(size, (size_t)MAX_SSR_REASON_LEN));
|
||||
pr_err("modem subsystem failure reason: %s.\n", reason);
|
||||
}
|
||||
|
||||
static void restart_modem(struct modem_data *drv)
|
||||
{
|
||||
log_modem_sfr(drv);
|
||||
drv->ignore_errors = true;
|
||||
subsystem_restart_dev(drv->subsys);
|
||||
}
|
||||
|
||||
static irqreturn_t modem_err_ready_intr_handler(int irq, void *drv_data)
|
||||
{
|
||||
struct modem_data *drv = drv_data;
|
||||
|
||||
pr_info("Subsystem error monitoring/handling services are up from%s\n",
|
||||
drv->subsys_desc.name);
|
||||
complete(&drv->err_ready);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t modem_err_fatal_intr_handler(int irq, void *drv_data)
|
||||
{
|
||||
struct modem_data *drv = drv_data;
|
||||
|
||||
/* Ignore if we're the one that set the force stop BIT */
|
||||
if (drv->crash_shutdown)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
pr_err("Fatal error on the modem.\n");
|
||||
subsys_set_crash_status(drv->subsys, CRASH_STATUS_ERR_FATAL);
|
||||
restart_modem(drv);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t modem_stop_ack_intr_handler(int irq, void *drv_data)
|
||||
{
|
||||
struct modem_data *drv = drv_data;
|
||||
|
||||
pr_info("Received stop ack interrupt from modem\n");
|
||||
complete(&drv->stop_ack);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t modem_shutdown_ack_intr_handler(int irq, void *drv_data)
|
||||
{
|
||||
struct modem_data *drv = drv_data;
|
||||
|
||||
pr_info("Received stop shutdown interrupt from modem\n");
|
||||
complete_shutdown_ack(&drv->subsys_desc);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t modem_ramdump_disable_intr_handler(int irq, void *drv_data)
|
||||
{
|
||||
struct modem_data *drv = drv_data;
|
||||
|
||||
pr_info("Received ramdump disable interrupt from modem\n");
|
||||
drv->subsys_desc.ramdump_disable = 1;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int modem_shutdown(const struct subsys_desc *subsys, bool force_stop)
|
||||
{
|
||||
struct modem_data *drv = subsys_to_drv(subsys);
|
||||
unsigned long ret;
|
||||
|
||||
if (drv->is_not_loadable)
|
||||
return 0;
|
||||
|
||||
if (!subsys_get_crash_status(drv->subsys) && force_stop &&
|
||||
drv->force_stop_bit) {
|
||||
qcom_smem_state_update_bits(drv->state,
|
||||
BIT(drv->force_stop_bit), 1);
|
||||
ret = wait_for_completion_timeout(&drv->stop_ack,
|
||||
msecs_to_jiffies(STOP_ACK_TIMEOUT_MS));
|
||||
if (!ret)
|
||||
pr_warn("Timed out on stop ack from modem.\n");
|
||||
qcom_smem_state_update_bits(drv->state,
|
||||
BIT(subsys->force_stop_bit), 0);
|
||||
}
|
||||
|
||||
if (drv->ramdump_disable_irq) {
|
||||
pr_warn("Ramdump disable value is %d\n",
|
||||
drv->subsys_desc.ramdump_disable);
|
||||
}
|
||||
|
||||
pil_shutdown(&drv->q6->desc);
|
||||
pil_disable_all_irqs(drv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int modem_powerup(const struct subsys_desc *subsys)
|
||||
{
|
||||
struct modem_data *drv = subsys_to_drv(subsys);
|
||||
int ret = 0;
|
||||
|
||||
if (drv->is_not_loadable)
|
||||
return 0;
|
||||
/*
|
||||
* At this time, the modem is shutdown. Therefore this function cannot
|
||||
* run concurrently with the watchdog bite error handler, making it safe
|
||||
* to unset the flag below.
|
||||
*/
|
||||
reinit_completion(&drv->stop_ack);
|
||||
drv->subsys_desc.ramdump_disable = 0;
|
||||
drv->ignore_errors = false;
|
||||
drv->q6->desc.fw_name = subsys->fw_name;
|
||||
ret = pil_boot(&drv->q6->desc);
|
||||
if (ret) {
|
||||
pr_err("pil_boot failed for %s\n", drv->subsys_desc.name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("pil_boot is successful from %s and waiting for error ready\n",
|
||||
drv->subsys_desc.name);
|
||||
pil_enable_all_irqs(drv);
|
||||
ret = wait_for_err_ready(drv);
|
||||
if (ret) {
|
||||
pr_err("%s failed to get error ready for %s\n", __func__,
|
||||
drv->subsys_desc.name);
|
||||
pil_shutdown(&drv->q6->desc);
|
||||
pil_disable_all_irqs(drv);
|
||||
}
|
||||
}
|
||||
|
||||
static void modem_crash_shutdown(const struct subsys_desc *subsys)
|
||||
{
|
||||
struct modem_data *drv = subsys_to_drv(subsys);
|
||||
|
||||
drv->crash_shutdown = true;
|
||||
if (!subsys_get_crash_status(drv->subsys) &&
|
||||
subsys->force_stop_bit) {
|
||||
qcom_smem_state_update_bits(subsys->state,
|
||||
BIT(subsys->force_stop_bit), 1);
|
||||
msleep(STOP_ACK_TIMEOUT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
static int modem_ramdump(int enable, const struct subsys_desc *subsys)
|
||||
{
|
||||
struct modem_data *drv = subsys_to_drv(subsys);
|
||||
int ret;
|
||||
|
||||
if (!enable)
|
||||
return 0;
|
||||
|
||||
ret = pil_mss_make_proxy_votes(&drv->q6->desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pil_mss_debug_reset(&drv->q6->desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pil_mss_remove_proxy_votes(&drv->q6->desc);
|
||||
ret = pil_mss_make_proxy_votes(&drv->q6->desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pil_mss_reset_load_mba(&drv->q6->desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pil_do_ramdump(&drv->q6->desc,
|
||||
drv->ramdump_dev, drv->minidump_dev);
|
||||
if (ret < 0)
|
||||
pr_err("Unable to dump modem fw memory (rc = %d).\n", ret);
|
||||
|
||||
ret = __pil_mss_deinit_image(&drv->q6->desc, false);
|
||||
if (ret < 0)
|
||||
pr_err("Unable to free up resources (rc = %d).\n", ret);
|
||||
|
||||
pil_mss_remove_proxy_votes(&drv->q6->desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t modem_wdog_bite_intr_handler(int irq, void *dev_data)
|
||||
{
|
||||
struct modem_data *drv = subsys_to_drv(dev_data);
|
||||
|
||||
if (drv->ignore_errors)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
pr_err("Watchdog bite received from modem software!\n");
|
||||
if (drv->subsys_desc.system_debug)
|
||||
panic("%s: System ramdump requested. Triggering device restart!\n",
|
||||
__func__);
|
||||
subsys_set_crash_status(drv->subsys, CRASH_STATUS_WDOG_BITE);
|
||||
restart_modem(drv);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void pil_enable_all_irqs(struct modem_data *drv)
|
||||
{
|
||||
if (drv->err_ready_irq)
|
||||
enable_irq(drv->err_ready_irq);
|
||||
if (drv->wdog_bite_irq) {
|
||||
enable_irq(drv->wdog_bite_irq);
|
||||
irq_set_irq_wake(drv->wdog_bite_irq, 1);
|
||||
}
|
||||
if (drv->err_fatal_irq)
|
||||
enable_irq(drv->err_fatal_irq);
|
||||
if (drv->stop_ack_irq)
|
||||
enable_irq(drv->stop_ack_irq);
|
||||
if (drv->shutdown_ack_irq)
|
||||
enable_irq(drv->shutdown_ack_irq);
|
||||
if (drv->ramdump_disable_irq)
|
||||
enable_irq(drv->ramdump_disable_irq);
|
||||
if (drv->generic_irq) {
|
||||
enable_irq(drv->generic_irq);
|
||||
irq_set_irq_wake(drv->generic_irq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void pil_disable_all_irqs(struct modem_data *drv)
|
||||
{
|
||||
if (drv->err_ready_irq)
|
||||
disable_irq(drv->err_ready_irq);
|
||||
if (drv->wdog_bite_irq) {
|
||||
disable_irq(drv->wdog_bite_irq);
|
||||
irq_set_irq_wake(drv->wdog_bite_irq, 0);
|
||||
}
|
||||
if (drv->err_fatal_irq)
|
||||
disable_irq(drv->err_fatal_irq);
|
||||
if (drv->stop_ack_irq)
|
||||
disable_irq(drv->stop_ack_irq);
|
||||
if (drv->shutdown_ack_irq)
|
||||
disable_irq(drv->shutdown_ack_irq);
|
||||
if (drv->generic_irq) {
|
||||
disable_irq(drv->generic_irq);
|
||||
irq_set_irq_wake(drv->generic_irq, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int __get_irq(struct platform_device *pdev, const char *prop,
|
||||
unsigned int *irq)
|
||||
{
|
||||
int irql = 0;
|
||||
struct device_node *dnode = pdev->dev.of_node;
|
||||
|
||||
if (of_property_match_string(dnode, "interrupt-names", prop) < 0)
|
||||
return -ENOENT;
|
||||
|
||||
irql = of_irq_get_byname(dnode, prop);
|
||||
if (irql < 0) {
|
||||
pr_err("[%s]: Error getting IRQ \"%s\"\n", pdev->name,
|
||||
prop);
|
||||
return irql;
|
||||
}
|
||||
*irq = irql;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __get_smem_state(struct modem_data *drv, const char *prop,
|
||||
int *smem_bit)
|
||||
{
|
||||
struct device_node *dnode = drv->dev->of_node;
|
||||
|
||||
if (of_find_property(dnode, "qcom,smem-states", NULL)) {
|
||||
drv->state = qcom_smem_state_get(drv->dev, prop, smem_bit);
|
||||
if (IS_ERR_OR_NULL(drv->state)) {
|
||||
pr_err("Could not get smem-states %s\n", prop);
|
||||
return PTR_ERR(drv->state);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int pil_parse_irqs(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct modem_data *drv = platform_get_drvdata(pdev);
|
||||
|
||||
ret = __get_irq(pdev, "qcom,err-fatal", &drv->err_fatal_irq);
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
ret = __get_irq(pdev, "qcom,err-ready", &drv->err_ready_irq);
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
ret = __get_irq(pdev, "qcom,stop-ack", &drv->stop_ack_irq);
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
ret = __get_irq(pdev, "qcom,ramdump-disabled",
|
||||
&drv->ramdump_disable_irq);
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
ret = __get_irq(pdev, "qcom,shutdown-ack", &drv->shutdown_ack_irq);
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
ret = __get_irq(pdev, "qcom,wdog", &drv->wdog_bite_irq);
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
ret = __get_smem_state(drv, "qcom,force-stop", &drv->force_stop_bit);
|
||||
if (ret && ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,pil-generic-irq-handler")) {
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret > 0)
|
||||
drv->generic_irq = ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pil_setup_irqs(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct modem_data *drv = platform_get_drvdata(pdev);
|
||||
|
||||
if (drv->err_fatal_irq) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, drv->err_fatal_irq,
|
||||
NULL, modem_err_fatal_intr_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
drv->desc.name, drv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "[%s]: Unable to register error fatal IRQ handler: %d, irq is %d\n",
|
||||
drv->desc.name, ret, drv->err_fatal_irq);
|
||||
return ret;
|
||||
}
|
||||
disable_irq(drv->err_fatal_irq);
|
||||
}
|
||||
|
||||
if (drv->stop_ack_irq) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, drv->stop_ack_irq,
|
||||
NULL, modem_stop_ack_intr_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
drv->desc.name, drv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "[%s]: Unable to register stop ack handler: %d\n",
|
||||
drv->desc.name, ret);
|
||||
return ret;
|
||||
}
|
||||
disable_irq(drv->stop_ack_irq);
|
||||
}
|
||||
|
||||
if (drv->wdog_bite_irq) {
|
||||
ret = devm_request_irq(&pdev->dev, drv->wdog_bite_irq,
|
||||
modem_wdog_bite_intr_handler,
|
||||
IRQF_TRIGGER_RISING, drv->desc.name, drv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "[%s]: Unable to register wdog bite handler: %d\n",
|
||||
drv->desc.name, ret);
|
||||
return ret;
|
||||
}
|
||||
disable_irq(drv->wdog_bite_irq);
|
||||
}
|
||||
|
||||
if (drv->shutdown_ack_irq) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
drv->shutdown_ack_irq,
|
||||
NULL, modem_shutdown_ack_intr_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
drv->desc.name, drv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "[%s]: Unable to register shutdown ack handler: %d\n",
|
||||
drv->desc.name, ret);
|
||||
return ret;
|
||||
}
|
||||
disable_irq(drv->shutdown_ack_irq);
|
||||
}
|
||||
|
||||
if (drv->ramdump_disable_irq) {
|
||||
ret = devm_request_threaded_irq(drv->dev,
|
||||
drv->ramdump_disable_irq,
|
||||
NULL, modem_ramdump_disable_intr_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
drv->desc.name, drv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "[%s]: Unable to register shutdown ack handler: %d\n",
|
||||
drv->desc.name, ret);
|
||||
return ret;
|
||||
}
|
||||
disable_irq(drv->ramdump_disable_irq);
|
||||
}
|
||||
|
||||
if (drv->err_ready_irq) {
|
||||
ret = devm_request_threaded_irq(drv->dev,
|
||||
drv->err_ready_irq,
|
||||
NULL, modem_err_ready_intr_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"error_ready_interrupt", drv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"[%s]: Unable to register err ready handler\n",
|
||||
drv->desc.name);
|
||||
return ret;
|
||||
}
|
||||
disable_irq(drv->err_ready_irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pil_subsys_init(struct modem_data *drv,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
drv->dev = &pdev->dev;
|
||||
drv->subsys_desc.name = "modem";
|
||||
drv->subsys_desc.dev = &pdev->dev;
|
||||
drv->subsys_desc.owner = THIS_MODULE;
|
||||
drv->subsys_desc.shutdown = modem_shutdown;
|
||||
drv->subsys_desc.powerup = modem_powerup;
|
||||
drv->subsys_desc.ramdump = modem_ramdump;
|
||||
drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
|
||||
|
||||
if (IS_ERR_OR_NULL(drv->q6)) {
|
||||
ret = PTR_ERR(drv->q6);
|
||||
dev_err(&pdev->dev, "Pil q6 data is err %pK %d!!!\n",
|
||||
drv->q6, ret);
|
||||
goto err_subsys;
|
||||
}
|
||||
|
||||
drv->q6->desc.modem_ssr = false;
|
||||
drv->q6->desc.signal_aop = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,signal-aop");
|
||||
if (drv->q6->desc.signal_aop) {
|
||||
drv->q6->desc.cl.dev = &pdev->dev;
|
||||
drv->q6->desc.cl.tx_block = true;
|
||||
drv->q6->desc.cl.tx_tout = 1000;
|
||||
drv->q6->desc.cl.knows_txdone = false;
|
||||
drv->q6->desc.mbox = mbox_request_channel(&drv->q6->desc.cl, 0);
|
||||
if (IS_ERR(drv->q6->desc.mbox)) {
|
||||
ret = PTR_ERR(drv->q6->desc.mbox);
|
||||
dev_err(&pdev->dev, "Failed to get mailbox channel %pK %d\n",
|
||||
drv->q6->desc.mbox, ret);
|
||||
goto err_subsys;
|
||||
}
|
||||
}
|
||||
|
||||
drv->subsys = subsys_register(&drv->subsys_desc);
|
||||
if (IS_ERR(drv->subsys)) {
|
||||
ret = PTR_ERR(drv->subsys);
|
||||
goto err_subsys;
|
||||
}
|
||||
|
||||
ret = pil_parse_irqs(pdev);
|
||||
if (ret) {
|
||||
subsys_unregister(drv->subsys);
|
||||
goto err_subsys;
|
||||
}
|
||||
|
||||
drv->ramdump_dev = create_ramdump_device("modem", &pdev->dev);
|
||||
if (!drv->ramdump_dev) {
|
||||
pr_err("%s: Unable to create a modem ramdump device.\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_ramdump;
|
||||
}
|
||||
drv->minidump_dev = create_ramdump_device("md_modem", &pdev->dev);
|
||||
if (!drv->minidump_dev) {
|
||||
pr_err("%s: Unable to create a modem minidump device.\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_minidump;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_minidump:
|
||||
destroy_ramdump_device(drv->ramdump_dev);
|
||||
err_ramdump:
|
||||
subsys_unregister(drv->subsys);
|
||||
err_subsys:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pil_mss_loadable_init(struct modem_data *drv,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct q6v5_data *q6;
|
||||
struct pil_desc *q6_desc;
|
||||
struct resource *res;
|
||||
struct property *prop;
|
||||
int ret;
|
||||
|
||||
q6 = pil_q6v5_init(pdev);
|
||||
if (IS_ERR_OR_NULL(q6))
|
||||
return PTR_ERR(q6);
|
||||
drv->q6 = q6;
|
||||
drv->xo = q6->xo;
|
||||
|
||||
q6_desc = &q6->desc;
|
||||
q6_desc->owner = THIS_MODULE;
|
||||
q6_desc->proxy_timeout = PROXY_TIMEOUT_MS;
|
||||
|
||||
q6_desc->ops = &pil_msa_mss_ops;
|
||||
|
||||
q6->reset_clk = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,reset-clk");
|
||||
q6->self_auth = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,pil-self-auth");
|
||||
if (q6->self_auth) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"rmb_base");
|
||||
q6->rmb_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(q6->rmb_base))
|
||||
return PTR_ERR(q6->rmb_base);
|
||||
drv->rmb_base = q6->rmb_base;
|
||||
q6_desc->ops = &pil_msa_mss_ops_selfauth;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
|
||||
if (!res) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"restart_reg_sec");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "No restart register defined\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
q6->restart_reg_sec = true;
|
||||
}
|
||||
|
||||
q6->restart_reg = devm_ioremap(&pdev->dev,
|
||||
res->start, resource_size(res));
|
||||
if (!q6->restart_reg)
|
||||
return -ENOMEM;
|
||||
|
||||
q6->pdc_sync = NULL;
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pdc_sync");
|
||||
if (res) {
|
||||
q6->pdc_sync = devm_ioremap(&pdev->dev,
|
||||
res->start, resource_size(res));
|
||||
if (of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,mss_pdc_offset", &q6->mss_pdc_offset)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Offset for MSS PDC not specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
q6->alt_reset = NULL;
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "alt_reset");
|
||||
if (res) {
|
||||
q6->alt_reset = devm_ioremap(&pdev->dev,
|
||||
res->start, resource_size(res));
|
||||
}
|
||||
|
||||
q6->vreg = NULL;
|
||||
|
||||
prop = of_find_property(pdev->dev.of_node, "vdd_mss-supply", NULL);
|
||||
if (prop) {
|
||||
q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
|
||||
if (IS_ERR(q6->vreg))
|
||||
return PTR_ERR(q6->vreg);
|
||||
}
|
||||
|
||||
q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx");
|
||||
if (IS_ERR(q6->vreg_mx))
|
||||
return PTR_ERR(q6->vreg_mx);
|
||||
prop = of_find_property(pdev->dev.of_node, "vdd_mx-uV", NULL);
|
||||
if (!prop) {
|
||||
dev_err(&pdev->dev, "Missing vdd_mx-uV property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"cxrail_bhs_reg");
|
||||
if (res)
|
||||
q6->cxrail_bhs = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
|
||||
q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
|
||||
if (IS_ERR(q6->ahb_clk))
|
||||
return PTR_ERR(q6->ahb_clk);
|
||||
|
||||
q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
|
||||
if (IS_ERR(q6->axi_clk))
|
||||
return PTR_ERR(q6->axi_clk);
|
||||
|
||||
q6->rom_clk = devm_clk_get(&pdev->dev, "mem_clk");
|
||||
if (IS_ERR(q6->rom_clk))
|
||||
return PTR_ERR(q6->rom_clk);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,pas-id", &drv->pas_id);
|
||||
if (ret)
|
||||
dev_info(&pdev->dev, "No pas_id found.\n");
|
||||
|
||||
drv->subsys_desc.pil_mss_memsetup =
|
||||
of_property_read_bool(pdev->dev.of_node, "qcom,pil-mss-memsetup");
|
||||
|
||||
/* Optional. */
|
||||
if (of_property_match_string(pdev->dev.of_node,
|
||||
"qcom,active-clock-names", "gpll0_mss_clk") >= 0)
|
||||
q6->gpll0_mss_clk = devm_clk_get(&pdev->dev, "gpll0_mss_clk");
|
||||
|
||||
if (of_property_match_string(pdev->dev.of_node,
|
||||
"qcom,active-clock-names", "snoc_axi_clk") >= 0)
|
||||
q6->snoc_axi_clk = devm_clk_get(&pdev->dev, "snoc_axi_clk");
|
||||
|
||||
if (of_property_match_string(pdev->dev.of_node,
|
||||
"qcom,active-clock-names", "mnoc_axi_clk") >= 0)
|
||||
q6->mnoc_axi_clk = devm_clk_get(&pdev->dev, "mnoc_axi_clk");
|
||||
|
||||
/* Defaulting smem_id to be not present */
|
||||
q6->smem_id = -1;
|
||||
|
||||
if (of_find_property(pdev->dev.of_node, "qcom,smem-id", NULL)) {
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "qcom,smem-id",
|
||||
&q6->smem_id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to get the smem_id(ret:%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = pil_desc_init(q6_desc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pil_mss_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct modem_data *drv;
|
||||
int ret;
|
||||
|
||||
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, drv);
|
||||
|
||||
ret = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,is-not-loadable");
|
||||
if (ret) {
|
||||
drv->is_not_loadable = 1;
|
||||
} else {
|
||||
ret = pil_mss_loadable_init(drv, pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
init_completion(&drv->stop_ack);
|
||||
init_completion(&drv->err_ready);
|
||||
|
||||
/* Probe the MBA mem device if present */
|
||||
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return pil_subsys_init(drv, pdev);
|
||||
}
|
||||
|
||||
static int pil_mss_driver_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct modem_data *drv = platform_get_drvdata(pdev);
|
||||
|
||||
subsys_unregister(drv->subsys);
|
||||
destroy_ramdump_device(drv->ramdump_dev);
|
||||
destroy_ramdump_device(drv->minidump_dev);
|
||||
pil_desc_release(&drv->q6->desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pil_mba_mem_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct modem_data *drv;
|
||||
|
||||
if (!pdev->dev.parent) {
|
||||
pr_err("No parent found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
drv = dev_get_drvdata(pdev->dev.parent);
|
||||
drv->mba_mem_dev_fixed = &pdev->dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mba_mem_match_table[] = {
|
||||
{ .compatible = "qcom,pil-mba-mem" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver pil_mba_mem_driver = {
|
||||
.probe = pil_mba_mem_driver_probe,
|
||||
.driver = {
|
||||
.name = "pil-mba-mem",
|
||||
.of_match_table = mba_mem_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id mss_match_table[] = {
|
||||
{ .compatible = "qcom,pil-q6v5-mss" },
|
||||
{ .compatible = "qcom,pil-q6v55-mss" },
|
||||
{ .compatible = "qcom,pil-q6v56-mss" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver pil_mss_driver = {
|
||||
.probe = pil_mss_driver_probe,
|
||||
.remove = pil_mss_driver_exit,
|
||||
.driver = {
|
||||
.name = "pil-q6v5-mss",
|
||||
.of_match_table = mss_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pil_mss_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&pil_mba_mem_driver);
|
||||
if (!ret)
|
||||
ret = platform_driver_register(&pil_mss_driver);
|
||||
return ret;
|
||||
}
|
||||
module_init(pil_mss_init);
|
||||
|
||||
static void __exit pil_mss_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pil_mss_driver);
|
||||
}
|
||||
module_exit(pil_mss_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
839
drivers/soc/qcom/pil-q6v5.c
Normal file
839
drivers/soc/qcom/pil-q6v5.c
Normal file
|
|
@ -0,0 +1,839 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <trace/events/trace_msm_pil_event.h>
|
||||
|
||||
#include "peripheral-loader.h"
|
||||
#include "pil-msa.h"
|
||||
#include "pil-q6v5.h"
|
||||
|
||||
/* QDSP6SS Register Offsets */
|
||||
#define QDSP6SS_RESET 0x014
|
||||
#define QDSP6SS_GFMUX_CTL 0x020
|
||||
#define QDSP6SS_PWR_CTL 0x030
|
||||
#define QDSP6V6SS_MEM_PWR_CTL 0x034
|
||||
#define QDSP6SS_BHS_STATUS 0x078
|
||||
#define QDSP6SS_MEM_PWR_CTL 0x0B0
|
||||
#define QDSP6SS_STRAP_ACC 0x110
|
||||
#define QDSP6V62SS_BHS_STATUS 0x0C4
|
||||
|
||||
/* AXI Halt Register Offsets */
|
||||
#define AXI_HALTREQ 0x0
|
||||
#define AXI_HALTACK 0x4
|
||||
#define AXI_IDLE 0x8
|
||||
|
||||
#define HALT_ACK_TIMEOUT_US 100000
|
||||
|
||||
/* QDSP6SS_RESET */
|
||||
#define Q6SS_STOP_CORE BIT(0)
|
||||
#define Q6SS_CORE_ARES BIT(1)
|
||||
#define Q6SS_BUS_ARES_ENA BIT(2)
|
||||
|
||||
/* QDSP6SS_GFMUX_CTL */
|
||||
#define Q6SS_CLK_ENA BIT(1)
|
||||
#define Q6SS_CLK_SRC_SEL_C BIT(3)
|
||||
#define Q6SS_CLK_SRC_SEL_FIELD 0xC
|
||||
#define Q6SS_CLK_SRC_SWITCH_CLK_OVR BIT(8)
|
||||
|
||||
/* QDSP6SS_PWR_CTL */
|
||||
#define Q6SS_L2DATA_SLP_NRET_N_0 BIT(0)
|
||||
#define Q6SS_L2DATA_SLP_NRET_N_1 BIT(1)
|
||||
#define Q6SS_L2DATA_SLP_NRET_N_2 BIT(2)
|
||||
#define Q6SS_L2TAG_SLP_NRET_N BIT(16)
|
||||
#define Q6SS_ETB_SLP_NRET_N BIT(17)
|
||||
#define Q6SS_L2DATA_STBY_N BIT(18)
|
||||
#define Q6SS_SLP_RET_N BIT(19)
|
||||
#define Q6SS_CLAMP_IO BIT(20)
|
||||
#define QDSS_BHS_ON BIT(21)
|
||||
#define QDSS_LDO_BYP BIT(22)
|
||||
|
||||
/* QDSP6v55 parameters */
|
||||
#define QDSP6v55_LDO_ON BIT(26)
|
||||
#define QDSP6v55_LDO_BYP BIT(25)
|
||||
#define QDSP6v55_BHS_ON BIT(24)
|
||||
#define QDSP6v55_CLAMP_WL BIT(21)
|
||||
#define QDSP6v55_CLAMP_QMC_MEM BIT(22)
|
||||
#define L1IU_SLP_NRET_N BIT(15)
|
||||
#define L1DU_SLP_NRET_N BIT(14)
|
||||
#define L2PLRU_SLP_NRET_N BIT(13)
|
||||
#define QDSP6v55_BHS_EN_REST_ACK BIT(0)
|
||||
|
||||
#define HALT_CHECK_MAX_LOOPS (200)
|
||||
#define BHS_CHECK_MAX_LOOPS (200)
|
||||
#define QDSP6SS_XO_CBCR (0x0038)
|
||||
|
||||
/* QDSP6v65 parameters */
|
||||
#define QDSP6SS_BOOT_CORE_START (0x400)
|
||||
#define QDSP6SS_BOOT_CMD (0x404)
|
||||
#define MSS_STATUS (0x40)
|
||||
#define QDSP6SS_SLEEP (0x3C)
|
||||
#define SLEEP_CHECK_MAX_LOOPS (200)
|
||||
#define BOOT_FSM_TIMEOUT (10000)
|
||||
|
||||
#define QDSP6SS_ACC_OVERRIDE_VAL 0x20
|
||||
|
||||
int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
|
||||
{
|
||||
int ret;
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
int uv;
|
||||
|
||||
ret = of_property_read_u32(pil->dev->of_node, "vdd_cx-voltage", &uv);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "missing vdd_cx-voltage property(rc:%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drv->xo);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to vote for XO(rc:%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drv->pnoc_clk);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to vote for pnoc(rc:%d)\n", ret);
|
||||
goto err_pnoc_vote;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drv->qdss_clk);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to vote for qdss(rc:%d)\n", ret);
|
||||
goto err_qdss_vote;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drv->prng_clk);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to vote for prng(rc:%d)\n", ret);
|
||||
goto err_prng_vote;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drv->axis2_clk);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to vote for axis2(rc:%d)\n", ret);
|
||||
goto err_axis2_vote;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(drv->vreg_cx, uv, INT_MAX);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to request vdd_cx voltage(rc:%d)\n",
|
||||
ret);
|
||||
goto err_cx_voltage;
|
||||
}
|
||||
|
||||
ret = regulator_set_load(drv->vreg_cx, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(pil->dev, "Failed to set vdd_cx mode(rc:%d)\n", ret);
|
||||
goto err_cx_mode;
|
||||
}
|
||||
|
||||
ret = regulator_enable(drv->vreg_cx);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to vote for vdd_cx(rc:%d)\n", ret);
|
||||
goto err_cx_enable;
|
||||
}
|
||||
|
||||
if (drv->vreg_pll) {
|
||||
ret = regulator_enable(drv->vreg_pll);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "Failed to vote for vdd_pll(rc:%d)\n",
|
||||
ret);
|
||||
goto err_vreg_pll;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_vreg_pll:
|
||||
regulator_disable(drv->vreg_cx);
|
||||
err_cx_enable:
|
||||
regulator_set_load(drv->vreg_cx, 0);
|
||||
err_cx_mode:
|
||||
regulator_set_voltage(drv->vreg_cx, 0, INT_MAX);
|
||||
err_cx_voltage:
|
||||
clk_disable_unprepare(drv->axis2_clk);
|
||||
err_axis2_vote:
|
||||
clk_disable_unprepare(drv->prng_clk);
|
||||
err_prng_vote:
|
||||
clk_disable_unprepare(drv->qdss_clk);
|
||||
err_qdss_vote:
|
||||
clk_disable_unprepare(drv->pnoc_clk);
|
||||
err_pnoc_vote:
|
||||
clk_disable_unprepare(drv->xo);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pil_q6v5_make_proxy_votes);
|
||||
|
||||
void pil_q6v5_remove_proxy_votes(struct pil_desc *pil)
|
||||
{
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
int uv, ret = 0;
|
||||
|
||||
ret = of_property_read_u32(pil->dev->of_node, "vdd_cx-voltage", &uv);
|
||||
if (ret) {
|
||||
dev_err(pil->dev, "missing vdd_cx-voltage property(rc:%d)\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (drv->vreg_pll) {
|
||||
regulator_disable(drv->vreg_pll);
|
||||
regulator_set_load(drv->vreg_pll, 0);
|
||||
}
|
||||
regulator_disable(drv->vreg_cx);
|
||||
regulator_set_load(drv->vreg_cx, 0);
|
||||
regulator_set_voltage(drv->vreg_cx, 0, INT_MAX);
|
||||
clk_disable_unprepare(drv->xo);
|
||||
clk_disable_unprepare(drv->pnoc_clk);
|
||||
clk_disable_unprepare(drv->qdss_clk);
|
||||
clk_disable_unprepare(drv->prng_clk);
|
||||
clk_disable_unprepare(drv->axis2_clk);
|
||||
}
|
||||
EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes);
|
||||
|
||||
void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base)
|
||||
{
|
||||
int ret;
|
||||
u32 status;
|
||||
|
||||
/* Assert halt request */
|
||||
writel_relaxed(1, halt_base + AXI_HALTREQ);
|
||||
|
||||
/* Wait for halt */
|
||||
ret = readl_poll_timeout(halt_base + AXI_HALTACK,
|
||||
status, status != 0, 50, HALT_ACK_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_warn(pil->dev, "Port %pK halt timeout\n", halt_base);
|
||||
else if (!readl_relaxed(halt_base + AXI_IDLE))
|
||||
dev_warn(pil->dev, "Port %pK halt failed\n", halt_base);
|
||||
|
||||
/* Clear halt request (port will remain halted until reset) */
|
||||
writel_relaxed(0, halt_base + AXI_HALTREQ);
|
||||
}
|
||||
EXPORT_SYMBOL(pil_q6v5_halt_axi_port);
|
||||
|
||||
void assert_clamps(struct pil_desc *pil)
|
||||
{
|
||||
u32 val;
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
|
||||
/*
|
||||
* Assert QDSP6 I/O clamp, memory wordline clamp, and compiler memory
|
||||
* clamp as a software workaround to avoid high MX current during
|
||||
* LPASS/MSS restart.
|
||||
*/
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= (Q6SS_CLAMP_IO | QDSP6v55_CLAMP_WL |
|
||||
QDSP6v55_CLAMP_QMC_MEM);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
/* To make sure asserting clamps is done before MSS restart*/
|
||||
mb();
|
||||
}
|
||||
|
||||
static void __pil_q6v5_shutdown(struct pil_desc *pil)
|
||||
{
|
||||
u32 val;
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
|
||||
/* Turn off core clock */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
|
||||
val &= ~Q6SS_CLK_ENA;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL);
|
||||
|
||||
/* Clamp IO */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= Q6SS_CLAMP_IO;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Turn off Q6 memories */
|
||||
val &= ~(Q6SS_L2DATA_SLP_NRET_N_0 | Q6SS_L2DATA_SLP_NRET_N_1 |
|
||||
Q6SS_L2DATA_SLP_NRET_N_2 | Q6SS_SLP_RET_N |
|
||||
Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLP_NRET_N |
|
||||
Q6SS_L2DATA_STBY_N);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Assert Q6 resets */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
|
||||
val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
|
||||
|
||||
/* Kill power at block headswitch */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val &= ~QDSS_BHS_ON;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
}
|
||||
|
||||
void pil_q6v5_shutdown(struct pil_desc *pil)
|
||||
{
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
|
||||
if (drv->qdsp6v55) {
|
||||
/* Subsystem driver expected to halt bus and assert reset */
|
||||
return;
|
||||
}
|
||||
__pil_q6v5_shutdown(pil);
|
||||
}
|
||||
EXPORT_SYMBOL(pil_q6v5_shutdown);
|
||||
|
||||
static int __pil_q6v5_reset(struct pil_desc *pil)
|
||||
{
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
u32 val;
|
||||
|
||||
/* Assert resets, stop core */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
|
||||
val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA | Q6SS_STOP_CORE);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
|
||||
|
||||
/* Enable power block headswitch, and wait for it to stabilize */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= QDSS_BHS_ON | QDSS_LDO_BYP;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Ensure physical memory access is done*/
|
||||
mb();
|
||||
udelay(1);
|
||||
|
||||
/*
|
||||
* Turn on memories. L2 banks should be done individually
|
||||
* to minimize inrush current.
|
||||
*/
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N |
|
||||
Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= Q6SS_L2DATA_SLP_NRET_N_2;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= Q6SS_L2DATA_SLP_NRET_N_1;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= Q6SS_L2DATA_SLP_NRET_N_0;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Remove IO clamp */
|
||||
val &= ~Q6SS_CLAMP_IO;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Bring core out of reset */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
|
||||
val &= ~Q6SS_CORE_ARES;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
|
||||
|
||||
/* Turn on core clock */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
|
||||
val |= Q6SS_CLK_ENA;
|
||||
|
||||
/* Need a different clock source for v5.2.0 */
|
||||
if (drv->qdsp6v5_2_0) {
|
||||
val &= ~Q6SS_CLK_SRC_SEL_FIELD;
|
||||
val |= Q6SS_CLK_SRC_SEL_C;
|
||||
}
|
||||
|
||||
/* force clock on during source switch */
|
||||
if (drv->qdsp6v56)
|
||||
val |= Q6SS_CLK_SRC_SWITCH_CLK_OVR;
|
||||
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL);
|
||||
|
||||
/* Start core execution */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
|
||||
val &= ~Q6SS_STOP_CORE;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int q6v55_branch_clk_enable(struct q6v5_data *drv)
|
||||
{
|
||||
u32 val, count;
|
||||
void __iomem *cbcr_reg = drv->reg_base + QDSP6SS_XO_CBCR;
|
||||
|
||||
val = readl_relaxed(cbcr_reg);
|
||||
val |= 0x1;
|
||||
writel_relaxed(val, cbcr_reg);
|
||||
|
||||
for (count = HALT_CHECK_MAX_LOOPS; count > 0; count--) {
|
||||
val = readl_relaxed(cbcr_reg);
|
||||
if (!(val & BIT(31)))
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
dev_err(drv->desc.dev, "Failed to enable xo branch clock.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __pil_q6v65_reset(struct pil_desc *pil)
|
||||
{
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
u32 val, count;
|
||||
int ret;
|
||||
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_SLEEP);
|
||||
val |= 0x1;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_SLEEP);
|
||||
for (count = SLEEP_CHECK_MAX_LOOPS; count > 0; count--) {
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_SLEEP);
|
||||
if (!(val & BIT(31)))
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
dev_err(drv->desc.dev, "Sleep clock did not come on in time\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* De-assert QDSP6 stop core */
|
||||
writel_relaxed(1, drv->reg_base + QDSP6SS_BOOT_CORE_START);
|
||||
/* De-assert stop core before starting boot FSM */
|
||||
mb();
|
||||
/* Trigger boot FSM */
|
||||
writel_relaxed(1, drv->reg_base + QDSP6SS_BOOT_CMD);
|
||||
|
||||
/* Wait for boot FSM to complete */
|
||||
ret = readl_poll_timeout(drv->rmb_base + MSS_STATUS, val,
|
||||
(val & BIT(0)) != 0, 10, BOOT_FSM_TIMEOUT);
|
||||
|
||||
if (ret) {
|
||||
dev_err(drv->desc.dev, "Boot FSM failed to complete.\n");
|
||||
/* Reset the modem so that boot FSM is in reset state */
|
||||
pil_mss_assert_resets(drv);
|
||||
/* Wait 6 32kHz sleep cycles for reset */
|
||||
udelay(200);
|
||||
pil_mss_deassert_resets(drv);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __pil_q6v55_reset(struct pil_desc *pil)
|
||||
{
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
trace_pil_func(__func__);
|
||||
/* Override the ACC value if required */
|
||||
if (drv->override_acc)
|
||||
writel_relaxed(QDSP6SS_ACC_OVERRIDE_VAL,
|
||||
drv->reg_base + QDSP6SS_STRAP_ACC);
|
||||
|
||||
/* Override the ACC value with input value */
|
||||
if (!of_property_read_u32(pil->dev->of_node, "qcom,override-acc-1",
|
||||
&drv->override_acc_1))
|
||||
writel_relaxed(drv->override_acc_1,
|
||||
drv->reg_base + QDSP6SS_STRAP_ACC);
|
||||
|
||||
/* Assert resets, stop core */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
|
||||
val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA | Q6SS_STOP_CORE);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
|
||||
|
||||
/* BHS require xo cbcr to be enabled */
|
||||
i = q6v55_branch_clk_enable(drv);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
/* Enable power block headswitch, and wait for it to stabilize */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= QDSP6v55_BHS_ON;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Ensure physical memory access is done*/
|
||||
mb();
|
||||
udelay(1);
|
||||
|
||||
if (drv->qdsp6v62_1_2 || drv->qdsp6v62_1_5 || drv->qdsp6v62_1_4) {
|
||||
for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) {
|
||||
if (readl_relaxed(drv->reg_base + QDSP6V62SS_BHS_STATUS)
|
||||
& QDSP6v55_BHS_EN_REST_ACK)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
if (!i) {
|
||||
pr_err("%s: BHS_EN_REST_ACK not set!\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (drv->qdsp6v61_1_1) {
|
||||
for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) {
|
||||
if (readl_relaxed(drv->reg_base + QDSP6SS_BHS_STATUS)
|
||||
& QDSP6v55_BHS_EN_REST_ACK)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
if (!i) {
|
||||
pr_err("%s: BHS_EN_REST_ACK not set!\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Put LDO in bypass mode */
|
||||
val |= QDSP6v55_LDO_BYP;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
if (drv->qdsp6v56_1_3) {
|
||||
/* Deassert memory peripheral sleep and L2 memory standby */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Turn on L1, L2 and ETB memories 1 at a time */
|
||||
for (i = 17; i >= 0; i--) {
|
||||
val |= BIT(i);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
udelay(1);
|
||||
}
|
||||
} else if (drv->qdsp6v56_1_5 || drv->qdsp6v56_1_8
|
||||
|| drv->qdsp6v56_1_10) {
|
||||
/* Deassert QDSP6 compiler memory clamp */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val &= ~QDSP6v55_CLAMP_QMC_MEM;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Deassert memory peripheral sleep and L2 memory standby */
|
||||
val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Turn on L1, L2, ETB and JU memories 1 at a time */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_MEM_PWR_CTL);
|
||||
for (i = 19; i >= 0; i--) {
|
||||
val |= BIT(i);
|
||||
writel_relaxed(val, drv->reg_base +
|
||||
QDSP6SS_MEM_PWR_CTL);
|
||||
val |= readl_relaxed(drv->reg_base +
|
||||
QDSP6SS_MEM_PWR_CTL);
|
||||
/*
|
||||
* Wait for 1us for both memory peripheral and
|
||||
* data array to turn on.
|
||||
*/
|
||||
udelay(1);
|
||||
}
|
||||
} else if (drv->qdsp6v56_1_8_inrush_current) {
|
||||
/* Deassert QDSP6 compiler memory clamp */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val &= ~QDSP6v55_CLAMP_QMC_MEM;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Deassert memory peripheral sleep and L2 memory standby */
|
||||
val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Turn on L1, L2, ETB and JU memories 1 at a time */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_MEM_PWR_CTL);
|
||||
for (i = 19; i >= 6; i--) {
|
||||
val |= BIT(i);
|
||||
writel_relaxed(val, drv->reg_base +
|
||||
QDSP6SS_MEM_PWR_CTL);
|
||||
/*
|
||||
* Wait for 1us for both memory peripheral and
|
||||
* data array to turn on.
|
||||
*/
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
for (i = 0 ; i <= 5 ; i++) {
|
||||
val |= BIT(i);
|
||||
writel_relaxed(val, drv->reg_base +
|
||||
QDSP6SS_MEM_PWR_CTL);
|
||||
/*
|
||||
* Wait for 1us for both memory peripheral and
|
||||
* data array to turn on.
|
||||
*/
|
||||
udelay(1);
|
||||
}
|
||||
} else if (drv->qdsp6v61_1_1 || drv->qdsp6v62_1_2 ||
|
||||
drv->qdsp6v62_1_4 || drv->qdsp6v62_1_5) {
|
||||
/* Deassert QDSP6 compiler memory clamp */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val &= ~QDSP6v55_CLAMP_QMC_MEM;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Deassert memory peripheral sleep and L2 memory standby */
|
||||
val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Turn on L1, L2, ETB and JU memories 1 at a time */
|
||||
val = readl_relaxed(drv->reg_base +
|
||||
QDSP6V6SS_MEM_PWR_CTL);
|
||||
|
||||
if (drv->qdsp6v62_1_4 || drv->qdsp6v62_1_5)
|
||||
i = 29;
|
||||
else
|
||||
i = 28;
|
||||
|
||||
for ( ; i >= 0; i--) {
|
||||
val |= BIT(i);
|
||||
writel_relaxed(val, drv->reg_base +
|
||||
QDSP6V6SS_MEM_PWR_CTL);
|
||||
/*
|
||||
* Wait for 1us for both memory peripheral and
|
||||
* data array to turn on.
|
||||
*/
|
||||
udelay(1);
|
||||
}
|
||||
} else {
|
||||
/* Turn on memories. */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val |= 0xFFF00;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Turn on L2 banks 1 at a time */
|
||||
for (i = 0; i <= 7; i++) {
|
||||
val |= BIT(i);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove word line clamp */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
val &= ~QDSP6v55_CLAMP_WL;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Remove IO clamp */
|
||||
val &= ~Q6SS_CLAMP_IO;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL);
|
||||
|
||||
/* Bring core out of reset */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_RESET);
|
||||
val &= ~(Q6SS_CORE_ARES | Q6SS_STOP_CORE);
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_RESET);
|
||||
|
||||
/* Turn on core clock */
|
||||
val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
|
||||
val |= Q6SS_CLK_ENA;
|
||||
writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pil_q6v5_reset(struct pil_desc *pil)
|
||||
{
|
||||
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
|
||||
|
||||
|
||||
if (drv->qdsp6v65_1_0)
|
||||
return __pil_q6v65_reset(pil);
|
||||
else if (drv->qdsp6v55)
|
||||
return __pil_q6v55_reset(pil);
|
||||
else
|
||||
return __pil_q6v5_reset(pil);
|
||||
}
|
||||
EXPORT_SYMBOL(pil_q6v5_reset);
|
||||
|
||||
struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
|
||||
{
|
||||
struct q6v5_data *drv;
|
||||
struct resource *res;
|
||||
struct pil_desc *desc;
|
||||
struct property *prop;
|
||||
int ret, vdd_pll;
|
||||
|
||||
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
|
||||
if (!drv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base");
|
||||
drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(drv->reg_base))
|
||||
return drv->reg_base;
|
||||
|
||||
desc = &drv->desc;
|
||||
ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
|
||||
&desc->name);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
desc->clear_fw_region = false;
|
||||
desc->dev = &pdev->dev;
|
||||
|
||||
drv->qdsp6v5_2_0 = of_device_is_compatible(pdev->dev.of_node,
|
||||
"qcom,pil-femto-modem");
|
||||
|
||||
if (drv->qdsp6v5_2_0)
|
||||
return drv;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base");
|
||||
if (res) {
|
||||
drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!drv->axi_halt_base) {
|
||||
dev_err(&pdev->dev, "Failed to map axi_halt_base.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
if (!drv->axi_halt_base) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"halt_q6");
|
||||
if (res) {
|
||||
drv->axi_halt_q6 = devm_ioremap(&pdev->dev,
|
||||
res->start, resource_size(res));
|
||||
if (!drv->axi_halt_q6) {
|
||||
dev_err(&pdev->dev, "Failed to map axi_halt_q6.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"halt_modem");
|
||||
if (res) {
|
||||
drv->axi_halt_mss = devm_ioremap(&pdev->dev,
|
||||
res->start, resource_size(res));
|
||||
if (!drv->axi_halt_mss) {
|
||||
dev_err(&pdev->dev, "Failed to map axi_halt_mss.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"halt_nc");
|
||||
if (res) {
|
||||
drv->axi_halt_nc = devm_ioremap(&pdev->dev,
|
||||
res->start, resource_size(res));
|
||||
if (!drv->axi_halt_nc) {
|
||||
dev_err(&pdev->dev, "Failed to map axi_halt_nc.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(drv->axi_halt_base || (drv->axi_halt_q6 && drv->axi_halt_mss
|
||||
&& drv->axi_halt_nc))) {
|
||||
dev_err(&pdev->dev, "halt bases for Q6 are not defined.\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
drv->qdsp6v55 = of_device_is_compatible(pdev->dev.of_node,
|
||||
"qcom,pil-q6v55-mss");
|
||||
drv->qdsp6v56 = of_device_is_compatible(pdev->dev.of_node,
|
||||
"qcom,pil-q6v56-mss");
|
||||
|
||||
drv->qdsp6v56_1_3 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v56-1-3");
|
||||
drv->qdsp6v56_1_5 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v56-1-5");
|
||||
|
||||
drv->qdsp6v56_1_8 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v56-1-8");
|
||||
drv->qdsp6v56_1_10 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v56-1-10");
|
||||
|
||||
drv->qdsp6v56_1_8_inrush_current = of_property_read_bool(
|
||||
pdev->dev.of_node,
|
||||
"qcom,qdsp6v56-1-8-inrush-current");
|
||||
|
||||
drv->qdsp6v61_1_1 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v61-1-1");
|
||||
|
||||
drv->qdsp6v62_1_2 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v62-1-2");
|
||||
|
||||
drv->qdsp6v62_1_4 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v62-1-4");
|
||||
|
||||
drv->qdsp6v62_1_5 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v62-1-5");
|
||||
|
||||
drv->qdsp6v65_1_0 = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,qdsp6v65-1-0");
|
||||
|
||||
drv->non_elf_image = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,mba-image-is-not-elf");
|
||||
|
||||
drv->override_acc = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,override-acc");
|
||||
|
||||
drv->ahb_clk_vote = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,ahb-clk-vote");
|
||||
drv->mx_spike_wa = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,mx-spike-wa");
|
||||
|
||||
drv->xo = devm_clk_get(&pdev->dev, "xo");
|
||||
if (IS_ERR(drv->xo))
|
||||
return ERR_CAST(drv->xo);
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "qcom,pnoc-clk-vote")) {
|
||||
drv->pnoc_clk = devm_clk_get(&pdev->dev, "pnoc_clk");
|
||||
if (IS_ERR(drv->pnoc_clk))
|
||||
return ERR_CAST(drv->pnoc_clk);
|
||||
} else {
|
||||
drv->pnoc_clk = NULL;
|
||||
}
|
||||
|
||||
if (of_property_match_string(pdev->dev.of_node,
|
||||
"qcom,proxy-clock-names", "qdss_clk") >= 0) {
|
||||
drv->qdss_clk = devm_clk_get(&pdev->dev, "qdss_clk");
|
||||
if (IS_ERR(drv->qdss_clk))
|
||||
return ERR_CAST(drv->qdss_clk);
|
||||
} else {
|
||||
drv->qdss_clk = NULL;
|
||||
}
|
||||
|
||||
if (of_property_match_string(pdev->dev.of_node,
|
||||
"qcom,proxy-clock-names", "prng_clk") >= 0) {
|
||||
drv->prng_clk = devm_clk_get(&pdev->dev, "prng_clk");
|
||||
if (IS_ERR(drv->prng_clk))
|
||||
return ERR_CAST(drv->prng_clk);
|
||||
} else {
|
||||
drv->prng_clk = NULL;
|
||||
}
|
||||
|
||||
if (of_property_match_string(pdev->dev.of_node,
|
||||
"qcom,proxy-clock-names", "axis2_clk") >= 0) {
|
||||
drv->axis2_clk = devm_clk_get(&pdev->dev, "axis2_clk");
|
||||
if (IS_ERR(drv->axis2_clk))
|
||||
return ERR_CAST(drv->axis2_clk);
|
||||
} else {
|
||||
drv->axis2_clk = NULL;
|
||||
}
|
||||
|
||||
drv->vreg_cx = devm_regulator_get(&pdev->dev, "vdd_cx");
|
||||
if (IS_ERR(drv->vreg_cx))
|
||||
return ERR_CAST(drv->vreg_cx);
|
||||
prop = of_find_property(pdev->dev.of_node, "vdd_cx-voltage", NULL);
|
||||
if (!prop) {
|
||||
dev_err(&pdev->dev, "Missing vdd_cx-voltage property\n");
|
||||
return ERR_CAST(prop);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "qcom,vdd_pll",
|
||||
&vdd_pll);
|
||||
if (!ret) {
|
||||
drv->vreg_pll = devm_regulator_get(&pdev->dev, "vdd_pll");
|
||||
if (!IS_ERR_OR_NULL(drv->vreg_pll)) {
|
||||
ret = regulator_set_voltage(drv->vreg_pll, vdd_pll,
|
||||
vdd_pll);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to set vdd_pll voltage(rc:%d)\n",
|
||||
ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = regulator_set_load(drv->vreg_pll, 10000);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to set vdd_pll mode(rc:%d)\n",
|
||||
ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
} else
|
||||
drv->vreg_pll = NULL;
|
||||
}
|
||||
|
||||
return drv;
|
||||
}
|
||||
EXPORT_SYMBOL(pil_q6v5_init);
|
||||
84
drivers/soc/qcom/pil-q6v5.h
Normal file
84
drivers/soc/qcom/pil-q6v5.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#ifndef __MSM_PIL_Q6V5_H
|
||||
#define __MSM_PIL_Q6V5_H
|
||||
|
||||
#include "peripheral-loader.h"
|
||||
|
||||
struct regulator;
|
||||
struct clk;
|
||||
struct pil_device;
|
||||
struct platform_device;
|
||||
|
||||
struct q6v5_data {
|
||||
void __iomem *reg_base;
|
||||
void __iomem *rmb_base;
|
||||
void __iomem *cxrail_bhs; /* External BHS register */
|
||||
struct clk *xo; /* XO clock source */
|
||||
struct clk *pnoc_clk; /* PNOC bus clock source */
|
||||
struct clk *ahb_clk; /* PIL access to registers */
|
||||
struct clk *axi_clk; /* CPU access to memory */
|
||||
struct clk *core_clk; /* CPU core */
|
||||
struct clk *reg_clk; /* CPU access registers */
|
||||
struct clk *gpll0_mss_clk; /* GPLL0 to MSS connection */
|
||||
struct clk *rom_clk; /* Boot ROM */
|
||||
struct clk *snoc_axi_clk;
|
||||
struct clk *mnoc_axi_clk;
|
||||
struct clk *qdss_clk;
|
||||
struct clk *prng_clk;
|
||||
struct clk *axis2_clk;
|
||||
void __iomem *axi_halt_base; /* Halt base of q6, mss,
|
||||
* nc are in same 4K page
|
||||
*/
|
||||
void __iomem *axi_halt_q6;
|
||||
void __iomem *axi_halt_mss;
|
||||
void __iomem *axi_halt_nc;
|
||||
void __iomem *restart_reg;
|
||||
void __iomem *pdc_sync;
|
||||
void __iomem *alt_reset;
|
||||
struct regulator *vreg;
|
||||
struct regulator *vreg_cx;
|
||||
struct regulator *vreg_mx;
|
||||
struct regulator *vreg_pll;
|
||||
bool is_booted;
|
||||
struct pil_desc desc;
|
||||
bool self_auth;
|
||||
phys_addr_t mba_dp_phys;
|
||||
void *mba_dp_virt;
|
||||
size_t mba_dp_size;
|
||||
size_t dp_size;
|
||||
bool qdsp6v55;
|
||||
bool qdsp6v5_2_0;
|
||||
bool qdsp6v56;
|
||||
bool qdsp6v56_1_3;
|
||||
bool qdsp6v56_1_5;
|
||||
bool qdsp6v56_1_8;
|
||||
bool qdsp6v56_1_8_inrush_current;
|
||||
bool qdsp6v56_1_10;
|
||||
bool qdsp6v61_1_1;
|
||||
bool qdsp6v62_1_2;
|
||||
bool qdsp6v62_1_4;
|
||||
bool qdsp6v62_1_5;
|
||||
bool qdsp6v65_1_0;
|
||||
bool non_elf_image;
|
||||
bool restart_reg_sec;
|
||||
bool override_acc;
|
||||
int override_acc_1;
|
||||
int mss_pdc_offset;
|
||||
int smem_id;
|
||||
bool ahb_clk_vote;
|
||||
bool mx_spike_wa;
|
||||
bool reset_clk;
|
||||
};
|
||||
|
||||
int pil_q6v5_make_proxy_votes(struct pil_desc *pil);
|
||||
void pil_q6v5_remove_proxy_votes(struct pil_desc *pil);
|
||||
void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base);
|
||||
void pil_q6v5_shutdown(struct pil_desc *pil);
|
||||
int pil_q6v5_reset(struct pil_desc *pil);
|
||||
void assert_clamps(struct pil_desc *pil);
|
||||
struct q6v5_data *pil_q6v5_init(struct platform_device *pdev);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue