brintos

brintos / linux-shallow public Read only

0
0
Text · 10.5 KiB · baa4ac6 Raw
436 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.4 * Copyright (c) 2022, Linaro Ltd5 */6#include <linux/auxiliary_bus.h>7#include <linux/delay.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11#include <linux/rpmsg.h>12#include <linux/slab.h>13#include <linux/soc/qcom/pdr.h>14#include <linux/soc/qcom/pmic_glink.h>15#include <linux/spinlock.h>16 17#define PMIC_GLINK_SEND_TIMEOUT (5 * HZ)18 19enum {20	PMIC_GLINK_CLIENT_BATT = 0,21	PMIC_GLINK_CLIENT_ALTMODE,22	PMIC_GLINK_CLIENT_UCSI,23};24 25struct pmic_glink {26	struct device *dev;27	struct pdr_handle *pdr;28 29	struct rpmsg_endpoint *ept;30 31	unsigned long client_mask;32 33	struct auxiliary_device altmode_aux;34	struct auxiliary_device ps_aux;35	struct auxiliary_device ucsi_aux;36 37	/* serializing client_state and pdr_state updates */38	struct mutex state_lock;39	unsigned int client_state;40	unsigned int pdr_state;41 42	/* serializing clients list updates */43	spinlock_t client_lock;44	struct list_head clients;45};46 47static struct pmic_glink *__pmic_glink;48static DEFINE_MUTEX(__pmic_glink_lock);49 50struct pmic_glink_client {51	struct list_head node;52 53	struct pmic_glink *pg;54	unsigned int id;55 56	void (*cb)(const void *data, size_t len, void *priv);57	void (*pdr_notify)(void *priv, int state);58	void *priv;59};60 61static void _devm_pmic_glink_release_client(struct device *dev, void *res)62{63	struct pmic_glink_client *client = (struct pmic_glink_client *)res;64	struct pmic_glink *pg = client->pg;65	unsigned long flags;66 67	spin_lock_irqsave(&pg->client_lock, flags);68	list_del(&client->node);69	spin_unlock_irqrestore(&pg->client_lock, flags);70}71 72struct pmic_glink_client *devm_pmic_glink_client_alloc(struct device *dev,73						       unsigned int id,74						       void (*cb)(const void *, size_t, void *),75						       void (*pdr)(void *, int),76						       void *priv)77{78	struct pmic_glink_client *client;79	struct pmic_glink *pg = dev_get_drvdata(dev->parent);80 81	client = devres_alloc(_devm_pmic_glink_release_client, sizeof(*client), GFP_KERNEL);82	if (!client)83		return ERR_PTR(-ENOMEM);84 85	client->pg = pg;86	client->id = id;87	client->cb = cb;88	client->pdr_notify = pdr;89	client->priv = priv;90	INIT_LIST_HEAD(&client->node);91 92	devres_add(dev, client);93 94	return client;95}96EXPORT_SYMBOL_GPL(devm_pmic_glink_client_alloc);97 98void pmic_glink_client_register(struct pmic_glink_client *client)99{100	struct pmic_glink *pg = client->pg;101	unsigned long flags;102 103	mutex_lock(&pg->state_lock);104	spin_lock_irqsave(&pg->client_lock, flags);105 106	list_add(&client->node, &pg->clients);107	client->pdr_notify(client->priv, pg->client_state);108 109	spin_unlock_irqrestore(&pg->client_lock, flags);110	mutex_unlock(&pg->state_lock);111 112}113EXPORT_SYMBOL_GPL(pmic_glink_client_register);114 115int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len)116{117	struct pmic_glink *pg = client->pg;118	bool timeout_reached = false;119	unsigned long start;120	int ret;121 122	mutex_lock(&pg->state_lock);123	if (!pg->ept) {124		ret = -ECONNRESET;125	} else {126		start = jiffies;127		for (;;) {128			ret = rpmsg_send(pg->ept, data, len);129			if (ret != -EAGAIN)130				break;131 132			if (timeout_reached) {133				ret = -ETIMEDOUT;134				break;135			}136 137			usleep_range(1000, 5000);138			timeout_reached = time_after(jiffies, start + PMIC_GLINK_SEND_TIMEOUT);139		}140	}141	mutex_unlock(&pg->state_lock);142 143	return ret;144}145EXPORT_SYMBOL_GPL(pmic_glink_send);146 147static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data,148				     int len, void *priv, u32 addr)149{150	struct pmic_glink_client *client;151	struct pmic_glink_hdr *hdr;152	struct pmic_glink *pg = dev_get_drvdata(&rpdev->dev);153	unsigned long flags;154 155	if (len < sizeof(*hdr)) {156		dev_warn(pg->dev, "ignoring truncated message\n");157		return 0;158	}159 160	hdr = data;161 162	spin_lock_irqsave(&pg->client_lock, flags);163	list_for_each_entry(client, &pg->clients, node) {164		if (client->id == le32_to_cpu(hdr->owner))165			client->cb(data, len, client->priv);166	}167	spin_unlock_irqrestore(&pg->client_lock, flags);168 169	return 0;170}171 172static void pmic_glink_aux_release(struct device *dev) {}173 174static int pmic_glink_add_aux_device(struct pmic_glink *pg,175				     struct auxiliary_device *aux,176				     const char *name)177{178	struct device *parent = pg->dev;179	int ret;180 181	aux->name = name;182	aux->dev.parent = parent;183	aux->dev.release = pmic_glink_aux_release;184	device_set_of_node_from_dev(&aux->dev, parent);185	ret = auxiliary_device_init(aux);186	if (ret)187		return ret;188 189	ret = auxiliary_device_add(aux);190	if (ret)191		auxiliary_device_uninit(aux);192 193	return ret;194}195 196static void pmic_glink_del_aux_device(struct pmic_glink *pg,197				      struct auxiliary_device *aux)198{199	auxiliary_device_delete(aux);200	auxiliary_device_uninit(aux);201}202 203static void pmic_glink_state_notify_clients(struct pmic_glink *pg)204{205	struct pmic_glink_client *client;206	unsigned int new_state = pg->client_state;207	unsigned long flags;208 209	if (pg->client_state != SERVREG_SERVICE_STATE_UP) {210		if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept)211			new_state = SERVREG_SERVICE_STATE_UP;212	} else {213		if (pg->pdr_state == SERVREG_SERVICE_STATE_DOWN || !pg->ept)214			new_state = SERVREG_SERVICE_STATE_DOWN;215	}216 217	if (new_state != pg->client_state) {218		spin_lock_irqsave(&pg->client_lock, flags);219		list_for_each_entry(client, &pg->clients, node)220			client->pdr_notify(client->priv, new_state);221		spin_unlock_irqrestore(&pg->client_lock, flags);222		pg->client_state = new_state;223	}224}225 226static void pmic_glink_pdr_callback(int state, char *svc_path, void *priv)227{228	struct pmic_glink *pg = priv;229 230	mutex_lock(&pg->state_lock);231	pg->pdr_state = state;232 233	pmic_glink_state_notify_clients(pg);234	mutex_unlock(&pg->state_lock);235}236 237static int pmic_glink_rpmsg_probe(struct rpmsg_device *rpdev)238{239	struct pmic_glink *pg = __pmic_glink;240	int ret = 0;241 242	mutex_lock(&__pmic_glink_lock);243	if (!pg) {244		ret = dev_err_probe(&rpdev->dev, -ENODEV, "no pmic_glink device to attach to\n");245		goto out_unlock;246	}247 248	dev_set_drvdata(&rpdev->dev, pg);249 250	mutex_lock(&pg->state_lock);251	pg->ept = rpdev->ept;252	pmic_glink_state_notify_clients(pg);253	mutex_unlock(&pg->state_lock);254 255out_unlock:256	mutex_unlock(&__pmic_glink_lock);257	return ret;258}259 260static void pmic_glink_rpmsg_remove(struct rpmsg_device *rpdev)261{262	struct pmic_glink *pg;263 264	mutex_lock(&__pmic_glink_lock);265	pg = __pmic_glink;266	if (!pg)267		goto out_unlock;268 269	mutex_lock(&pg->state_lock);270	pg->ept = NULL;271	pmic_glink_state_notify_clients(pg);272	mutex_unlock(&pg->state_lock);273out_unlock:274	mutex_unlock(&__pmic_glink_lock);275}276 277static const struct rpmsg_device_id pmic_glink_rpmsg_id_match[] = {278	{ "PMIC_RTR_ADSP_APPS" },279	{}280};281 282static struct rpmsg_driver pmic_glink_rpmsg_driver = {283	.probe = pmic_glink_rpmsg_probe,284	.remove = pmic_glink_rpmsg_remove,285	.callback = pmic_glink_rpmsg_callback,286	.id_table = pmic_glink_rpmsg_id_match,287	.drv  = {288		.name  = "qcom_pmic_glink_rpmsg",289	},290};291 292static int pmic_glink_probe(struct platform_device *pdev)293{294	const unsigned long *match_data;295	struct pdr_service *service;296	struct pmic_glink *pg;297	int ret;298 299	pg = devm_kzalloc(&pdev->dev, sizeof(*pg), GFP_KERNEL);300	if (!pg)301		return -ENOMEM;302 303	dev_set_drvdata(&pdev->dev, pg);304 305	pg->dev = &pdev->dev;306 307	INIT_LIST_HEAD(&pg->clients);308	spin_lock_init(&pg->client_lock);309	mutex_init(&pg->state_lock);310 311	match_data = (unsigned long *)of_device_get_match_data(&pdev->dev);312	if (!match_data)313		return -EINVAL;314 315	pg->client_mask = *match_data;316 317	pg->pdr = pdr_handle_alloc(pmic_glink_pdr_callback, pg);318	if (IS_ERR(pg->pdr)) {319		ret = dev_err_probe(&pdev->dev, PTR_ERR(pg->pdr),320				    "failed to initialize pdr\n");321		return ret;322	}323 324	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) {325		ret = pmic_glink_add_aux_device(pg, &pg->ucsi_aux, "ucsi");326		if (ret)327			goto out_release_pdr_handle;328	}329	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE)) {330		ret = pmic_glink_add_aux_device(pg, &pg->altmode_aux, "altmode");331		if (ret)332			goto out_release_ucsi_aux;333	}334	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) {335		ret = pmic_glink_add_aux_device(pg, &pg->ps_aux, "power-supply");336		if (ret)337			goto out_release_altmode_aux;338	}339 340	service = pdr_add_lookup(pg->pdr, "tms/servreg", "msm/adsp/charger_pd");341	if (IS_ERR(service)) {342		ret = dev_err_probe(&pdev->dev, PTR_ERR(service),343				    "failed adding pdr lookup for charger_pd\n");344		goto out_release_aux_devices;345	}346 347	mutex_lock(&__pmic_glink_lock);348	__pmic_glink = pg;349	mutex_unlock(&__pmic_glink_lock);350 351	return 0;352 353out_release_aux_devices:354	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_BATT))355		pmic_glink_del_aux_device(pg, &pg->ps_aux);356out_release_altmode_aux:357	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE))358		pmic_glink_del_aux_device(pg, &pg->altmode_aux);359out_release_ucsi_aux:360	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI))361		pmic_glink_del_aux_device(pg, &pg->ucsi_aux);362out_release_pdr_handle:363	pdr_handle_release(pg->pdr);364 365	return ret;366}367 368static void pmic_glink_remove(struct platform_device *pdev)369{370	struct pmic_glink *pg = dev_get_drvdata(&pdev->dev);371 372	pdr_handle_release(pg->pdr);373 374	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_BATT))375		pmic_glink_del_aux_device(pg, &pg->ps_aux);376	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE))377		pmic_glink_del_aux_device(pg, &pg->altmode_aux);378	if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI))379		pmic_glink_del_aux_device(pg, &pg->ucsi_aux);380 381	mutex_lock(&__pmic_glink_lock);382	__pmic_glink = NULL;383	mutex_unlock(&__pmic_glink_lock);384}385 386static const unsigned long pmic_glink_sc8280xp_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |387							     BIT(PMIC_GLINK_CLIENT_ALTMODE);388 389static const unsigned long pmic_glink_sm8450_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |390							   BIT(PMIC_GLINK_CLIENT_ALTMODE) |391							   BIT(PMIC_GLINK_CLIENT_UCSI);392 393static const struct of_device_id pmic_glink_of_match[] = {394	{ .compatible = "qcom,sc8280xp-pmic-glink", .data = &pmic_glink_sc8280xp_client_mask },395	{ .compatible = "qcom,pmic-glink", .data = &pmic_glink_sm8450_client_mask },396	{}397};398MODULE_DEVICE_TABLE(of, pmic_glink_of_match);399 400static struct platform_driver pmic_glink_driver = {401	.probe = pmic_glink_probe,402	.remove_new = pmic_glink_remove,403	.driver = {404		.name = "qcom_pmic_glink",405		.of_match_table = pmic_glink_of_match,406	},407};408 409static int pmic_glink_init(void)410{411	int ret;412 413	ret = platform_driver_register(&pmic_glink_driver);414	if (ret < 0)415		return ret;416 417	ret = register_rpmsg_driver(&pmic_glink_rpmsg_driver);418	if (ret < 0) {419		platform_driver_unregister(&pmic_glink_driver);420		return ret;421	}422 423	return 0;424}425module_init(pmic_glink_init);426 427static void pmic_glink_exit(void)428{429	unregister_rpmsg_driver(&pmic_glink_rpmsg_driver);430	platform_driver_unregister(&pmic_glink_driver);431}432module_exit(pmic_glink_exit);433 434MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver");435MODULE_LICENSE("GPL");436