diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-27 10:04:49 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-27 10:04:49 -0800 |
commit | 11c70529983e8136ea1bd5c32e4f9cd14503c644 (patch) | |
tree | 8e36f8397a68a4906fd49b72de8c7efb9b0e2a0d /drivers/soc/qcom | |
parent | d40b2f4c94f221bd5aab205f945e6f88d3df0929 (diff) | |
parent | 524af30c931382726b6a46ee4f392fb6e60f8a03 (diff) |
Merge tag 'soc-drivers-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC driver updates from Arnd Bergmann:
"As usual, there are lots of minor driver changes across SoC platforms
from NXP, Amlogic, AMD Zynq, Mediatek, Qualcomm, Apple and Samsung.
These usually add support for additional chip variations in existing
drivers, but also add features or bugfixes.
The SCMI firmware subsystem gains a unified raw userspace interface
through debugfs, which can be used for validation purposes.
Newly added drivers include:
- New power management drivers for StarFive JH7110, Allwinner D1 and
Renesas RZ/V2M
- A driver for Qualcomm battery and power supply status
- A SoC device driver for identifying Nuvoton WPCM450 chips
- A regulator coupler driver for Mediatek MT81xxv"
* tag 'soc-drivers-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (165 commits)
power: supply: Introduce Qualcomm PMIC GLINK power supply
soc: apple: rtkit: Do not copy the reg state structure to the stack
soc: sunxi: SUN20I_PPU should depend on PM
memory: renesas-rpc-if: Remove redundant division of dummy
soc: qcom: socinfo: Add IDs for IPQ5332 and its variant
dt-bindings: arm: qcom,ids: Add IDs for IPQ5332 and its variant
dt-bindings: power: qcom,rpmpd: add RPMH_REGULATOR_LEVEL_LOW_SVS_L1
firmware: qcom_scm: Move qcom_scm.h to include/linux/firmware/qcom/
MAINTAINERS: Update qcom CPR maintainer entry
dt-bindings: firmware: document Qualcomm SM8550 SCM
dt-bindings: firmware: qcom,scm: add qcom,scm-sa8775p compatible
soc: qcom: socinfo: Add Soc IDs for IPQ8064 and variants
dt-bindings: arm: qcom,ids: Add Soc IDs for IPQ8064 and variants
soc: qcom: socinfo: Add support for new field in revision 17
soc: qcom: smd-rpm: Add IPQ9574 compatible
soc: qcom: pmic_glink: remove redundant calculation of svid
soc: qcom: stats: Populate all subsystem debugfs files
dt-bindings: soc: qcom,rpmh-rsc: Update to allow for generic nodes
soc: qcom: pmic_glink: add CONFIG_NET/CONFIG_OF dependencies
soc: qcom: pmic_glink: Introduce altmode support
...
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r-- | drivers/soc/qcom/Kconfig | 27 | ||||
-rw-r--r-- | drivers/soc/qcom/Makefile | 3 | ||||
-rw-r--r-- | drivers/soc/qcom/mdt_loader.c | 2 | ||||
-rw-r--r-- | drivers/soc/qcom/ocmem.c | 2 | ||||
-rw-r--r-- | drivers/soc/qcom/pmic_glink.c | 336 | ||||
-rw-r--r-- | drivers/soc/qcom/pmic_glink_altmode.c | 478 | ||||
-rw-r--r-- | drivers/soc/qcom/qcom_stats.c | 10 | ||||
-rw-r--r-- | drivers/soc/qcom/ramp_controller.c | 343 | ||||
-rw-r--r-- | drivers/soc/qcom/rmtfs_mem.c | 31 | ||||
-rw-r--r-- | drivers/soc/qcom/rpmhpd.c | 34 | ||||
-rw-r--r-- | drivers/soc/qcom/rpmpd.c | 18 | ||||
-rw-r--r-- | drivers/soc/qcom/smd-rpm.c | 1 | ||||
-rw-r--r-- | drivers/soc/qcom/socinfo.c | 112 |
13 files changed, 1358 insertions, 39 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index ae504c43d9e7..a8f283086a21 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -91,11 +91,38 @@ config QCOM_OCMEM config QCOM_PDR_HELPERS tristate select QCOM_QMI_HELPERS + depends on NET + +config QCOM_PMIC_GLINK + tristate "Qualcomm PMIC GLINK driver" + depends on RPMSG + depends on TYPEC + depends on DRM + depends on NET + depends on OF + select AUXILIARY_BUS + select QCOM_PDR_HELPERS + help + The Qualcomm PMIC GLINK driver provides access, over GLINK, to the + USB and battery firmware running on one of the coprocessors in + several modern Qualcomm platforms. + + Say yes here to support USB-C and battery status on modern Qualcomm + platforms. config QCOM_QMI_HELPERS tristate depends on NET +config QCOM_RAMP_CTRL + tristate "Qualcomm Ramp Controller driver" + depends on ARCH_QCOM || COMPILE_TEST + help + The Ramp Controller is used to program the sequence ID for pulse + swallowing, enable sequence and link sequence IDs for the CPU + cores on some Qualcomm SoCs. + Say y here to enable support for the ramp controller. + config QCOM_RMTFS_MEM tristate "Qualcomm Remote Filesystem memory driver" depends on ARCH_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index d66604aff2b0..6e88da899f60 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -8,8 +8,11 @@ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o +obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink.o +obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink_altmode.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o +obj-$(CONFIG_QCOM_RAMP_CTRL) += ramp_controller.o obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o qcom_rpmh-y += rpmh-rsc.o diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 3f11554df2f3..33dd8c315eb7 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -12,7 +12,7 @@ #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/soc/qcom/mdt_loader.h> diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index c92d26b73e6f..199fe9872035 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/types.h> diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c new file mode 100644 index 000000000000..bb3fb57abcc6 --- /dev/null +++ b/drivers/soc/qcom/pmic_glink.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Linaro Ltd + */ +#include <linux/auxiliary_bus.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rpmsg.h> +#include <linux/slab.h> +#include <linux/soc/qcom/pdr.h> +#include <linux/soc/qcom/pmic_glink.h> + +struct pmic_glink { + struct device *dev; + struct pdr_handle *pdr; + + struct rpmsg_endpoint *ept; + + struct auxiliary_device altmode_aux; + struct auxiliary_device ps_aux; + struct auxiliary_device ucsi_aux; + + /* serializing client_state and pdr_state updates */ + struct mutex state_lock; + unsigned int client_state; + unsigned int pdr_state; + + /* serializing clients list updates */ + struct mutex client_lock; + struct list_head clients; +}; + +static struct pmic_glink *__pmic_glink; +static DEFINE_MUTEX(__pmic_glink_lock); + +struct pmic_glink_client { + struct list_head node; + + struct pmic_glink *pg; + unsigned int id; + + void (*cb)(const void *data, size_t len, void *priv); + void (*pdr_notify)(void *priv, int state); + void *priv; +}; + +static void _devm_pmic_glink_release_client(struct device *dev, void *res) +{ + struct pmic_glink_client *client = (struct pmic_glink_client *)res; + struct pmic_glink *pg = client->pg; + + mutex_lock(&pg->client_lock); + list_del(&client->node); + mutex_unlock(&pg->client_lock); +} + +struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev, + unsigned int id, + void (*cb)(const void *, size_t, void *), + void (*pdr)(void *, int), + void *priv) +{ + struct pmic_glink_client *client; + struct pmic_glink *pg = dev_get_drvdata(dev->parent); + + client = devres_alloc(_devm_pmic_glink_release_client, sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->pg = pg; + client->id = id; + client->cb = cb; + client->pdr_notify = pdr; + client->priv = priv; + + mutex_lock(&pg->client_lock); + list_add(&client->node, &pg->clients); + mutex_unlock(&pg->client_lock); + + devres_add(dev, client); + + return client; +} +EXPORT_SYMBOL_GPL(devm_pmic_glink_register_client); + +int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len) +{ + struct pmic_glink *pg = client->pg; + + return rpmsg_send(pg->ept, data, len); +} +EXPORT_SYMBOL_GPL(pmic_glink_send); + +static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data, + int len, void *priv, u32 addr) +{ + struct pmic_glink_client *client; + struct pmic_glink_hdr *hdr; + struct pmic_glink *pg = dev_get_drvdata(&rpdev->dev); + + if (len < sizeof(*hdr)) { + dev_warn(pg->dev, "ignoring truncated message\n"); + return 0; + } + + hdr = data; + + list_for_each_entry(client, &pg->clients, node) { + if (client->id == le32_to_cpu(hdr->owner)) + client->cb(data, len, client->priv); + } + + return 0; +} + +static void pmic_glink_aux_release(struct device *dev) {} + +static int pmic_glink_add_aux_device(struct pmic_glink *pg, + struct auxiliary_device *aux, + const char *name) +{ + struct device *parent = pg->dev; + int ret; + + aux->name = name; + aux->dev.parent = parent; + aux->dev.release = pmic_glink_aux_release; + device_set_of_node_from_dev(&aux->dev, parent); + ret = auxiliary_device_init(aux); + if (ret) + return ret; + + ret = auxiliary_device_add(aux); + if (ret) + auxiliary_device_uninit(aux); + + return ret; +} + +static void pmic_glink_del_aux_device(struct pmic_glink *pg, + struct auxiliary_device *aux) +{ + auxiliary_device_delete(aux); + auxiliary_device_uninit(aux); +} + +static void pmic_glink_state_notify_clients(struct pmic_glink *pg) +{ + struct pmic_glink_client *client; + unsigned int new_state = pg->client_state; + + if (pg->client_state != SERVREG_SERVICE_STATE_UP) { + if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept) + new_state = SERVREG_SERVICE_STATE_UP; + } else { + if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept) + new_state = SERVREG_SERVICE_STATE_DOWN; + } + + if (new_state != pg->client_state) { + list_for_each_entry(client, &pg->clients, node) + client->pdr_notify(client->priv, new_state); + pg->client_state = new_state; + } +} + +static void pmic_glink_pdr_callback(int state, char *svc_path, void *priv) +{ + struct pmic_glink *pg = priv; + + mutex_lock(&pg->state_lock); + pg->pdr_state = state; + + pmic_glink_state_notify_clients(pg); + mutex_unlock(&pg->state_lock); +} + +static int pmic_glink_rpmsg_probe(struct rpmsg_device *rpdev) +{ + struct pmic_glink *pg = __pmic_glink; + int ret = 0; + + mutex_lock(&__pmic_glink_lock); + if (!pg) { + ret = dev_err_probe(&rpdev->dev, -ENODEV, "no pmic_glink device to attach to\n"); + goto out_unlock; + } + + dev_set_drvdata(&rpdev->dev, pg); + + mutex_lock(&pg->state_lock); + pg->ept = rpdev->ept; + pmic_glink_state_notify_clients(pg); + mutex_unlock(&pg->state_lock); + +out_unlock: + mutex_unlock(&__pmic_glink_lock); + return ret; +} + +static void pmic_glink_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct pmic_glink *pg; + + mutex_lock(&__pmic_glink_lock); + pg = __pmic_glink; + if (!pg) + goto out_unlock; + + mutex_lock(&pg->state_lock); + pg->ept = NULL; + pmic_glink_state_notify_clients(pg); + mutex_unlock(&pg->state_lock); +out_unlock: + mutex_unlock(&__pmic_glink_lock); +} + +static const struct rpmsg_device_id pmic_glink_rpmsg_id_match[] = { + { "PMIC_RTR_ADSP_APPS" }, + {} +}; + +static struct rpmsg_driver pmic_glink_rpmsg_driver = { + .probe = pmic_glink_rpmsg_probe, + .remove = pmic_glink_rpmsg_remove, + .callback = pmic_glink_rpmsg_callback, + .id_table = pmic_glink_rpmsg_id_match, + .drv = { + .name = "qcom_pmic_glink_rpmsg", + }, +}; + +static int pmic_glink_probe(struct platform_device *pdev) +{ + struct pdr_service *service; + struct pmic_glink *pg; + int ret; + + pg = devm_kzalloc(&pdev->dev, sizeof(*pg), GFP_KERNEL); + if (!pg) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, pg); + + pg->dev = &pdev->dev; + + INIT_LIST_HEAD(&pg->clients); + mutex_init(&pg->client_lock); + mutex_init(&pg->state_lock); + + ret = pmic_glink_add_aux_device(pg, &pg->altmode_aux, "altmode"); + if (ret) + return ret; + ret = pmic_glink_add_aux_device(pg, &pg->ps_aux, "power-supply"); + if (ret) + goto out_release_altmode_aux; + + pg->pdr = pdr_handle_alloc(pmic_glink_pdr_callback, pg); + if (IS_ERR(pg->pdr)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(pg->pdr), "failed to initialize pdr\n"); + goto out_release_aux_devices; + } + + service = pdr_add_lookup(pg->pdr, "tms/servreg", "msm/adsp/charger_pd"); + if (IS_ERR(service)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(service), + "failed adding pdr lookup for charger_pd\n"); + goto out_release_pdr_handle; + } + + mutex_lock(&__pmic_glink_lock); + __pmic_glink = pg; + mutex_unlock(&__pmic_glink_lock); + + return 0; + +out_release_pdr_handle: + pdr_handle_release(pg->pdr); +out_release_aux_devices: + pmic_glink_del_aux_device(pg, &pg->ps_aux); +out_release_altmode_aux: + pmic_glink_del_aux_device(pg, &pg->altmode_aux); + + return ret; +} + +static int pmic_glink_remove(struct platform_device *pdev) +{ + struct pmic_glink *pg = dev_get_drvdata(&pdev->dev); + + pdr_handle_release(pg->pdr); + + pmic_glink_del_aux_device(pg, &pg->ps_aux); + pmic_glink_del_aux_device(pg, &pg->altmode_aux); + + mutex_lock(&__pmic_glink_lock); + __pmic_glink = NULL; + mutex_unlock(&__pmic_glink_lock); + + return 0; +} + +static const struct of_device_id pmic_glink_of_match[] = { + { .compatible = "qcom,pmic-glink", }, + {} +}; +MODULE_DEVICE_TABLE(of, pmic_glink_of_match); + +static struct platform_driver pmic_glink_driver = { + .probe = pmic_glink_probe, + .remove = pmic_glink_remove, + .driver = { + .name = "qcom_pmic_glink", + .of_match_table = pmic_glink_of_match, + }, +}; + +static int pmic_glink_init(void) +{ + platform_driver_register(&pmic_glink_driver); + register_rpmsg_driver(&pmic_glink_rpmsg_driver); + + return 0; +}; +module_init(pmic_glink_init); + +static void pmic_glink_exit(void) +{ + unregister_rpmsg_driver(&pmic_glink_rpmsg_driver); + platform_driver_unregister(&pmic_glink_driver); +}; +module_exit(pmic_glink_exit); + +MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c new file mode 100644 index 000000000000..4d7895bdeaf2 --- /dev/null +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Linaro Ltd + */ +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/soc/qcom/pdr.h> +#include <drm/drm_bridge.h> + +#include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> + +#include <linux/soc/qcom/pmic_glink.h> + +#define PMIC_GLINK_MAX_PORTS 2 + +#define USBC_SC8180X_NOTIFY_IND 0x13 +#define USBC_CMD_WRITE_REQ 0x15 +#define USBC_NOTIFY_IND 0x16 + +#define ALTMODE_PAN_EN 0x10 +#define ALTMODE_PAN_ACK 0x11 + +struct usbc_write_req { + struct pmic_glink_hdr hdr; + __le32 cmd; + __le32 arg; + __le32 reserved; +}; + +#define NOTIFY_PAYLOAD_SIZE 16 +struct usbc_notify { + struct pmic_glink_hdr hdr; + char payload[NOTIFY_PAYLOAD_SIZE]; + u32 reserved; +}; + +struct usbc_sc8180x_notify { + struct pmic_glink_hdr hdr; + __le32 notification; + __le32 reserved[2]; +}; + +enum pmic_glink_altmode_pin_assignment { + DPAM_HPD_OUT, + DPAM_HPD_A, + DPAM_HPD_B, + DPAM_HPD_C, + DPAM_HPD_D, + DPAM_HPD_E, + DPAM_HPD_F, +}; + +struct pmic_glink_altmode; + +#define work_to_altmode_port(w) container_of((w), struct pmic_glink_altmode_port, work) + +struct pmic_glink_altmode_port { + struct pmic_glink_altmode *altmode; + unsigned int index; + + struct typec_switch *typec_switch; + struct typec_mux *typec_mux; + struct typec_mux_state state; + struct typec_altmode dp_alt; + + struct work_struct work; + + struct drm_bridge bridge; + + enum typec_orientation orientation; + u16 svid; + u8 dp_data; + u8 mode; + u8 hpd_state; + u8 hpd_irq; +}; + +#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work) + +struct pmic_glink_altmode { + struct device *dev; + + unsigned int owner_id; + + /* To synchronize WRITE_REQ acks */ + struct mutex lock; + + struct completion pan_ack; + struct pmic_glink_client *client; + + struct work_struct enable_work; + + struct pmic_glink_altmode_port ports[PMIC_GLINK_MAX_PORTS]; +}; + +static int pmic_glink_altmode_request(struct pmic_glink_altmode *altmode, u32 cmd, u32 arg) +{ + struct usbc_write_req req = {}; + unsigned long left; + int ret; + + /* + * The USBC_CMD_WRITE_REQ ack doesn't identify the request, so wait for + * one ack at a time. + */ + mutex_lock(&altmode->lock); + + req.hdr.owner = cpu_to_le32(altmode->owner_id); + req.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP); + req.hdr.opcode = cpu_to_le32(USBC_CMD_WRITE_REQ); + req.cmd = cpu_to_le32(cmd); + req.arg = cpu_to_le32(arg); + + ret = pmic_glink_send(altmode->client, &req, sizeof(req)); + if (ret) { + dev_err(altmode->dev, "failed to send altmode request: %#x (%d)\n", cmd, ret); + goto out_unlock; + } + + left = wait_for_completion_timeout(&altmode->pan_ack, 5 * HZ); + if (!left) { + dev_err(altmode->dev, "timeout waiting for altmode request ack for: %#x\n", cmd); + ret = -ETIMEDOUT; + } + +out_unlock: + mutex_unlock(&altmode->lock); + return ret; +} + +static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, + struct pmic_glink_altmode_port *port, + u8 mode, bool hpd_state, + bool hpd_irq) +{ + struct typec_displayport_data dp_data = {}; + int ret; + + dp_data.status = DP_STATUS_ENABLED; + if (hpd_state) + dp_data.status |= DP_STATUS_HPD_STATE; + if (hpd_irq) + dp_data.status |= DP_STATUS_IRQ_HPD; + dp_data.conf = DP_CONF_SET_PIN_ASSIGN(mode); + + port->state.alt = &port->dp_alt; + port->state.data = &dp_data; + port->state.mode = TYPEC_MODAL_STATE(mode); + + ret = typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(altmode->dev, "failed to switch mux to DP\n"); +} + +static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, + struct pmic_glink_altmode_port *port) +{ + int ret; + + port->state.alt = NULL; + port->state.data = NULL; + port->state.mode = TYPEC_STATE_USB; + + ret = typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(altmode->dev, "failed to switch mux to USB\n"); +} + +static void pmic_glink_altmode_worker(struct work_struct *work) +{ + struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work); + struct pmic_glink_altmode *altmode = alt_port->altmode; + + typec_switch_set(alt_port->typec_switch, alt_port->orientation); + + if (alt_port->svid == USB_TYPEC_DP_SID) + pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode, + alt_port->hpd_state, alt_port->hpd_irq); + else + pmic_glink_altmode_enable_usb(altmode, alt_port); + + if (alt_port->hpd_state) + drm_bridge_hpd_notify(&alt_port->bridge, connector_status_connected); + else + drm_bridge_hpd_notify(&alt_port->bridge, connector_status_disconnected); + + pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index); +}; + +static enum typec_orientation pmic_glink_altmode_orientation(unsigned int orientation) +{ + if (orientation == 0) + return TYPEC_ORIENTATION_NORMAL; + else if (orientation == 1) + return TYPEC_ORIENTATION_REVERSE; + else + return TYPEC_ORIENTATION_NONE; +} + +#define SC8180X_PORT_MASK 0x000000ff +#define SC8180X_ORIENTATION_MASK 0x0000ff00 +#define SC8180X_MUX_MASK 0x00ff0000 +#define SC8180X_MODE_MASK 0x3f000000 +#define SC8180X_HPD_STATE_MASK 0x40000000 +#define SC8180X_HPD_IRQ_MASK 0x80000000 + +static void pmic_glink_altmode_sc8180xp_notify(struct pmic_glink_altmode *altmode, + const void *data, size_t len) +{ + struct pmic_glink_altmode_port *alt_port; + const struct usbc_sc8180x_notify *msg; + u32 notification; + u8 orientation; + u8 hpd_state; + u8 hpd_irq; + u16 svid; + u8 port; + u8 mode; + u8 mux; + + if (len != sizeof(*msg)) { + dev_warn(altmode->dev, "invalid length of USBC_NOTIFY indication: %zd\n", len); + return; + } + + msg = data; + notification = le32_to_cpu(msg->notification); + port = FIELD_GET(SC8180X_PORT_MASK, notification); + orientation = FIELD_GET(SC8180X_ORIENTATION_MASK, notification); + mux = FIELD_GET(SC8180X_MUX_MASK, notification); + mode = FIELD_GET(SC8180X_MODE_MASK, notification); + hpd_state = FIELD_GET(SC8180X_HPD_STATE_MASK, notification); + hpd_irq = FIELD_GET(SC8180X_HPD_IRQ_MASK, notification); + + svid = mux == 2 ? USB_TYPEC_DP_SID : 0; + + if (!altmode->ports[port].altmode) { + dev_dbg(altmode->dev, "notification on undefined port %d\n", port); + return; + } + + alt_port = &altmode->ports[port]; + alt_port->orientation = pmic_glink_altmode_orientation(orientation); + alt_port->svid = svid; + alt_port->mode = mode; + alt_port->hpd_state = hpd_state; + alt_port->hpd_irq = hpd_irq; + schedule_work(&alt_port->work); +} + +#define SC8280XP_DPAM_MASK 0x3f +#define SC8280XP_HPD_STATE_MASK BIT(6) +#define SC8280XP_HPD_IRQ_MASK BIT(7) + +static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmode, + u16 svid, const void *data, size_t len) +{ + struct pmic_glink_altmode_port *alt_port; + const struct usbc_notify *notify; + u8 orientation; + u8 hpd_state; + u8 hpd_irq; + u8 mode; + u8 port; + + if (len != sizeof(*notify)) { + dev_warn(altmode->dev, "invalid length USBC_NOTIFY_IND: %zd\n", + len); + return; + } + + notify = data; + + port = notify->payload[0]; + orientation = notify->payload[1]; + mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A; + hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]); + hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]); + + if (!altmode->ports[port].altmode) { + dev_dbg(altmode->dev, "notification on undefined port %d\n", port); + return; + } + + alt_port = &altmode->ports[port]; + alt_port->orientation = pmic_glink_altmode_orientation(orientation); + alt_port->svid = svid; + alt_port->mode = mode; + alt_port->hpd_state = hpd_state; + alt_port->hpd_irq = hpd_irq; + schedule_work(&alt_port->work); +} + +static void pmic_glink_altmode_callback(const void *data, size_t len, void *priv) +{ + struct pmic_glink_altmode *altmode = priv; + const struct pmic_glink_hdr *hdr = data; + u16 opcode; + u16 svid; + + opcode = le32_to_cpu(hdr->opcode) & 0xff; + svid = le32_to_cpu(hdr->opcode) >> 16; + + switch (opcode) { + case USBC_CMD_WRITE_REQ: + complete(&altmode->pan_ack); + break; + case USBC_NOTIFY_IND: + pmic_glink_altmode_sc8280xp_notify(altmode, svid, data, len); + break; + case USBC_SC8180X_NOTIFY_IND: + pmic_glink_altmode_sc8180xp_notify(altmode, data, len); + break; + } +} + +static int pmic_glink_altmode_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; +} + +static const struct drm_bridge_funcs pmic_glink_altmode_bridge_funcs = { + .attach = pmic_glink_altmode_attach, +}; + +static void pmic_glink_altmode_put_mux(void *data) +{ + typec_mux_put(data); +} + +static void pmic_glink_altmode_put_switch(void *data) +{ + typec_switch_put(data); +} + +static void pmic_glink_altmode_enable_worker(struct work_struct *work) +{ + struct pmic_glink_altmode *altmode = work_to_altmode(work); + int ret; + + ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0); + if (ret) + dev_err(altmode->dev, "failed to request altmode notifications\n"); +} + +static void pmic_glink_altmode_pdr_notify(void *priv, int state) +{ + struct pmic_glink_altmode *altmode = priv; + + if (state == SERVREG_SERVICE_STATE_UP) + schedule_work(&altmode->enable_work); +} + +static const struct of_device_id pmic_glink_altmode_of_quirks[] = { + { .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)PMIC_GLINK_OWNER_USBC }, + {} +}; + +static int pmic_glink_altmode_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct pmic_glink_altmode_port *alt_port; + struct pmic_glink_altmode *altmode; + struct typec_altmode_desc mux_desc = {}; + const struct of_device_id *match; + struct fwnode_handle *fwnode; + struct device *dev = &adev->dev; + u32 port; + int ret; + + altmode = devm_kzalloc(dev, sizeof(*altmode), GFP_KERNEL); + if (!altmode) + return -ENOMEM; + + altmode->dev = dev; + + match = of_match_device(pmic_glink_altmode_of_quirks, dev->parent); + if (match) + altmode->owner_id = (unsigned long)match->data; + else + altmode->owner_id = PMIC_GLINK_OWNER_USBC_PAN; + + INIT_WORK(&altmode->enable_work, pmic_glink_altmode_enable_worker); + init_completion(&altmode->pan_ack); + mutex_init(&altmode->lock); + + device_for_each_child_node(dev, fwnode) { + ret = fwnode_property_read_u32(fwnode, "reg", &port); + if (ret < 0) { + dev_err(dev, "missing reg property of %pOFn\n", fwnode); + return ret; + } + + if (port >= ARRAY_SIZE(altmode->ports)) { + dev_warn(dev, "invalid connector number, ignoring\n"); + continue; + } + + if (altmode->ports[port].altmode) { + dev_err(dev, "multiple connector definition for port %u\n", port); + return -EINVAL; + } + + alt_port = &altmode->ports[port]; + alt_port->altmode = altmode; + alt_port->index = port; + INIT_WORK(&alt_port->work, pmic_glink_altmode_worker); + + alt_port->bridge.funcs = &pmic_glink_altmode_bridge_funcs; + alt_port->bridge.of_node = to_of_node(fwnode); + alt_port->bridge.ops = DRM_BRIDGE_OP_HPD; + alt_port->bridge.type = DRM_MODE_CONNECTOR_USB; + + ret = devm_drm_bridge_add(dev, &alt_port->bridge); + if (ret) + return ret; + + alt_port->dp_alt.svid = USB_TYPEC_DP_SID; + alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; + alt_port->dp_alt.active = 1; + + mux_desc.svid = USB_TYPEC_DP_SID; + mux_desc.mode = USB_TYPEC_DP_MODE; + alt_port->typec_mux = fwnode_typec_mux_get(fwnode, &mux_desc); + if (IS_ERR(alt_port->typec_mux)) + return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux), + "failed to acquire mode-switch for port: %d\n", + port); + + ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux, + alt_port->typec_mux); + if (ret) + return ret; + + alt_port->typec_switch = fwnode_typec_switch_get(fwnode); + if (IS_ERR(alt_port->typec_switch)) + return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch), + "failed to acquire orientation-switch for port: %d\n", + port); + + ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch, + alt_port->typec_switch); + if (ret) + return ret; + } + + altmode->client = devm_pmic_glink_register_client(dev, + altmode->owner_id, + pmic_glink_altmode_callback, + pmic_glink_altmode_pdr_notify, + altmode); + return PTR_ERR_OR_ZERO(altmode->client); +} + +static const struct auxiliary_device_id pmic_glink_altmode_id_table[] = { + { .name = "pmic_glink.altmode", }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, pmic_glink_altmode_id_table); + +static struct auxiliary_driver pmic_glink_altmode_driver = { + .name = "pmic_glink_altmode", + .probe = pmic_glink_altmode_probe, + .id_table = pmic_glink_altmode_id_table, +}; + +module_auxiliary_driver(pmic_glink_altmode_driver); + +MODULE_DESCRIPTION("Qualcomm PMIC GLINK Altmode driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 6228af057120..c207bb96c523 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -92,7 +92,7 @@ static int qcom_subsystem_sleep_stats_show(struct seq_file *s, void *unused) /* Items are allocated lazily, so lookup pointer each time */ stat = qcom_smem_get(subsystem->pid, subsystem->smem_item, NULL); if (IS_ERR(stat)) - return -EIO; + return 0; qcom_print_stats(s, stat); @@ -170,20 +170,14 @@ static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem * static void qcom_create_subsystem_stat_files(struct dentry *root, const struct stats_config *config) { - const struct sleep_stats *stat; int i; if (!config->subsystem_stats_in_smem) return; - for (i = 0; i < ARRAY_SIZE(subsystems); i++) { - stat = qcom_smem_get(subsystems[i].pid, subsystems[i].smem_item, NULL); - if (IS_ERR(stat)) - continue; - + for (i = 0; i < ARRAY_SIZE(subsystems); i++) debugfs_create_file(subsystems[i].name, 0400, root, (void *)&subsystems[i], &qcom_subsystem_sleep_stats_fops); - } } static int qcom_stats_probe(struct platform_device *pdev) diff --git a/drivers/soc/qcom/ramp_controller.c b/drivers/soc/qcom/ramp_controller.c new file mode 100644 index 000000000000..dc74d2a19de2 --- /dev/null +++ b/drivers/soc/qcom/ramp_controller.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm Ramp Controller driver + * Copyright (c) 2022, AngeloGioacchino Del Regno + * <angelogioacchino.delregno@collabora.com> + */ + +#include <linux/bitfield.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#define RC_UPDATE_EN BIT(0) +#define RC_ROOT_EN BIT(1) + +#define RC_REG_CFG_UPDATE 0x60 +#define RC_CFG_UPDATE_EN BIT(8) +#define RC_CFG_ACK GENMASK(31, 16) + +#define RC_DCVS_CFG_SID 2 +#define RC_LINK_SID 3 +#define RC_LMH_SID 6 +#define RC_DFS_SID 14 + +#define RC_UPDATE_TIMEOUT_US 500 + +/** + * struct qcom_ramp_controller_desc - SoC specific parameters + * @cfg_dfs_sid: Dynamic Frequency Scaling SID configuration + * @cfg_link_sid: Link SID configuration + * @cfg_lmh_sid: Limits Management hardware SID configuration + * @cfg_ramp_en: Ramp Controller enable sequence + * @cfg_ramp_dis: Ramp Controller disable sequence + * @cmd_reg: Command register offset + * @num_dfs_sids: Number of DFS SIDs (max 8) + * @num_link_sids: Number of Link SIDs (max 3) + * @num_lmh_sids: Number of LMh SIDs (max 8) + * @num_ramp_en: Number of entries in enable sequence + * @num_ramp_dis: Number of entries in disable sequence + */ +struct qcom_ramp_controller_desc { + const struct reg_sequence *cfg_dfs_sid; + const struct reg_sequence *cfg_link_sid; + const struct reg_sequence *cfg_lmh_sid; + const struct reg_sequence *cfg_ramp_en; + const struct reg_sequence *cfg_ramp_dis; + u8 cmd_reg; + u8 num_dfs_sids; + u8 num_link_sids; + u8 num_lmh_sids; + u8 num_ramp_en; + u8 num_ramp_dis; +}; + +/** + * struct qcom_ramp_controller - Main driver structure + * @regmap: Regmap handle + * @desc: SoC specific parameters + */ +struct qcom_ramp_controller { + struct regmap *regmap; + const struct qcom_ramp_controller_desc *desc; +}; + +/** + * rc_wait_for_update() - Wait for Ramp Controller root update + * @qrc: Main driver structure + * + * Return: Zero for success or negative number for failure + */ +static int rc_wait_for_update(struct qcom_ramp_controller *qrc) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + struct regmap *r = qrc->regmap; + u32 val; + int ret; + + ret = regmap_set_bits(r, d->cmd_reg, RC_ROOT_EN); + if (ret) + return ret; + + return regmap_read_poll_timeout(r, d->cmd_reg, val, !(val & RC_UPDATE_EN), + 1, RC_UPDATE_TIMEOUT_US); +} + +/** + * rc_set_cfg_update() - Ramp Controller configuration update + * @qrc: Main driver structure + * @ce: Configuration entry to update + * + * Return: Zero for success or negative number for failure + */ +static int rc_set_cfg_update(struct qcom_ramp_controller *qrc, u8 ce) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + struct regmap *r = qrc->regmap; + u32 ack, val; + int ret; + + /* The ack bit is between bits 16-31 of RC_REG_CFG_UPDATE */ + ack = FIELD_PREP(RC_CFG_ACK, BIT(ce)); + + /* Write the configuration type first... */ + ret = regmap_set_bits(r, d->cmd_reg + RC_REG_CFG_UPDATE, ce); + if (ret) + return ret; + + /* ...and after that, enable the update bit to sync the changes */ + ret = regmap_set_bits(r, d->cmd_reg + RC_REG_CFG_UPDATE, RC_CFG_UPDATE_EN); + if (ret) + return ret; + + /* Wait for the changes to go through */ + ret = regmap_read_poll_timeout(r, d->cmd_reg + RC_REG_CFG_UPDATE, val, + val & ack, 1, RC_UPDATE_TIMEOUT_US); + if (ret) + return ret; + + /* + * Configuration update success! The CFG_UPDATE register will not be + * cleared automatically upon applying the configuration, so we have + * to do that manually in order to leave the ramp controller in a + * predictable and clean state. + */ + ret = regmap_write(r, d->cmd_reg + RC_REG_CFG_UPDATE, 0); + if (ret) + return ret; + + /* Wait for the update bit cleared ack */ + return regmap_read_poll_timeout(r, d->cmd_reg + RC_REG_CFG_UPDATE, + val, !(val & RC_CFG_ACK), 1, + RC_UPDATE_TIMEOUT_US); +} + +/** + * rc_write_cfg - Send configuration sequence + * @qrc: Main driver structure + * @seq: Register sequence to send before asking for update + * @ce: Configuration SID + * @nsids: Total number of SIDs + * + * Returns: Zero for success or negative number for error + */ +static int rc_write_cfg(struct qcom_ramp_controller *qrc, + const struct reg_sequence *seq, + u16 ce, u8 nsids) +{ + int ret; + u8 i; + + /* Check if, and wait until the ramp controller is ready */ + ret = rc_wait_for_update(qrc); + if (ret) + return ret; + + /* Write the sequence */ + ret = regmap_multi_reg_write(qrc->regmap, seq, nsids); + if (ret) + return ret; + + /* Pull the trigger: do config update starting from the last sid */ + for (i = 0; i < nsids; i++) { + ret = rc_set_cfg_update(qrc, (u8)ce - i); + if (ret) + return ret; + } + + return 0; +} + +/** + * rc_ramp_ctrl_enable() - Enable Ramp up/down Control + * @qrc: Main driver structure + * + * Return: Zero for success or negative number for error + */ +static int rc_ramp_ctrl_enable(struct qcom_ramp_controller *qrc) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + int i, ret; + + for (i = 0; i < d->num_ramp_en; i++) { + ret = rc_write_cfg(qrc, &d->cfg_ramp_en[i], RC_DCVS_CFG_SID, 1); + if (ret) + return ret; + } + + return 0; +} + +/** + * qcom_ramp_controller_start() - Initialize and start the ramp controller + * @qrc: Main driver structure + * + * The Ramp Controller needs to be initialized by programming the relevant + * registers with SoC-specific configuration: once programming is done, + * the hardware will take care of the rest (no further handling required). + * + * Return: Zero for success or negative number for error + */ +static int qcom_ramp_controller_start(struct qcom_ramp_controller *qrc) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + int ret; + + /* Program LMH, DFS, Link SIDs */ + ret = rc_write_cfg(qrc, d->cfg_lmh_sid, RC_LMH_SID, d->num_lmh_sids); + if (ret) + return ret; + + ret = rc_write_cfg(qrc, d->cfg_dfs_sid, RC_DFS_SID, d->num_dfs_sids); + if (ret) + return ret; + + ret = rc_write_cfg(qrc, d->cfg_link_sid, RC_LINK_SID, d->num_link_sids); + if (ret) + return ret; + + /* Everything is ready! Enable the ramp up/down control */ + return rc_ramp_ctrl_enable(qrc); +} + +static const struct regmap_config qrc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x68, + .fast_io = true, +}; + +static const struct reg_sequence msm8976_cfg_dfs_sid[] = { + { 0x10, 0xfefebff7 }, + { 0x14, 0xfdff7fef }, + { 0x18, 0xfbffdefb }, + { 0x1c, 0xb69b5555 }, + { 0x20, 0x24929249 }, + { 0x24, 0x49241112 }, + { 0x28, 0x11112111 }, + { 0x2c, 0x8102 } +}; + +static const struct reg_sequence msm8976_cfg_link_sid[] = { + { 0x40, 0xfc987 } +}; + +static const struct reg_sequence msm8976_cfg_lmh_sid[] = { + { 0x30, 0x77706db }, + { 0x34, 0x5550249 }, + { 0x38, 0x111 } +}; + +static const struct reg_sequence msm8976_cfg_ramp_en[] = { + { 0x50, 0x800 }, /* pre_en */ + { 0x50, 0xc00 }, /* en */ + { 0x50, 0x400 } /* post_en */ +}; + +static const struct reg_sequence msm8976_cfg_ramp_dis[] = { + { 0x50, 0x0 } +}; + +static const struct qcom_ramp_controller_desc msm8976_rc_cfg = { + .cfg_dfs_sid = msm8976_cfg_dfs_sid, + .num_dfs_sids = ARRAY_SIZE(msm8976_cfg_dfs_sid), + + .cfg_link_sid = msm8976_cfg_link_sid, + .num_link_sids = ARRAY_SIZE(msm8976_cfg_link_sid), + + .cfg_lmh_sid = msm8976_cfg_lmh_sid, + .num_lmh_sids = ARRAY_SIZE(msm8976_cfg_lmh_sid), + + .cfg_ramp_en = msm8976_cfg_ramp_en, + .num_ramp_en = ARRAY_SIZE(msm8976_cfg_ramp_en), + + .cfg_ramp_dis = msm8976_cfg_ramp_dis, + .num_ramp_dis = ARRAY_SIZE(msm8976_cfg_ramp_dis), + + .cmd_reg = 0x0, +}; + +static int qcom_ramp_controller_probe(struct platform_device *pdev) +{ + struct qcom_ramp_controller *qrc; + void __iomem *base; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + qrc = devm_kmalloc(&pdev->dev, sizeof(*qrc), GFP_KERNEL); + if (!qrc) + return -ENOMEM; + + qrc->desc = device_get_match_data(&pdev->dev); + if (!qrc) + return -EINVAL; + + qrc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &qrc_regmap_config); + if (IS_ERR(qrc->regmap)) + return PTR_ERR(qrc->regmap); + + platform_set_drvdata(pdev, qrc); + + return qcom_ramp_controller_start(qrc); +} + +static int qcom_ramp_controller_remove(struct platform_device *pdev) +{ + struct qcom_ramp_controller *qrc = platform_get_drvdata(pdev); + + return rc_write_cfg(qrc, qrc->desc->cfg_ramp_dis, + RC_DCVS_CFG_SID, qrc->desc->num_ramp_dis); +} + +static const struct of_device_id qcom_ramp_controller_match_table[] = { + { .compatible = "qcom,msm8976-ramp-controller", .data = &msm8976_rc_cfg }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, qcom_ramp_controller_match_table); + +static struct platform_driver qcom_ramp_controller_driver = { + .driver = { + .name = "qcom-ramp-controller", + .of_match_table = qcom_ramp_controller_match_table, + .suppress_bind_attrs = true, + }, + .probe = qcom_ramp_controller_probe, + .remove = qcom_ramp_controller_remove, +}; + +static int __init qcom_ramp_controller_init(void) +{ + return platform_driver_register(&qcom_ramp_controller_driver); +} +arch_initcall(qcom_ramp_controller_init); + +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); +MODULE_DESCRIPTION("Qualcomm Ramp Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index 0feaae357821..2d3ee22b9249 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -14,9 +14,10 @@ #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/io.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1) +#define NUM_MAX_VMIDS 2 static dev_t qcom_rmtfs_mem_major; @@ -171,12 +172,12 @@ static void qcom_rmtfs_mem_release_device(struct device *dev) static int qcom_rmtfs_mem_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; - struct qcom_scm_vmperm perms[2]; + struct qcom_scm_vmperm perms[NUM_MAX_VMIDS + 1]; struct reserved_mem *rmem; struct qcom_rmtfs_mem *rmtfs_mem; u32 client_id; - u32 vmid; - int ret; + u32 num_vmids, vmid[NUM_MAX_VMIDS]; + int ret, i; rmem = of_reserved_mem_lookup(node); if (!rmem) { @@ -226,7 +227,18 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) goto put_device; } - ret = of_property_read_u32(node, "qcom,vmid", &vmid); + num_vmids = of_property_count_u32_elems(node, "qcom,vmid"); + if (num_vmids < 0) { + dev_err(&pdev->dev, "failed to count qcom,vmid elements: %d\n", ret); + goto remove_cdev; + } else if (num_vmids > NUM_MAX_VMIDS) { + dev_warn(&pdev->dev, + "too many VMIDs (%d) specified! Only mapping first %d entries\n", + num_vmids, NUM_MAX_VMIDS); + num_vmids = NUM_MAX_VMIDS; + } + + ret = of_property_read_u32_array(node, "qcom,vmid", vmid, num_vmids); if (ret < 0 && ret != -EINVAL) { dev_err(&pdev->dev, "failed to parse qcom,vmid\n"); goto remove_cdev; @@ -238,12 +250,15 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) perms[0].vmid = QCOM_SCM_VMID_HLOS; perms[0].perm = QCOM_SCM_PERM_RW; - perms[1].vmid = vmid; - perms[1].perm = QCOM_SCM_PERM_RW; + + for (i = 0; i < num_vmids; i++) { + perms[i + 1].vmid = vmid[i]; + perms[i + 1].perm = QCOM_SCM_PERM_RW; + } rmtfs_mem->perms = BIT(QCOM_SCM_VMID_HLOS); ret = qcom_scm_assign_mem(rmtfs_mem->addr, rmtfs_mem->size, - &rmtfs_mem->perms, perms, 2); + &rmtfs_mem->perms, perms, num_vmids + 1); if (ret < 0) { dev_err(&pdev->dev, "assign memory failed\n"); goto remove_cdev; diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 4c2d2c296790..f20e2a49a669 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -187,6 +187,16 @@ static struct rpmhpd nsp = { .res_name = "nsp.lvl", }; +static struct rpmhpd nsp0 = { + .pd = { .name = "nsp0", }, + .res_name = "nsp0.lvl", +}; + +static struct rpmhpd nsp1 = { + .pd = { .name = "nsp1", }, + .res_name = "nsp1.lvl", +}; + static struct rpmhpd qphy = { .pd = { .name = "qphy", }, .res_name = "qphy.lvl", @@ -212,6 +222,29 @@ static const struct rpmhpd_desc sa8540p_desc = { .num_pds = ARRAY_SIZE(sa8540p_rpmhpds), }; +/* SA8775P RPMH power domains */ +static struct rpmhpd *sa8775p_rpmhpds[] = { + [SA8775P_CX] = &cx, + [SA8775P_CX_AO] = &cx_ao, + [SA8775P_EBI] = &ebi, + [SA8775P_GFX] = &gfx, + [SA8775P_LCX] = &lcx, + [SA8775P_LMX] = &lmx, + [SA8775P_MMCX] = &mmcx, + [SA8775P_MMCX_AO] = &mmcx_ao, + [SA8775P_MXC] = &mxc, + [SA8775P_MXC_AO] = &mxc_ao, + [SA8775P_MX] = &mx, + [SA8775P_MX_AO] = &mx_ao, + [SA8775P_NSP0] = &nsp0, + [SA8775P_NSP1] = &nsp1, +}; + +static const struct rpmhpd_desc sa8775p_desc = { + .rpmhpds = sa8775p_rpmhpds, + .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), +}; + /* SDM670 RPMH powerdomains */ static struct rpmhpd *sdm670_rpmhpds[] = { [SDM670_CX] = &cx_w_mx_parent, @@ -487,6 +520,7 @@ static const struct rpmhpd_desc sc8280xp_desc = { static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, + { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc }, { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index f0db6a10cf4e..337b1ad1cd3b 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -471,23 +471,6 @@ static const struct rpmpd_desc qcm2290_desc = { .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, }; -static struct rpmpd *sm4250_rpmpds[] = { - [SM4250_VDDCX] = &sm6115_vddcx, - [SM4250_VDDCX_AO] = &sm6115_vddcx_ao, - [SM4250_VDDCX_VFL] = &sm6115_vddcx_vfl, - [SM4250_VDDMX] = &sm6115_vddmx, - [SM4250_VDDMX_AO] = &sm6115_vddmx_ao, - [SM4250_VDDMX_VFL] = &sm6115_vddmx_vfl, - [SM4250_VDD_LPI_CX] = &sm6115_vdd_lpi_cx, - [SM4250_VDD_LPI_MX] = &sm6115_vdd_lpi_mx, -}; - -static const struct rpmpd_desc sm4250_desc = { - .rpmpds = sm4250_rpmpds, - .num_pds = ARRAY_SIZE(sm4250_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, -}; - static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc }, { .compatible = "qcom,msm8226-rpmpd", .data = &msm8226_desc }, @@ -502,7 +485,6 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,qcm2290-rpmpd", .data = &qcm2290_desc }, { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc }, - { .compatible = "qcom,sm4250-rpmpd", .data = &sm4250_desc }, { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc }, { .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc }, { .compatible = "qcom,sm6375-rpmpd", .data = &sm6375_desc }, diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index 7e3b6a7ea34c..523627d5d398 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -233,6 +233,7 @@ static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8084" }, { .compatible = "qcom,rpm-ipq6018" }, + { .compatible = "qcom,rpm-ipq9574" }, { .compatible = "qcom,rpm-msm8226" }, { .compatible = "qcom,rpm-msm8909" }, { .compatible = "qcom,rpm-msm8916" }, diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index ebcbf9b9c18b..e9012ca1a87b 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -169,6 +169,13 @@ struct socinfo { __le32 ndefective_parts_array_offset; /* Version 15 */ __le32 nmodem_supported; + /* Version 16 */ + __le32 feature_code; + __le32 pcode; + __le32 npartnamemap_offset; + __le32 nnum_partname_mapping; + /* Version 17 */ + __le32 oem_variant; }; #ifdef CONFIG_DEBUG_FS @@ -189,6 +196,9 @@ struct socinfo_params { u32 num_defective_parts; u32 ndefective_parts_array_offset; u32 nmodem_supported; + u32 feature_code; + u32 pcode; + u32 oem_variant; }; struct smem_image_version { @@ -214,44 +224,72 @@ struct soc_id { }; static const struct soc_id soc_id[] = { + { qcom_board_id(MSM8260) }, + { qcom_board_id(MSM8660) }, + { qcom_board_id(APQ8060) }, { qcom_board_id(MSM8960) }, { qcom_board_id(APQ8064) }, + { qcom_board_id(MSM8930) }, + { qcom_board_id(MSM8630) }, + { qcom_board_id(MSM8230) }, + { qcom_board_id(APQ8030) }, + { qcom_board_id(MSM8627) }, + { qcom_board_id(MSM8227) }, { qcom_board_id(MSM8660A) }, { qcom_board_id(MSM8260A) }, { qcom_board_id(APQ8060A) }, { qcom_board_id(MSM8974) }, + { qcom_board_id(MSM8225) }, + { qcom_board_id(MSM8625) }, { qcom_board_id(MPQ8064) }, { qcom_board_id(MSM8960AB) }, { qcom_board_id(APQ8060AB) }, { qcom_board_id(MSM8260AB) }, { qcom_board_id(MSM8660AB) }, + { qcom_board_id(MSM8930AA) }, + { qcom_board_id(MSM8630AA) }, + { qcom_board_id(MSM8230AA) }, { qcom_board_id(MSM8626) }, { qcom_board_id(MSM8610) }, { qcom_board_id(APQ8064AB) }, + { qcom_board_id(MSM8930AB) }, + { qcom_board_id(MSM8630AB) }, + { qcom_board_id(MSM8230AB) }, + { qcom_board_id(APQ8030AB) }, { qcom_board_id(MSM8226) }, { qcom_board_id(MSM8526) }, + { qcom_board_id(APQ8030AA) }, { qcom_board_id(MSM8110) }, { qcom_board_id(MSM8210) }, { qcom_board_id(MSM8810) }, { qcom_board_id(MSM8212) }, { qcom_board_id(MSM8612) }, { qcom_board_id(MSM8112) }, + { qcom_board_id(MSM8125) }, { qcom_board_id(MSM8225Q) }, { qcom_board_id(MSM8625Q) }, { qcom_board_id(MSM8125Q) }, { qcom_board_id(APQ8064AA) }, { qcom_board_id(APQ8084) }, + { qcom_board_id(MSM8130) }, + { qcom_board_id(MSM8130AA) }, + { qcom_board_id(MSM8130AB) }, + { qcom_board_id(MSM8627AA) }, + { qcom_board_id(MSM8227AA) }, { qcom_board_id(APQ8074) }, { qcom_board_id(MSM8274) }, { qcom_board_id(MSM8674) }, + { qcom_board_id(MDM9635) }, { qcom_board_id_named(MSM8974PRO_AC, "MSM8974PRO-AC") }, { qcom_board_id(MSM8126) }, { qcom_board_id(APQ8026) }, { qcom_board_id(MSM8926) }, + { qcom_board_id(IPQ8062) }, + { qcom_board_id(IPQ8064) }, + { qcom_board_id(IPQ8066) }, + { qcom_board_id(IPQ8068) }, { qcom_board_id(MSM8326) }, { qcom_board_id(MSM8916) }, - { qcom_board_id(MSM8956) }, - { qcom_board_id(MSM8976) }, { qcom_board_id(MSM8994) }, { qcom_board_id_named(APQ8074PRO_AA, "APQ8074PRO-AA") }, { qcom_board_id_named(APQ8074PRO_AB, "APQ8074PRO-AB") }, @@ -273,32 +311,74 @@ static const struct soc_id soc_id[] = { { qcom_board_id(MSM8510) }, { qcom_board_id(MSM8512) }, { qcom_board_id(MSM8936) }, + { qcom_board_id(MDM9640) }, { qcom_board_id(MSM8939) }, { qcom_board_id(APQ8036) }, { qcom_board_id(APQ8039) }, + { qcom_board_id(MSM8236) }, + { qcom_board_id(MSM8636) }, + { qcom_board_id(MSM8909) }, { qcom_board_id(MSM8996) }, { qcom_board_id(APQ8016) }, { qcom_board_id(MSM8216) }, { qcom_board_id(MSM8116) }, { qcom_board_id(MSM8616) }, { qcom_board_id(MSM8992) }, + { qcom_board_id(APQ8092) }, { qcom_board_id(APQ8094) }, + { qcom_board_id(MSM8209) }, + { qcom_board_id(MSM8208) }, + { qcom_board_id(MDM9209) }, + { qcom_board_id(MDM9309) }, + { qcom_board_id(MDM9609) }, + { qcom_board_id(MSM8239) }, + { qcom_board_id(MSM8952) }, + { qcom_board_id(APQ8009) }, + { qcom_board_id(MSM8956) }, + { qcom_board_id(MSM8929) }, + { qcom_board_id(MSM8629) }, + { qcom_board_id(MSM8229) }, + { qcom_board_id(APQ8029) }, + { qcom_board_id(APQ8056) }, + { qcom_board_id(MSM8609) }, + { qcom_board_id(APQ8076) }, + { qcom_board_id(MSM8976) }, + { qcom_board_id(IPQ8065) }, + { qcom_board_id(IPQ8069) }, + { qcom_board_id(MDM9650) }, + { qcom_board_id(MDM9655) }, + { qcom_board_id(MDM9250) }, + { qcom_board_id(MDM9255) }, + { qcom_board_id(MDM9350) }, + { qcom_board_id(APQ8052) }, { qcom_board_id(MDM9607) }, { qcom_board_id(APQ8096) }, { qcom_board_id(MSM8998) }, { qcom_board_id(MSM8953) }, + { qcom_board_id(MSM8937) }, + { qcom_board_id(APQ8037) }, { qcom_board_id(MDM8207) }, { qcom_board_id(MDM9207) }, { qcom_board_id(MDM9307) }, { qcom_board_id(MDM9628) }, + { qcom_board_id(MSM8909W) }, + { qcom_board_id(APQ8009W) }, + { qcom_board_id(MSM8996L) }, + { qcom_board_id(MSM8917) }, { qcom_board_id(APQ8053) }, { qcom_board_id(MSM8996SG) }, + { qcom_board_id(APQ8017) }, + { qcom_board_id(MSM8217) }, + { qcom_board_id(MSM8617) }, { qcom_board_id(MSM8996AU) }, { qcom_board_id(APQ8096AU) }, { qcom_board_id(APQ8096SG) }, + { qcom_board_id(MSM8940) }, + { qcom_board_id(SDX201) }, { qcom_board_id(SDM660) }, { qcom_board_id(SDM630) }, { qcom_board_id(APQ8098) }, + { qcom_board_id(MSM8920) }, { qcom_board_id(SDM845) }, { qcom_board_id(MDM9206) }, { qcom_board_id(IPQ8074) }, @@ -306,6 +386,8 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SDM658) }, { qcom_board_id(SDA658) }, { qcom_board_id(SDA630) }, + { qcom_board_id(MSM8905) }, + { qcom_board_id(SDX202) }, { qcom_board_id(SDM450) }, { qcom_board_id(SM8150) }, { qcom_board_id(SDA845) }, @@ -317,10 +399,15 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SDM632) }, { qcom_board_id(SDA632) }, { qcom_board_id(SDA450) }, + { qcom_board_id(SDM439) }, + { qcom_board_id(SDM429) }, { qcom_board_id(SM8250) }, { qcom_board_id(SA8155) }, + { qcom_board_id(SDA439) }, + { qcom_board_id(SDA429) }, { qcom_board_id(IPQ8070) }, { qcom_board_id(IPQ8071) }, + { qcom_board_id(QM215) }, { qcom_board_id(IPQ8072A) }, { qcom_board_id(IPQ8074A) }, { qcom_board_id(IPQ8076A) }, @@ -330,18 +417,20 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ8071A) }, { qcom_board_id(IPQ6018) }, { qcom_board_id(IPQ6028) }, + { qcom_board_id(SDM429W) }, { qcom_board_id(SM4250) }, { qcom_board_id(IPQ6000) }, { qcom_board_id(IPQ6010) }, { qcom_board_id(SC7180) }, { qcom_board_id(SM6350) }, + { qcom_board_id(QCM2150) }, + { qcom_board_id(SDA429W) }, { qcom_board_id(SM8350) }, { qcom_board_id(SM6115) }, { qcom_board_id(SC8280XP) }, { qcom_board_id(IPQ6005) }, { qcom_board_id(QRB5165) }, { qcom_board_id(SM8450) }, - { qcom_board_id(SM8550) }, { qcom_board_id(SM7225) }, { qcom_board_id(SA8295P) }, { qcom_board_id(SA8540P) }, @@ -352,12 +441,15 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SC7280) }, { qcom_board_id(SC7180P) }, { qcom_board_id(SM6375) }, + { qcom_board_id(SM8550) }, { qcom_board_id(QRU1000) }, { qcom_board_id(QDU1000) }, { qcom_board_id(QDU1010) }, { qcom_board_id(QRU1032) }, { qcom_board_id(QRU1052) }, { qcom_board_id(QRU1062) }, + { qcom_board_id(IPQ5332) }, + { qcom_board_id(IPQ5322) }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) @@ -512,6 +604,20 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, &qcom_socinfo->info.fmt); switch (qcom_socinfo->info.fmt) { + case SOCINFO_VERSION(0, 17): + qcom_socinfo->info.oem_variant = __le32_to_cpu(info->oem_variant); + debugfs_create_u32("oem_variant", 0444, qcom_socinfo->dbg_root, + &qcom_socinfo->info.oem_variant); + fallthrough; + case SOCINFO_VERSION(0, 16): + qcom_socinfo->info.feature_code = __le32_to_cpu(info->feature_code); + qcom_socinfo->info.pcode = __le32_to_cpu(info->pcode); + + debugfs_create_u32("feature_code", 0444, qcom_socinfo->dbg_root, + &qcom_socinfo->info.feature_code); + debugfs_create_u32("pcode", 0444, qcom_socinfo->dbg_root, + &qcom_socinfo->info.pcode); + fallthrough; case SOCINFO_VERSION(0, 15): qcom_socinfo->info.nmodem_supported = __le32_to_cpu(info->nmodem_supported); |