Qualcomm ARM Based Driver Updates for v5.3
* Add ACPI support to Qualcomm GENI SE * Update Qualcomm Maintainers entry to remove David Brown as maintainer and fixup typos and incorrect DT file entry * Fixup APR domain id usage and making callbacks in non-atomic context * Add AOSS QMP driver and bindings * Add power domains for MSM8998 and QCS404 in QCOM RPMPD * Add corner macros, max state support, and fixups for setting performance state for Qcom RPMPD -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJdCeKEAAoJEFKiBbHx2RXVwvsP/3SBUTeyDP7HsD1+D9f8ojhC 8TDQIpQw9jB+txczdnnoF5vewALpwc3E+intuUZd3oOPB6Nw/g6VU73HDGSDOBHc nbAf5UjpJgyo7XI+tJk9/m27Ee7y+MIzrk2qtDoboOv0/PZzfrAoFpm75mFJsylw luf3TMCTDd23aj6HxVe6dzz5F0gHVxF8hldlDkgiQZXGK0h/yInHEYgRe8vBdQGD wRkU8jKBU49n3OOY7qqD9PKKNSREC6nYxWIK/EJdjaMSNlwmzNJYUXANgtJj4/6G WmNBA84EplqDJIz6GJ6u5oTYQYISxNlvk0w0USmtXFTktv4wP1IsXb9swZGsYD2B lD825pM72tO3YqS14HHZ/GvxNsZvn5THPlUrp5Z5s5o+l4HL88FxHOfd6vk/H5mF dP/MO3xSIRD65FHZMOoSevAJkXtxZhAYNcaTBWWCPpJwgb6iPOY4q17R5ODATJgA 5qJmebzWEn0AoseVEx6PTiejInf6yoDeC5iwcYEjIXqESZrLTvs0+kuZ/nbsD5v4 1yUWGpw1fhVctYmesJk7CfQ2tFLbTtSH+1XTxHnqltriy1I01meTIA5azSaAumzx YGzVhmsAp0RxmNPjB41L8B5A3V1LfqXcQwauT5pCzGif9uQKxpjUoS6gSwvAa8Da QAuwPgQEH5ISDat755pO =JunO -----END PGP SIGNATURE----- Merge tag 'qcom-drivers-for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into arm/drivers Qualcomm ARM Based Driver Updates for v5.3 * Add ACPI support to Qualcomm GENI SE * Update Qualcomm Maintainers entry to remove David Brown as maintainer and fixup typos and incorrect DT file entry * Fixup APR domain id usage and making callbacks in non-atomic context * Add AOSS QMP driver and bindings * Add power domains for MSM8998 and QCS404 in QCOM RPMPD * Add corner macros, max state support, and fixups for setting performance state for Qcom RPMPD * tag 'qcom-drivers-for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: soc: qcom: geni: Add support for ACPI MAINTAINERS: Remove myself as qcom maintainer soc: qcom: apr: Don't use reg for domain id soc: qcom: fix QCOM_AOSS_QMP dependency and build errors soc: qcom: Add AOSS QMP driver dt-bindings: soc: qcom: Add AOSS QMP binding qcom: apr: Make apr callbacks in non-atomic context soc: qcom: rpmpd: Add MSM8998 power-domains dt-bindings: power: Add rpm power domain bindings for msm8998 soc: qcom: rpmpd: Add QCS404 power-domains dt-bindings: power: Add rpm power domain bindings for qcs404 soc: qcom: rpmpd: Modify corner defining macros soc: qcom: rpmpd: Add support to set rpmpd state to max soc: qcom: rpmpd: fixup rpmpd set performance state MAINTAINER: Fix Qualcomm ETHQOS ethernet DT file MAINTAINERS: fix typo in file name Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Коммит
c616ea191d
|
@ -6,6 +6,8 @@ which then translates it into a corresponding voltage on a rail
|
|||
Required Properties:
|
||||
- compatible: Should be one of the following
|
||||
* qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC
|
||||
* qcom,msm8998-rpmpd: RPM Power domain for the msm8998 family of SoC
|
||||
* qcom,qcs404-rpmpd: RPM Power domain for the qcs404 family of SoC
|
||||
* qcom,sdm845-rpmhpd: RPMh Power domain for the sdm845 family of SoC
|
||||
- #power-domain-cells: number of cells in Power domain specifier
|
||||
must be 1.
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
Qualcomm Always-On Subsystem side channel binding
|
||||
|
||||
This binding describes the hardware component responsible for side channel
|
||||
requests to the always-on subsystem (AOSS), used for certain power management
|
||||
requests that is not handled by the standard RPMh interface. Each client in the
|
||||
SoC has it's own block of message RAM and IRQ for communication with the AOSS.
|
||||
The protocol used to communicate in the message RAM is known as Qualcomm
|
||||
Messaging Protocol (QMP)
|
||||
|
||||
The AOSS side channel exposes control over a set of resources, used to control
|
||||
a set of debug related clocks and to affect the low power state of resources
|
||||
related to the secondary subsystems. These resources are exposed as a set of
|
||||
power-domains.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be "qcom,sdm845-aoss-qmp"
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: the base address and size of the message RAM for this
|
||||
client's communication with the AOSS
|
||||
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: should specify the AOSS message IRQ for this client
|
||||
|
||||
- mboxes:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: reference to the mailbox representing the outgoing doorbell
|
||||
in APCS for this client, as described in mailbox/mailbox.txt
|
||||
|
||||
- #clock-cells:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: must be 0
|
||||
The single clock represents the QDSS clock.
|
||||
|
||||
- #power-domain-cells:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: must be 1
|
||||
The provided power-domains are:
|
||||
CDSP state (0), LPASS state (1), modem state (2), SLPI
|
||||
state (3), SPSS state (4) and Venus state (5).
|
||||
|
||||
= SUBNODES
|
||||
The AOSS side channel also provides the controls for three cooling devices,
|
||||
these are expressed as subnodes of the QMP node. The name of the node is used
|
||||
to identify the resource and must therefor be "cx", "mx" or "ebi".
|
||||
|
||||
- #cooling-cells:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: must be 2
|
||||
|
||||
= EXAMPLE
|
||||
|
||||
The following example represents the AOSS side-channel message RAM and the
|
||||
mechanism exposing the power-domains, as found in SDM845.
|
||||
|
||||
aoss_qmp: qmp@c300000 {
|
||||
compatible = "qcom,sdm845-aoss-qmp";
|
||||
reg = <0x0c300000 0x100000>;
|
||||
interrupts = <GIC_SPI 389 IRQ_TYPE_EDGE_RISING>;
|
||||
mboxes = <&apss_shared 0>;
|
||||
|
||||
#power-domain-cells = <1>;
|
||||
|
||||
cx_cdev: cx {
|
||||
#cooling-cells = <2>;
|
||||
};
|
||||
|
||||
mx_cdev: mx {
|
||||
#cooling-cells = <2>;
|
||||
};
|
||||
};
|
|
@ -9,7 +9,7 @@ used for audio/voice services on the QDSP.
|
|||
Value type: <stringlist>
|
||||
Definition: must be "qcom,apr-v<VERSION-NUMBER>", example "qcom,apr-v2"
|
||||
|
||||
- reg
|
||||
- qcom,apr-domain
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Destination processor ID.
|
||||
|
@ -49,9 +49,9 @@ by the individual bindings for the specific service
|
|||
The following example represents a QDSP based sound card on a MSM8996 device
|
||||
which uses apr as communication between Apps and QDSP.
|
||||
|
||||
apr@4 {
|
||||
apr {
|
||||
compatible = "qcom,apr-v2";
|
||||
reg = <APR_DOMAIN_ADSP>;
|
||||
qcom,apr-domain = <APR_DOMAIN_ADSP>;
|
||||
|
||||
q6core@3 {
|
||||
compatible = "qcom,q6core";
|
||||
|
|
|
@ -2050,7 +2050,6 @@ S: Maintained
|
|||
|
||||
ARM/QUALCOMM SUPPORT
|
||||
M: Andy Gross <agross@kernel.org>
|
||||
M: David Brown <david.brown@linaro.org>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/soc/qcom/
|
||||
|
@ -2072,7 +2071,7 @@ F: drivers/i2c/busses/i2c-qup.c
|
|||
F: drivers/i2c/busses/i2c-qcom-geni.c
|
||||
F: drivers/mfd/ssbi.c
|
||||
F: drivers/mmc/host/mmci_qcom*
|
||||
F: drivers/mmc/host/sdhci_msm.c
|
||||
F: drivers/mmc/host/sdhci-msm.c
|
||||
F: drivers/pci/controller/dwc/pcie-qcom.c
|
||||
F: drivers/phy/qualcomm/
|
||||
F: drivers/power/*/msm*
|
||||
|
@ -13056,7 +13055,7 @@ M: Niklas Cassel <niklas.cassel@linaro.org>
|
|||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
|
||||
F: Documentation/devicetree/bindings/net/qcom,dwmac.txt
|
||||
F: Documentation/devicetree/bindings/net/qcom,ethqos.txt
|
||||
|
||||
QUALCOMM GENERIC INTERFACE I2C DRIVER
|
||||
M: Alok Chauhan <alokc@codeaurora.org>
|
||||
|
|
|
@ -4,6 +4,18 @@
|
|||
#
|
||||
menu "Qualcomm SoC drivers"
|
||||
|
||||
config QCOM_AOSS_QMP
|
||||
tristate "Qualcomm AOSS Driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on MAILBOX
|
||||
depends on COMMON_CLK && PM
|
||||
select PM_GENERIC_DOMAINS
|
||||
help
|
||||
This driver provides the means of communicating with and controlling
|
||||
the low-power state for resources related to the remoteproc
|
||||
subsystems as well as controlling the debug clocks exposed by the Always On
|
||||
Subsystem (AOSS) using Qualcomm Messaging Protocol (QMP).
|
||||
|
||||
config QCOM_COMMAND_DB
|
||||
bool "Qualcomm Command DB"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
CFLAGS_rpmh-rsc.o := -I$(src)
|
||||
obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o
|
||||
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
|
||||
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
|
||||
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/soc/qcom/apr.h>
|
||||
#include <linux/rpmsg.h>
|
||||
|
@ -17,8 +18,18 @@ struct apr {
|
|||
struct rpmsg_endpoint *ch;
|
||||
struct device *dev;
|
||||
spinlock_t svcs_lock;
|
||||
spinlock_t rx_lock;
|
||||
struct idr svcs_idr;
|
||||
int dest_domain_id;
|
||||
struct workqueue_struct *rxwq;
|
||||
struct work_struct rx_work;
|
||||
struct list_head rx_list;
|
||||
};
|
||||
|
||||
struct apr_rx_buf {
|
||||
struct list_head node;
|
||||
int len;
|
||||
uint8_t buf[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -62,11 +73,7 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
|
|||
int len, void *priv, u32 addr)
|
||||
{
|
||||
struct apr *apr = dev_get_drvdata(&rpdev->dev);
|
||||
uint16_t hdr_size, msg_type, ver, svc_id;
|
||||
struct apr_device *svc = NULL;
|
||||
struct apr_driver *adrv = NULL;
|
||||
struct apr_resp_pkt resp;
|
||||
struct apr_hdr *hdr;
|
||||
struct apr_rx_buf *abuf;
|
||||
unsigned long flags;
|
||||
|
||||
if (len <= APR_HDR_SIZE) {
|
||||
|
@ -75,6 +82,34 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
abuf = kzalloc(sizeof(*abuf) + len, GFP_ATOMIC);
|
||||
if (!abuf)
|
||||
return -ENOMEM;
|
||||
|
||||
abuf->len = len;
|
||||
memcpy(abuf->buf, buf, len);
|
||||
|
||||
spin_lock_irqsave(&apr->rx_lock, flags);
|
||||
list_add_tail(&abuf->node, &apr->rx_list);
|
||||
spin_unlock_irqrestore(&apr->rx_lock, flags);
|
||||
|
||||
queue_work(apr->rxwq, &apr->rx_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
|
||||
{
|
||||
uint16_t hdr_size, msg_type, ver, svc_id;
|
||||
struct apr_device *svc = NULL;
|
||||
struct apr_driver *adrv = NULL;
|
||||
struct apr_resp_pkt resp;
|
||||
struct apr_hdr *hdr;
|
||||
unsigned long flags;
|
||||
void *buf = abuf->buf;
|
||||
int len = abuf->len;
|
||||
|
||||
hdr = buf;
|
||||
ver = APR_HDR_FIELD_VER(hdr->hdr_field);
|
||||
if (ver > APR_PKT_VER + 1)
|
||||
|
@ -132,6 +167,23 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void apr_rxwq(struct work_struct *work)
|
||||
{
|
||||
struct apr *apr = container_of(work, struct apr, rx_work);
|
||||
struct apr_rx_buf *abuf, *b;
|
||||
unsigned long flags;
|
||||
|
||||
if (!list_empty(&apr->rx_list)) {
|
||||
list_for_each_entry_safe(abuf, b, &apr->rx_list, node) {
|
||||
apr_do_rx_callback(apr, abuf);
|
||||
spin_lock_irqsave(&apr->rx_lock, flags);
|
||||
list_del(&abuf->node);
|
||||
spin_unlock_irqrestore(&apr->rx_lock, flags);
|
||||
kfree(abuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int apr_device_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct apr_device *adev = to_apr_device(dev);
|
||||
|
@ -276,7 +328,7 @@ static int apr_probe(struct rpmsg_device *rpdev)
|
|||
if (!apr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "reg", &apr->dest_domain_id);
|
||||
ret = of_property_read_u32(dev->of_node, "qcom,apr-domain", &apr->dest_domain_id);
|
||||
if (ret) {
|
||||
dev_err(dev, "APR Domain ID not specified in DT\n");
|
||||
return ret;
|
||||
|
@ -285,6 +337,14 @@ static int apr_probe(struct rpmsg_device *rpdev)
|
|||
dev_set_drvdata(dev, apr);
|
||||
apr->ch = rpdev->ept;
|
||||
apr->dev = dev;
|
||||
apr->rxwq = create_singlethread_workqueue("qcom_apr_rx");
|
||||
if (!apr->rxwq) {
|
||||
dev_err(apr->dev, "Failed to start Rx WQ\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_WORK(&apr->rx_work, apr_rxwq);
|
||||
INIT_LIST_HEAD(&apr->rx_list);
|
||||
spin_lock_init(&apr->rx_lock);
|
||||
spin_lock_init(&apr->svcs_lock);
|
||||
idr_init(&apr->svcs_idr);
|
||||
of_register_apr_devices(dev);
|
||||
|
@ -303,7 +363,11 @@ static int apr_remove_device(struct device *dev, void *null)
|
|||
|
||||
static void apr_remove(struct rpmsg_device *rpdev)
|
||||
{
|
||||
struct apr *apr = dev_get_drvdata(&rpdev->dev);
|
||||
|
||||
device_for_each_child(&rpdev->dev, NULL, apr_remove_device);
|
||||
flush_workqueue(apr->rxwq);
|
||||
destroy_workqueue(apr->rxwq);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
@ -450,6 +451,9 @@ int geni_se_resources_off(struct geni_se *se)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (has_acpi_companion(se->dev))
|
||||
return 0;
|
||||
|
||||
ret = pinctrl_pm_select_sleep_state(se->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -487,6 +491,9 @@ int geni_se_resources_on(struct geni_se *se)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (has_acpi_companion(se->dev))
|
||||
return 0;
|
||||
|
||||
ret = geni_se_clks_on(se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -724,12 +731,14 @@ static int geni_se_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(wrapper->base))
|
||||
return PTR_ERR(wrapper->base);
|
||||
|
||||
wrapper->ahb_clks[0].id = "m-ahb";
|
||||
wrapper->ahb_clks[1].id = "s-ahb";
|
||||
ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
|
||||
if (ret) {
|
||||
dev_err(dev, "Err getting AHB clks %d\n", ret);
|
||||
return ret;
|
||||
if (!has_acpi_companion(&pdev->dev)) {
|
||||
wrapper->ahb_clks[0].id = "m-ahb";
|
||||
wrapper->ahb_clks[1].id = "s-ahb";
|
||||
ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks);
|
||||
if (ret) {
|
||||
dev_err(dev, "Err getting AHB clks %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, wrapper);
|
||||
|
|
|
@ -0,0 +1,480 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2019, Linaro Ltd
|
||||
*/
|
||||
#include <dt-bindings/power/qcom-aoss-qmp.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
|
||||
#define QMP_DESC_MAGIC 0x0
|
||||
#define QMP_DESC_VERSION 0x4
|
||||
#define QMP_DESC_FEATURES 0x8
|
||||
|
||||
/* AOP-side offsets */
|
||||
#define QMP_DESC_UCORE_LINK_STATE 0xc
|
||||
#define QMP_DESC_UCORE_LINK_STATE_ACK 0x10
|
||||
#define QMP_DESC_UCORE_CH_STATE 0x14
|
||||
#define QMP_DESC_UCORE_CH_STATE_ACK 0x18
|
||||
#define QMP_DESC_UCORE_MBOX_SIZE 0x1c
|
||||
#define QMP_DESC_UCORE_MBOX_OFFSET 0x20
|
||||
|
||||
/* Linux-side offsets */
|
||||
#define QMP_DESC_MCORE_LINK_STATE 0x24
|
||||
#define QMP_DESC_MCORE_LINK_STATE_ACK 0x28
|
||||
#define QMP_DESC_MCORE_CH_STATE 0x2c
|
||||
#define QMP_DESC_MCORE_CH_STATE_ACK 0x30
|
||||
#define QMP_DESC_MCORE_MBOX_SIZE 0x34
|
||||
#define QMP_DESC_MCORE_MBOX_OFFSET 0x38
|
||||
|
||||
#define QMP_STATE_UP GENMASK(15, 0)
|
||||
#define QMP_STATE_DOWN GENMASK(31, 16)
|
||||
|
||||
#define QMP_MAGIC 0x4d41494c /* mail */
|
||||
#define QMP_VERSION 1
|
||||
|
||||
/* 64 bytes is enough to store the requests and provides padding to 4 bytes */
|
||||
#define QMP_MSG_LEN 64
|
||||
|
||||
/**
|
||||
* struct qmp - driver state for QMP implementation
|
||||
* @msgram: iomem referencing the message RAM used for communication
|
||||
* @dev: reference to QMP device
|
||||
* @mbox_client: mailbox client used to ring the doorbell on transmit
|
||||
* @mbox_chan: mailbox channel used to ring the doorbell on transmit
|
||||
* @offset: offset within @msgram where messages should be written
|
||||
* @size: maximum size of the messages to be transmitted
|
||||
* @event: wait_queue for synchronization with the IRQ
|
||||
* @tx_lock: provides synchronization between multiple callers of qmp_send()
|
||||
* @qdss_clk: QDSS clock hw struct
|
||||
* @pd_data: genpd data
|
||||
*/
|
||||
struct qmp {
|
||||
void __iomem *msgram;
|
||||
struct device *dev;
|
||||
|
||||
struct mbox_client mbox_client;
|
||||
struct mbox_chan *mbox_chan;
|
||||
|
||||
size_t offset;
|
||||
size_t size;
|
||||
|
||||
wait_queue_head_t event;
|
||||
|
||||
struct mutex tx_lock;
|
||||
|
||||
struct clk_hw qdss_clk;
|
||||
struct genpd_onecell_data pd_data;
|
||||
};
|
||||
|
||||
struct qmp_pd {
|
||||
struct qmp *qmp;
|
||||
struct generic_pm_domain pd;
|
||||
};
|
||||
|
||||
#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
|
||||
|
||||
static void qmp_kick(struct qmp *qmp)
|
||||
{
|
||||
mbox_send_message(qmp->mbox_chan, NULL);
|
||||
mbox_client_txdone(qmp->mbox_chan, 0);
|
||||
}
|
||||
|
||||
static bool qmp_magic_valid(struct qmp *qmp)
|
||||
{
|
||||
return readl(qmp->msgram + QMP_DESC_MAGIC) == QMP_MAGIC;
|
||||
}
|
||||
|
||||
static bool qmp_link_acked(struct qmp *qmp)
|
||||
{
|
||||
return readl(qmp->msgram + QMP_DESC_MCORE_LINK_STATE_ACK) == QMP_STATE_UP;
|
||||
}
|
||||
|
||||
static bool qmp_mcore_channel_acked(struct qmp *qmp)
|
||||
{
|
||||
return readl(qmp->msgram + QMP_DESC_MCORE_CH_STATE_ACK) == QMP_STATE_UP;
|
||||
}
|
||||
|
||||
static bool qmp_ucore_channel_up(struct qmp *qmp)
|
||||
{
|
||||
return readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE) == QMP_STATE_UP;
|
||||
}
|
||||
|
||||
static int qmp_open(struct qmp *qmp)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (!qmp_magic_valid(qmp)) {
|
||||
dev_err(qmp->dev, "QMP magic doesn't match\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = readl(qmp->msgram + QMP_DESC_VERSION);
|
||||
if (val != QMP_VERSION) {
|
||||
dev_err(qmp->dev, "unsupported QMP version %d\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qmp->offset = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_OFFSET);
|
||||
qmp->size = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_SIZE);
|
||||
if (!qmp->size) {
|
||||
dev_err(qmp->dev, "invalid mailbox size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Ack remote core's link state */
|
||||
val = readl(qmp->msgram + QMP_DESC_UCORE_LINK_STATE);
|
||||
writel(val, qmp->msgram + QMP_DESC_UCORE_LINK_STATE_ACK);
|
||||
|
||||
/* Set local core's link state to up */
|
||||
writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
|
||||
|
||||
qmp_kick(qmp);
|
||||
|
||||
ret = wait_event_timeout(qmp->event, qmp_link_acked(qmp), HZ);
|
||||
if (!ret) {
|
||||
dev_err(qmp->dev, "ucore didn't ack link\n");
|
||||
goto timeout_close_link;
|
||||
}
|
||||
|
||||
writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
|
||||
|
||||
qmp_kick(qmp);
|
||||
|
||||
ret = wait_event_timeout(qmp->event, qmp_ucore_channel_up(qmp), HZ);
|
||||
if (!ret) {
|
||||
dev_err(qmp->dev, "ucore didn't open channel\n");
|
||||
goto timeout_close_channel;
|
||||
}
|
||||
|
||||
/* Ack remote core's channel state */
|
||||
writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_UCORE_CH_STATE_ACK);
|
||||
|
||||
qmp_kick(qmp);
|
||||
|
||||
ret = wait_event_timeout(qmp->event, qmp_mcore_channel_acked(qmp), HZ);
|
||||
if (!ret) {
|
||||
dev_err(qmp->dev, "ucore didn't ack channel\n");
|
||||
goto timeout_close_channel;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
timeout_close_channel:
|
||||
writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
|
||||
|
||||
timeout_close_link:
|
||||
writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
|
||||
qmp_kick(qmp);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void qmp_close(struct qmp *qmp)
|
||||
{
|
||||
writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
|
||||
writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
|
||||
qmp_kick(qmp);
|
||||
}
|
||||
|
||||
static irqreturn_t qmp_intr(int irq, void *data)
|
||||
{
|
||||
struct qmp *qmp = data;
|
||||
|
||||
wake_up_interruptible_all(&qmp->event);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static bool qmp_message_empty(struct qmp *qmp)
|
||||
{
|
||||
return readl(qmp->msgram + qmp->offset) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qmp_send() - send a message to the AOSS
|
||||
* @qmp: qmp context
|
||||
* @data: message to be sent
|
||||
* @len: length of the message
|
||||
*
|
||||
* Transmit @data to AOSS and wait for the AOSS to acknowledge the message.
|
||||
* @len must be a multiple of 4 and not longer than the mailbox size. Access is
|
||||
* synchronized by this implementation.
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure
|
||||
*/
|
||||
static int qmp_send(struct qmp *qmp, const void *data, size_t len)
|
||||
{
|
||||
long time_left;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(len + sizeof(u32) > qmp->size))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(len % sizeof(u32)))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&qmp->tx_lock);
|
||||
|
||||
/* The message RAM only implements 32-bit accesses */
|
||||
__iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32),
|
||||
data, len / sizeof(u32));
|
||||
writel(len, qmp->msgram + qmp->offset);
|
||||
qmp_kick(qmp);
|
||||
|
||||
time_left = wait_event_interruptible_timeout(qmp->event,
|
||||
qmp_message_empty(qmp), HZ);
|
||||
if (!time_left) {
|
||||
dev_err(qmp->dev, "ucore did not ack channel\n");
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
/* Clear message from buffer */
|
||||
writel(0, qmp->msgram + qmp->offset);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&qmp->tx_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmp_qdss_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
static const char buf[QMP_MSG_LEN] = "{class: clock, res: qdss, val: 1}";
|
||||
struct qmp *qmp = container_of(hw, struct qmp, qdss_clk);
|
||||
|
||||
return qmp_send(qmp, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static void qmp_qdss_clk_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
static const char buf[QMP_MSG_LEN] = "{class: clock, res: qdss, val: 0}";
|
||||
struct qmp *qmp = container_of(hw, struct qmp, qdss_clk);
|
||||
|
||||
qmp_send(qmp, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static const struct clk_ops qmp_qdss_clk_ops = {
|
||||
.prepare = qmp_qdss_clk_prepare,
|
||||
.unprepare = qmp_qdss_clk_unprepare,
|
||||
};
|
||||
|
||||
static int qmp_qdss_clk_add(struct qmp *qmp)
|
||||
{
|
||||
static const struct clk_init_data qdss_init = {
|
||||
.ops = &qmp_qdss_clk_ops,
|
||||
.name = "qdss",
|
||||
};
|
||||
int ret;
|
||||
|
||||
qmp->qdss_clk.init = &qdss_init;
|
||||
ret = clk_hw_register(qmp->dev, &qmp->qdss_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(qmp->dev, "failed to register qdss clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_clk_add_hw_provider(qmp->dev->of_node, of_clk_hw_simple_get,
|
||||
&qmp->qdss_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(qmp->dev, "unable to register of clk hw provider\n");
|
||||
clk_hw_unregister(&qmp->qdss_clk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qmp_qdss_clk_remove(struct qmp *qmp)
|
||||
{
|
||||
of_clk_del_provider(qmp->dev->of_node);
|
||||
clk_hw_unregister(&qmp->qdss_clk);
|
||||
}
|
||||
|
||||
static int qmp_pd_power_toggle(struct qmp_pd *res, bool enable)
|
||||
{
|
||||
char buf[QMP_MSG_LEN] = {};
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{class: image, res: load_state, name: %s, val: %s}",
|
||||
res->pd.name, enable ? "on" : "off");
|
||||
return qmp_send(res->qmp, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int qmp_pd_power_on(struct generic_pm_domain *domain)
|
||||
{
|
||||
return qmp_pd_power_toggle(to_qmp_pd_resource(domain), true);
|
||||
}
|
||||
|
||||
static int qmp_pd_power_off(struct generic_pm_domain *domain)
|
||||
{
|
||||
return qmp_pd_power_toggle(to_qmp_pd_resource(domain), false);
|
||||
}
|
||||
|
||||
static const char * const sdm845_resources[] = {
|
||||
[AOSS_QMP_LS_CDSP] = "cdsp",
|
||||
[AOSS_QMP_LS_LPASS] = "adsp",
|
||||
[AOSS_QMP_LS_MODEM] = "modem",
|
||||
[AOSS_QMP_LS_SLPI] = "slpi",
|
||||
[AOSS_QMP_LS_SPSS] = "spss",
|
||||
[AOSS_QMP_LS_VENUS] = "venus",
|
||||
};
|
||||
|
||||
static int qmp_pd_add(struct qmp *qmp)
|
||||
{
|
||||
struct genpd_onecell_data *data = &qmp->pd_data;
|
||||
struct device *dev = qmp->dev;
|
||||
struct qmp_pd *res;
|
||||
size_t num = ARRAY_SIZE(sdm845_resources);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
res = devm_kcalloc(dev, num, sizeof(*res), GFP_KERNEL);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
|
||||
data->domains = devm_kcalloc(dev, num, sizeof(*data->domains),
|
||||
GFP_KERNEL);
|
||||
if (!data->domains)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
res[i].qmp = qmp;
|
||||
res[i].pd.name = sdm845_resources[i];
|
||||
res[i].pd.power_on = qmp_pd_power_on;
|
||||
res[i].pd.power_off = qmp_pd_power_off;
|
||||
|
||||
ret = pm_genpd_init(&res[i].pd, NULL, true);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to init genpd\n");
|
||||
goto unroll_genpds;
|
||||
}
|
||||
|
||||
data->domains[i] = &res[i].pd;
|
||||
}
|
||||
|
||||
data->num_domains = i;
|
||||
|
||||
ret = of_genpd_add_provider_onecell(dev->of_node, data);
|
||||
if (ret < 0)
|
||||
goto unroll_genpds;
|
||||
|
||||
return 0;
|
||||
|
||||
unroll_genpds:
|
||||
for (i--; i >= 0; i--)
|
||||
pm_genpd_remove(data->domains[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qmp_pd_remove(struct qmp *qmp)
|
||||
{
|
||||
struct genpd_onecell_data *data = &qmp->pd_data;
|
||||
struct device *dev = qmp->dev;
|
||||
int i;
|
||||
|
||||
of_genpd_del_provider(dev->of_node);
|
||||
|
||||
for (i = 0; i < data->num_domains; i++)
|
||||
pm_genpd_remove(data->domains[i]);
|
||||
}
|
||||
|
||||
static int qmp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct qmp *qmp;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL);
|
||||
if (!qmp)
|
||||
return -ENOMEM;
|
||||
|
||||
qmp->dev = &pdev->dev;
|
||||
init_waitqueue_head(&qmp->event);
|
||||
mutex_init(&qmp->tx_lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(qmp->msgram))
|
||||
return PTR_ERR(qmp->msgram);
|
||||
|
||||
qmp->mbox_client.dev = &pdev->dev;
|
||||
qmp->mbox_client.knows_txdone = true;
|
||||
qmp->mbox_chan = mbox_request_channel(&qmp->mbox_client, 0);
|
||||
if (IS_ERR(qmp->mbox_chan)) {
|
||||
dev_err(&pdev->dev, "failed to acquire ipc mailbox\n");
|
||||
return PTR_ERR(qmp->mbox_chan);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(&pdev->dev, irq, qmp_intr, IRQF_ONESHOT,
|
||||
"aoss-qmp", qmp);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request interrupt\n");
|
||||
goto err_free_mbox;
|
||||
}
|
||||
|
||||
ret = qmp_open(qmp);
|
||||
if (ret < 0)
|
||||
goto err_free_mbox;
|
||||
|
||||
ret = qmp_qdss_clk_add(qmp);
|
||||
if (ret)
|
||||
goto err_close_qmp;
|
||||
|
||||
ret = qmp_pd_add(qmp);
|
||||
if (ret)
|
||||
goto err_remove_qdss_clk;
|
||||
|
||||
platform_set_drvdata(pdev, qmp);
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_qdss_clk:
|
||||
qmp_qdss_clk_remove(qmp);
|
||||
err_close_qmp:
|
||||
qmp_close(qmp);
|
||||
err_free_mbox:
|
||||
mbox_free_channel(qmp->mbox_chan);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qmp *qmp = platform_get_drvdata(pdev);
|
||||
|
||||
qmp_qdss_clk_remove(qmp);
|
||||
qmp_pd_remove(qmp);
|
||||
|
||||
qmp_close(qmp);
|
||||
mbox_free_channel(qmp->mbox_chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qmp_dt_match[] = {
|
||||
{ .compatible = "qcom,sdm845-aoss-qmp", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qmp_dt_match);
|
||||
|
||||
static struct platform_driver qmp_driver = {
|
||||
.driver = {
|
||||
.name = "qcom_aoss_qmp",
|
||||
.of_match_table = qmp_dt_match,
|
||||
},
|
||||
.probe = qmp_probe,
|
||||
.remove = qmp_remove,
|
||||
};
|
||||
module_platform_driver(qmp_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm AOSS QMP driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -16,56 +16,76 @@
|
|||
|
||||
#define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd)
|
||||
|
||||
/* Resource types */
|
||||
/* Resource types:
|
||||
* RPMPD_X is X encoded as a little-endian, lower-case, ASCII string */
|
||||
#define RPMPD_SMPA 0x61706d73
|
||||
#define RPMPD_LDOA 0x616f646c
|
||||
#define RPMPD_RWCX 0x78637772
|
||||
#define RPMPD_RWMX 0x786d7772
|
||||
#define RPMPD_RWLC 0x636c7772
|
||||
#define RPMPD_RWLM 0x6d6c7772
|
||||
#define RPMPD_RWSC 0x63737772
|
||||
#define RPMPD_RWSM 0x6d737772
|
||||
|
||||
/* Operation Keys */
|
||||
#define KEY_CORNER 0x6e726f63 /* corn */
|
||||
#define KEY_ENABLE 0x6e657773 /* swen */
|
||||
#define KEY_FLOOR_CORNER 0x636676 /* vfc */
|
||||
#define KEY_FLOOR_LEVEL 0x6c6676 /* vfl */
|
||||
#define KEY_LEVEL 0x6c766c76 /* vlvl */
|
||||
|
||||
#define MAX_RPMPD_STATE 6
|
||||
#define MAX_8996_RPMPD_STATE 6
|
||||
|
||||
#define DEFINE_RPMPD_CORNER_SMPA(_platform, _name, _active, r_id) \
|
||||
#define DEFINE_RPMPD_PAIR(_platform, _name, _active, r_type, r_key, \
|
||||
r_id) \
|
||||
static struct rpmpd _platform##_##_active; \
|
||||
static struct rpmpd _platform##_##_name = { \
|
||||
.pd = { .name = #_name, }, \
|
||||
.peer = &_platform##_##_active, \
|
||||
.res_type = RPMPD_SMPA, \
|
||||
.res_type = RPMPD_##r_type, \
|
||||
.res_id = r_id, \
|
||||
.key = KEY_CORNER, \
|
||||
.key = KEY_##r_key, \
|
||||
}; \
|
||||
static struct rpmpd _platform##_##_active = { \
|
||||
.pd = { .name = #_active, }, \
|
||||
.peer = &_platform##_##_name, \
|
||||
.active_only = true, \
|
||||
.res_type = RPMPD_SMPA, \
|
||||
.res_type = RPMPD_##r_type, \
|
||||
.res_id = r_id, \
|
||||
.key = KEY_##r_key, \
|
||||
}
|
||||
|
||||
#define DEFINE_RPMPD_CORNER(_platform, _name, r_type, r_id) \
|
||||
static struct rpmpd _platform##_##_name = { \
|
||||
.pd = { .name = #_name, }, \
|
||||
.res_type = RPMPD_##r_type, \
|
||||
.res_id = r_id, \
|
||||
.key = KEY_CORNER, \
|
||||
}
|
||||
|
||||
#define DEFINE_RPMPD_CORNER_LDOA(_platform, _name, r_id) \
|
||||
#define DEFINE_RPMPD_LEVEL(_platform, _name, r_type, r_id) \
|
||||
static struct rpmpd _platform##_##_name = { \
|
||||
.pd = { .name = #_name, }, \
|
||||
.res_type = RPMPD_LDOA, \
|
||||
.res_type = RPMPD_##r_type, \
|
||||
.res_id = r_id, \
|
||||
.key = KEY_CORNER, \
|
||||
.key = KEY_LEVEL, \
|
||||
}
|
||||
|
||||
#define DEFINE_RPMPD_VFC(_platform, _name, r_id, r_type) \
|
||||
#define DEFINE_RPMPD_VFC(_platform, _name, r_type, r_id) \
|
||||
static struct rpmpd _platform##_##_name = { \
|
||||
.pd = { .name = #_name, }, \
|
||||
.res_type = r_type, \
|
||||
.res_type = RPMPD_##r_type, \
|
||||
.res_id = r_id, \
|
||||
.key = KEY_FLOOR_CORNER, \
|
||||
}
|
||||
|
||||
#define DEFINE_RPMPD_VFC_SMPA(_platform, _name, r_id) \
|
||||
DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_SMPA)
|
||||
|
||||
#define DEFINE_RPMPD_VFC_LDOA(_platform, _name, r_id) \
|
||||
DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_LDOA)
|
||||
#define DEFINE_RPMPD_VFL(_platform, _name, r_type, r_id) \
|
||||
static struct rpmpd _platform##_##_name = { \
|
||||
.pd = { .name = #_name, }, \
|
||||
.res_type = RPMPD_##r_type, \
|
||||
.res_id = r_id, \
|
||||
.key = KEY_FLOOR_LEVEL, \
|
||||
}
|
||||
|
||||
struct rpmpd_req {
|
||||
__le32 key;
|
||||
|
@ -83,23 +103,25 @@ struct rpmpd {
|
|||
const int res_type;
|
||||
const int res_id;
|
||||
struct qcom_smd_rpm *rpm;
|
||||
unsigned int max_state;
|
||||
__le32 key;
|
||||
};
|
||||
|
||||
struct rpmpd_desc {
|
||||
struct rpmpd **rpmpds;
|
||||
size_t num_pds;
|
||||
unsigned int max_state;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(rpmpd_lock);
|
||||
|
||||
/* msm8996 RPM Power domains */
|
||||
DEFINE_RPMPD_CORNER_SMPA(msm8996, vddcx, vddcx_ao, 1);
|
||||
DEFINE_RPMPD_CORNER_SMPA(msm8996, vddmx, vddmx_ao, 2);
|
||||
DEFINE_RPMPD_CORNER_LDOA(msm8996, vddsscx, 26);
|
||||
DEFINE_RPMPD_PAIR(msm8996, vddcx, vddcx_ao, SMPA, CORNER, 1);
|
||||
DEFINE_RPMPD_PAIR(msm8996, vddmx, vddmx_ao, SMPA, CORNER, 2);
|
||||
DEFINE_RPMPD_CORNER(msm8996, vddsscx, LDOA, 26);
|
||||
|
||||
DEFINE_RPMPD_VFC_SMPA(msm8996, vddcx_vfc, 1);
|
||||
DEFINE_RPMPD_VFC_LDOA(msm8996, vddsscx_vfc, 26);
|
||||
DEFINE_RPMPD_VFC(msm8996, vddcx_vfc, SMPA, 1);
|
||||
DEFINE_RPMPD_VFC(msm8996, vddsscx_vfc, LDOA, 26);
|
||||
|
||||
static struct rpmpd *msm8996_rpmpds[] = {
|
||||
[MSM8996_VDDCX] = &msm8996_vddcx,
|
||||
|
@ -114,10 +136,71 @@ static struct rpmpd *msm8996_rpmpds[] = {
|
|||
static const struct rpmpd_desc msm8996_desc = {
|
||||
.rpmpds = msm8996_rpmpds,
|
||||
.num_pds = ARRAY_SIZE(msm8996_rpmpds),
|
||||
.max_state = MAX_8996_RPMPD_STATE,
|
||||
};
|
||||
|
||||
/* msm8998 RPM Power domains */
|
||||
DEFINE_RPMPD_PAIR(msm8998, vddcx, vddcx_ao, RWCX, LEVEL, 0);
|
||||
DEFINE_RPMPD_VFL(msm8998, vddcx_vfl, RWCX, 0);
|
||||
|
||||
DEFINE_RPMPD_PAIR(msm8998, vddmx, vddmx_ao, RWMX, LEVEL, 0);
|
||||
DEFINE_RPMPD_VFL(msm8998, vddmx_vfl, RWMX, 0);
|
||||
|
||||
DEFINE_RPMPD_LEVEL(msm8998, vdd_ssccx, RWSC, 0);
|
||||
DEFINE_RPMPD_VFL(msm8998, vdd_ssccx_vfl, RWSC, 0);
|
||||
|
||||
DEFINE_RPMPD_LEVEL(msm8998, vdd_sscmx, RWSM, 0);
|
||||
DEFINE_RPMPD_VFL(msm8998, vdd_sscmx_vfl, RWSM, 0);
|
||||
|
||||
static struct rpmpd *msm8998_rpmpds[] = {
|
||||
[MSM8998_VDDCX] = &msm8998_vddcx,
|
||||
[MSM8998_VDDCX_AO] = &msm8998_vddcx_ao,
|
||||
[MSM8998_VDDCX_VFL] = &msm8998_vddcx_vfl,
|
||||
[MSM8998_VDDMX] = &msm8998_vddmx,
|
||||
[MSM8998_VDDMX_AO] = &msm8998_vddmx_ao,
|
||||
[MSM8998_VDDMX_VFL] = &msm8998_vddmx_vfl,
|
||||
[MSM8998_SSCCX] = &msm8998_vdd_ssccx,
|
||||
[MSM8998_SSCCX_VFL] = &msm8998_vdd_ssccx_vfl,
|
||||
[MSM8998_SSCMX] = &msm8998_vdd_sscmx,
|
||||
[MSM8998_SSCMX_VFL] = &msm8998_vdd_sscmx_vfl,
|
||||
};
|
||||
|
||||
static const struct rpmpd_desc msm8998_desc = {
|
||||
.rpmpds = msm8998_rpmpds,
|
||||
.num_pds = ARRAY_SIZE(msm8998_rpmpds),
|
||||
.max_state = RPM_SMD_LEVEL_BINNING,
|
||||
};
|
||||
|
||||
/* qcs404 RPM Power domains */
|
||||
DEFINE_RPMPD_PAIR(qcs404, vddmx, vddmx_ao, RWMX, LEVEL, 0);
|
||||
DEFINE_RPMPD_VFL(qcs404, vddmx_vfl, RWMX, 0);
|
||||
|
||||
DEFINE_RPMPD_LEVEL(qcs404, vdd_lpicx, RWLC, 0);
|
||||
DEFINE_RPMPD_VFL(qcs404, vdd_lpicx_vfl, RWLC, 0);
|
||||
|
||||
DEFINE_RPMPD_LEVEL(qcs404, vdd_lpimx, RWLM, 0);
|
||||
DEFINE_RPMPD_VFL(qcs404, vdd_lpimx_vfl, RWLM, 0);
|
||||
|
||||
static struct rpmpd *qcs404_rpmpds[] = {
|
||||
[QCS404_VDDMX] = &qcs404_vddmx,
|
||||
[QCS404_VDDMX_AO] = &qcs404_vddmx_ao,
|
||||
[QCS404_VDDMX_VFL] = &qcs404_vddmx_vfl,
|
||||
[QCS404_LPICX] = &qcs404_vdd_lpicx,
|
||||
[QCS404_LPICX_VFL] = &qcs404_vdd_lpicx_vfl,
|
||||
[QCS404_LPIMX] = &qcs404_vdd_lpimx,
|
||||
[QCS404_LPIMX_VFL] = &qcs404_vdd_lpimx_vfl,
|
||||
};
|
||||
|
||||
static const struct rpmpd_desc qcs404_desc = {
|
||||
.rpmpds = qcs404_rpmpds,
|
||||
.num_pds = ARRAY_SIZE(qcs404_rpmpds),
|
||||
.max_state = RPM_SMD_LEVEL_BINNING,
|
||||
};
|
||||
|
||||
static const struct of_device_id rpmpd_match_table[] = {
|
||||
{ .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc },
|
||||
{ .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc },
|
||||
{ .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -225,14 +308,16 @@ static int rpmpd_set_performance(struct generic_pm_domain *domain,
|
|||
int ret = 0;
|
||||
struct rpmpd *pd = domain_to_rpmpd(domain);
|
||||
|
||||
if (state > MAX_RPMPD_STATE)
|
||||
goto out;
|
||||
if (state > pd->max_state)
|
||||
state = pd->max_state;
|
||||
|
||||
mutex_lock(&rpmpd_lock);
|
||||
|
||||
pd->corner = state;
|
||||
|
||||
if (!pd->enabled && pd->key != KEY_FLOOR_CORNER)
|
||||
/* Always send updates for vfc and vfl */
|
||||
if (!pd->enabled && pd->key != KEY_FLOOR_CORNER &&
|
||||
pd->key != KEY_FLOOR_LEVEL)
|
||||
goto out;
|
||||
|
||||
ret = rpmpd_aggregate_corner(pd);
|
||||
|
@ -287,6 +372,7 @@ static int rpmpd_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
rpmpds[i]->rpm = rpm;
|
||||
rpmpds[i]->max_state = desc->max_state;
|
||||
rpmpds[i]->pd.power_off = rpmpd_power_off;
|
||||
rpmpds[i]->pd.power_on = rpmpd_power_on;
|
||||
rpmpds[i]->pd.set_performance_state = rpmpd_set_performance;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2018, Linaro Ltd. */
|
||||
|
||||
#ifndef __DT_BINDINGS_POWER_QCOM_AOSS_QMP_H
|
||||
#define __DT_BINDINGS_POWER_QCOM_AOSS_QMP_H
|
||||
|
||||
#define AOSS_QMP_LS_CDSP 0
|
||||
#define AOSS_QMP_LS_LPASS 1
|
||||
#define AOSS_QMP_LS_MODEM 2
|
||||
#define AOSS_QMP_LS_SLPI 3
|
||||
#define AOSS_QMP_LS_SPSS 4
|
||||
#define AOSS_QMP_LS_VENUS 5
|
||||
|
||||
#endif
|
|
@ -36,4 +36,38 @@
|
|||
#define MSM8996_VDDSSCX 5
|
||||
#define MSM8996_VDDSSCX_VFC 6
|
||||
|
||||
/* MSM8998 Power Domain Indexes */
|
||||
#define MSM8998_VDDCX 0
|
||||
#define MSM8998_VDDCX_AO 1
|
||||
#define MSM8998_VDDCX_VFL 2
|
||||
#define MSM8998_VDDMX 3
|
||||
#define MSM8998_VDDMX_AO 4
|
||||
#define MSM8998_VDDMX_VFL 5
|
||||
#define MSM8998_SSCCX 6
|
||||
#define MSM8998_SSCCX_VFL 7
|
||||
#define MSM8998_SSCMX 8
|
||||
#define MSM8998_SSCMX_VFL 9
|
||||
|
||||
/* QCS404 Power Domains */
|
||||
#define QCS404_VDDMX 0
|
||||
#define QCS404_VDDMX_AO 1
|
||||
#define QCS404_VDDMX_VFL 2
|
||||
#define QCS404_LPICX 3
|
||||
#define QCS404_LPICX_VFL 4
|
||||
#define QCS404_LPIMX 5
|
||||
#define QCS404_LPIMX_VFL 6
|
||||
|
||||
/* RPM SMD Power Domain performance levels */
|
||||
#define RPM_SMD_LEVEL_RETENTION 16
|
||||
#define RPM_SMD_LEVEL_RETENTION_PLUS 32
|
||||
#define RPM_SMD_LEVEL_MIN_SVS 48
|
||||
#define RPM_SMD_LEVEL_LOW_SVS 64
|
||||
#define RPM_SMD_LEVEL_SVS 128
|
||||
#define RPM_SMD_LEVEL_SVS_PLUS 192
|
||||
#define RPM_SMD_LEVEL_NOM 256
|
||||
#define RPM_SMD_LEVEL_NOM_PLUS 320
|
||||
#define RPM_SMD_LEVEL_TURBO 384
|
||||
#define RPM_SMD_LEVEL_TURBO_NO_CPR 416
|
||||
#define RPM_SMD_LEVEL_BINNING 512
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче