Message ID | 20220818031512.319310-3-bjorn.andersson@linaro.org (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | soc: qcom: Introduce PMIC GLINK | expand |
On 18/08/2022 06:15, Bjorn Andersson wrote: > The PMIC GLINK service runs on one of the co-processors of some modern > Qualcomm platforms and implements USB-C and battery managements. It uses > a message based protocol over GLINK for communication with the OS, hence > the name. > > The driver implemented provides the rpmsg device for communication and > uses auxilirary bus to spawn off individual devices in respsective typos: auxiliary respective > subsystem. The auxilirary devices are spawned off from a auxiliary > platform_device, so that the drm_bridge is available early, to allow the > DisplayPort driver to probe even before the remoteproc has spun up. > (...) > + > +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) > +{ > + platform_driver_unregister(&pmic_glink_driver); > + unregister_rpmsg_driver(&pmic_glink_rpmsg_driver); Shouldn't this be in reversed order of init()? So first unregister rpmsg, then platform driver. > +}; > +module_exit(pmic_glink_exit); > + > +MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h > new file mode 100644 > index 000000000000..40470f8dfc1e > --- /dev/null > +++ b/include/linux/soc/qcom/pmic_glink.h > @@ -0,0 +1,32 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Copyright (c) 2022, Linaro Ltd > + */ > +#ifndef __PMIC_GLINK_H__ I propose more detailed guard: __SOC_QCOM_PMIC_GLINK_H__ > +#define __PMIC_GLINK_H__ Best regards, Krzysztof
On Wed, Aug 17, 2022 at 08:15:10PM -0700, Bjorn Andersson wrote: > The PMIC GLINK service runs on one of the co-processors of some modern > Qualcomm platforms and implements USB-C and battery managements. It uses > a message based protocol over GLINK for communication with the OS, hence > the name. > > The driver implemented provides the rpmsg device for communication and > uses auxilirary bus to spawn off individual devices in respsective > subsystem. The auxilirary devices are spawned off from a > platform_device, so that the drm_bridge is available early, to allow the > DisplayPort driver to probe even before the remoteproc has spun up. > > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> > --- <snip> > diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c > new file mode 100644 > index 000000000000..d42127521eca > --- /dev/null > +++ b/drivers/soc/qcom/pmic_glink.c > @@ -0,0 +1,336 @@ <snip> > + > +static void _devm_pmic_glink_release_client(struct device *dev, void *res) > +{ > + struct pmic_glink_client *client = *(struct pmic_glink_client **)res; As Eric Chanudet pointed out to me, this should be: struct pmic_glink_client *client = (struct pmic_glink_client *)res; Otherwise you get a splat like below (which somehow resulted in my panel output not to work on my x13s... not sure of the connection there, and is easily reproducible with a probe deferal or qcom_battmgr unload): Unable to handle kernel NULL pointer dereference at virtual address 0000000000000958 Mem abort info: ESR = 0x0000000096000004 EC = 0x25: DABT (current EL), IL = 32 bits ESR = 0x0000000096000004 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x04: level 0 translation fault Data abort info: ISV = 0, ISS = 0x00000004 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgdp=0000000106b92000 [0000000000000958] pgd=0000000000000000, p4d=0000000000000000 Internal error: Oops: 96000004 [#1] PREEMPT SMP Modules linked in: llcc_qcom qcom_battmgr aes_ce_blk pmic_glink_altmode aes_ce_cipher ghash_ce gf128mul sha2_ce sha256_arm64 sha1_ce gpio_sbu_mux pmic_glink gpio_keys autofs4 CPU: 2 PID: 182 Comm: kworker/u16:5 Not tainted 6.0.0-rc6 #29 Hardware name: LENOVO 21BX0016US/21BX0016US, BIOS N3HET47W (1.19 ) 07/04/2022 Workqueue: events_unbound deferred_probe_work_func pstate: 80400005 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : mutex_lock+0x1c/0x60 lr : _devm_pmic_glink_release_client+0x2c/0x74 [pmic_glink] sp : ffff80000c553970 x29: ffff80000c553970 x28: 0000000000000000 x27: 0000000000000000 x26: ffffc297e181e0e8 x25: ffffc297e181d000 x24: ffffc2984efd9a80 x23: ffffc2984ea7a008 x22: ffff1738863cc3a0 x21: ffff80000c553a28 x20: 0000000000000958 x19: ffff1738863cc9f8 x18: ffffffffffffffff x17: 0000000000000000 x16: ffffc2984e1bb110 x15: 61622d6d6f63713d x14: ffffc2984f3b23e0 x13: 554e514553007972 x12: 0000000000000000 x11: 00313731333d4d55 x10: 0000000000000000 x9 : ffffc297e181d1cc x8 : ffff80000c553910 x7 : 0000000000000000 x6 : 0000000080200016 x5 : 0000000000000038 x4 : 0000000000000000 x3 : 0000000000000958 x2 : ffff17388522c100 x1 : 0000000000000000 x0 : 0000000000000958 Call trace: mutex_lock+0x1c/0x60 release_nodes+0x68/0x100 devres_release_all+0x94/0xf0 device_unbind_cleanup+0x20/0x70 device_release_driver_internal+0x214/0x260 device_release_driver+0x20/0x30 bus_remove_device+0xdc/0x170 device_del+0x178/0x3ac pmic_glink_probe+0x1e8/0x240 [pmic_glink] platform_probe+0x70/0xcc really_probe+0xc8/0x3e0 __driver_probe_device+0x84/0x190 driver_probe_device+0x44/0x100 __device_attach_driver+0xc4/0x160 bus_for_each_drv+0x84/0xe0 __device_attach+0xa4/0x1c4 device_initial_probe+0x1c/0x30 bus_probe_device+0xa4/0xb0 deferred_probe_work_func+0xc0/0x114 process_one_work+0x1ec/0x470 worker_thread+0x74/0x410 kthread+0xfc/0x110 ret_from_fork+0x10/0x20 Code: d5384102 d503201f d2800001 aa0103e4 (c8e47c02) ---[ end trace 0000000000000000 ]--- All credit to Eric[0] on that, I'm just tying up loose ends. [0] https://gitlab.com/ahalaney/linux/-/commit/1819fbccd03de430d9fd4c58ded35f5be83e9aa8 Thanks, Andrew
> +static struct pmic_glink *__pmic_glink; > +static DEFINE_MUTEX(__pmic_glink_lock); Having this global device and lock to update pmic_glink struct pointer might not work well if there are multiple pmic_glink devices that corresponds to multiple pmic_glink channels (or rpmsg devices). This is fine for the primary pmic_glink channel that supports mission mode clients. However, to support debugging, there could be a secondary pmic_glink channel and some clients under it. > +struct pmic_glink_client { > + struct list_head node; > + > + struct pmic_glink *pmic; It would be good to name this pointer as "pg" or something.
On Wed, Aug 17, 2022 at 08:15:10PM -0700, Bjorn Andersson wrote: > The PMIC GLINK service runs on one of the co-processors of some modern > Qualcomm platforms and implements USB-C and battery managements. It uses > a message based protocol over GLINK for communication with the OS, hence > the name. > > The driver implemented provides the rpmsg device for communication and > uses auxilirary bus to spawn off individual devices in respsective > subsystem. The auxilirary devices are spawned off from a > platform_device, so that the drm_bridge is available early, to allow the > DisplayPort driver to probe even before the remoteproc has spun up. > > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> > --- > drivers/soc/qcom/Kconfig | 14 ++ > drivers/soc/qcom/Makefile | 1 + > drivers/soc/qcom/pmic_glink.c | 336 ++++++++++++++++++++++++++++ > include/linux/soc/qcom/pmic_glink.h | 32 +++ > 4 files changed, 383 insertions(+) > create mode 100644 drivers/soc/qcom/pmic_glink.c > create mode 100644 include/linux/soc/qcom/pmic_glink.h > > diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig > index e0d7a5459562..2289f5e0d5ad 100644 > --- a/drivers/soc/qcom/Kconfig > +++ b/drivers/soc/qcom/Kconfig > @@ -91,6 +91,20 @@ config QCOM_PDR_HELPERS > tristate > select QCOM_QMI_HELPERS > > +config QCOM_PMIC_GLINK > + tristate "Qualcomm PMIC GLINK driver" > + depends on RPMSG > + depends on TYPEC > + depends on DRM You should add select AUXILIARY_BUS here as this driver will not compile without it. Then you can drop the corresponding select from the battery driver. > + 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. Johan
[ Resending to Bjorn's current address. ] On Wed, Aug 17, 2022 at 08:15:10PM -0700, Bjorn Andersson wrote: > The PMIC GLINK service runs on one of the co-processors of some modern > Qualcomm platforms and implements USB-C and battery managements. It uses > a message based protocol over GLINK for communication with the OS, hence > the name. > > The driver implemented provides the rpmsg device for communication and > uses auxilirary bus to spawn off individual devices in respsective > subsystem. The auxilirary devices are spawned off from a > platform_device, so that the drm_bridge is available early, to allow the > DisplayPort driver to probe even before the remoteproc has spun up. > > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> > --- > drivers/soc/qcom/Kconfig | 14 ++ > drivers/soc/qcom/Makefile | 1 + > drivers/soc/qcom/pmic_glink.c | 336 ++++++++++++++++++++++++++++ > include/linux/soc/qcom/pmic_glink.h | 32 +++ > 4 files changed, 383 insertions(+) > create mode 100644 drivers/soc/qcom/pmic_glink.c > create mode 100644 include/linux/soc/qcom/pmic_glink.h > > diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig > index e0d7a5459562..2289f5e0d5ad 100644 > --- a/drivers/soc/qcom/Kconfig > +++ b/drivers/soc/qcom/Kconfig > @@ -91,6 +91,20 @@ config QCOM_PDR_HELPERS > tristate > select QCOM_QMI_HELPERS > > +config QCOM_PMIC_GLINK > + tristate "Qualcomm PMIC GLINK driver" > + depends on RPMSG > + depends on TYPEC > + depends on DRM You should add select AUXILIARY_BUS here as this driver will not compile without it. Then you can drop the corresponding select from the battery driver. > + 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. Johan
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index e0d7a5459562..2289f5e0d5ad 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -91,6 +91,20 @@ config QCOM_PDR_HELPERS tristate select QCOM_QMI_HELPERS +config QCOM_PMIC_GLINK + tristate "Qualcomm PMIC GLINK driver" + depends on RPMSG + depends on TYPEC + depends on DRM + 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 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index d66604aff2b0..fbbd1231e554 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -8,6 +8,7 @@ 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_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c new file mode 100644 index 000000000000..d42127521eca --- /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 *pmic; + 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->pmic; + + 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->pmic = 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->pmic; + + 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) +{ + platform_driver_unregister(&pmic_glink_driver); + unregister_rpmsg_driver(&pmic_glink_rpmsg_driver); +}; +module_exit(pmic_glink_exit); + +MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h new file mode 100644 index 000000000000..40470f8dfc1e --- /dev/null +++ b/include/linux/soc/qcom/pmic_glink.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022, Linaro Ltd + */ +#ifndef __PMIC_GLINK_H__ +#define __PMIC_GLINK_H__ + +struct pmic_glink; +struct pmic_glink_client; + +#define PMIC_GLINK_OWNER_BATTMGR 32778 +#define PMIC_GLINK_OWNER_USBC 32779 +#define PMIC_GLINK_OWNER_USBC_PAN 32780 + +#define PMIC_GLINK_REQ_RESP 1 +#define PMIC_GLINK_NOTIFY 2 + +struct pmic_glink_hdr { + __le32 owner; + __le32 type; + __le32 opcode; +}; + +int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len); + +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); + +#endif
The PMIC GLINK service runs on one of the co-processors of some modern Qualcomm platforms and implements USB-C and battery managements. It uses a message based protocol over GLINK for communication with the OS, hence the name. The driver implemented provides the rpmsg device for communication and uses auxilirary bus to spawn off individual devices in respsective subsystem. The auxilirary devices are spawned off from a platform_device, so that the drm_bridge is available early, to allow the DisplayPort driver to probe even before the remoteproc has spun up. Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/Kconfig | 14 ++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/pmic_glink.c | 336 ++++++++++++++++++++++++++++ include/linux/soc/qcom/pmic_glink.h | 32 +++ 4 files changed, 383 insertions(+) create mode 100644 drivers/soc/qcom/pmic_glink.c create mode 100644 include/linux/soc/qcom/pmic_glink.h