Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2019-2022, The Linux Foundation. All rights reserved.
  4 * Copyright (c) 2023, Linaro Ltd
  5 */
  6#include <linux/of_device.h>
  7#include <linux/module.h>
  8#include <linux/platform_device.h>
  9#include <linux/rpmsg.h>
 10#include <linux/slab.h>
 11#include <linux/soc/qcom/pdr.h>
 12#include <linux/debugfs.h>
 13
 14#define CREATE_TRACE_POINTS
 15#include "pmic_pdcharger_ulog.h"
 16
 17#define MSG_OWNER_CHG_ULOG		32778
 18#define MSG_TYPE_REQ_RESP		1
 19
 20#define GET_CHG_ULOG_REQ		0x18
 21#define SET_CHG_ULOG_PROP_REQ		0x19
 22
 23#define LOG_DEFAULT_TIME_MS		1000
 24
 25#define MAX_ULOG_SIZE			8192
 26
 27struct pmic_pdcharger_ulog_hdr {
 28	__le32 owner;
 29	__le32 type;
 30	__le32 opcode;
 31};
 32
 33struct pmic_pdcharger_ulog {
 34	struct rpmsg_device *rpdev;
 35	struct delayed_work ulog_work;
 36};
 37
 38struct get_ulog_req_msg {
 39	struct pmic_pdcharger_ulog_hdr	hdr;
 40	u32				log_size;
 41};
 42
 43struct get_ulog_resp_msg {
 44	struct pmic_pdcharger_ulog_hdr	hdr;
 45	u8				buf[MAX_ULOG_SIZE];
 46};
 47
 48static int pmic_pdcharger_ulog_write_async(struct pmic_pdcharger_ulog *pg, void *data, size_t len)
 49{
 50	return rpmsg_send(pg->rpdev->ept, data, len);
 51}
 52
 53static int pmic_pdcharger_ulog_request(struct pmic_pdcharger_ulog *pg)
 54{
 55	struct get_ulog_req_msg req_msg = {
 56		.hdr = {
 57			.owner = cpu_to_le32(MSG_OWNER_CHG_ULOG),
 58			.type = cpu_to_le32(MSG_TYPE_REQ_RESP),
 59			.opcode = cpu_to_le32(GET_CHG_ULOG_REQ)
 60		},
 61		.log_size = MAX_ULOG_SIZE
 62	};
 63
 64	return pmic_pdcharger_ulog_write_async(pg, &req_msg, sizeof(req_msg));
 65}
 66
 67static void pmic_pdcharger_ulog_work(struct work_struct *work)
 68{
 69	struct pmic_pdcharger_ulog *pg = container_of(work, struct pmic_pdcharger_ulog,
 70						      ulog_work.work);
 71	int rc;
 72
 73	rc = pmic_pdcharger_ulog_request(pg);
 74	if (rc) {
 75		dev_err(&pg->rpdev->dev, "Error requesting ulog, rc=%d\n", rc);
 76		return;
 77	}
 78}
 79
 80static void pmic_pdcharger_ulog_handle_message(struct pmic_pdcharger_ulog *pg,
 81					       struct get_ulog_resp_msg *resp_msg,
 82					       size_t len)
 83{
 84	char *token, *buf = resp_msg->buf;
 85
 86	if (len != sizeof(*resp_msg)) {
 87		dev_err(&pg->rpdev->dev, "Expected data length: %zu, received: %zu\n",
 88			sizeof(*resp_msg), len);
 89		return;
 90	}
 91
 92	buf[MAX_ULOG_SIZE - 1] = '\0';
 93
 94	do {
 95		token = strsep((char **)&buf, "\n");
 96		if (token && strlen(token))
 97			trace_pmic_pdcharger_ulog_msg(token);
 98	} while (token);
 99}
100
101static int pmic_pdcharger_ulog_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
102					      int len, void *priv, u32 addr)
103{
104	struct pmic_pdcharger_ulog *pg = dev_get_drvdata(&rpdev->dev);
105	struct pmic_pdcharger_ulog_hdr *hdr = data;
106	u32 opcode;
107
108	opcode = le32_to_cpu(hdr->opcode);
109
110	switch (opcode) {
111	case GET_CHG_ULOG_REQ:
112		schedule_delayed_work(&pg->ulog_work, msecs_to_jiffies(LOG_DEFAULT_TIME_MS));
113		pmic_pdcharger_ulog_handle_message(pg, data, len);
114		break;
115	default:
116		dev_err(&pg->rpdev->dev, "Unknown opcode %u\n", opcode);
117		break;
118	}
119
120	return 0;
121}
122
123static int pmic_pdcharger_ulog_rpmsg_probe(struct rpmsg_device *rpdev)
124{
125	struct pmic_pdcharger_ulog *pg;
126	struct device *dev = &rpdev->dev;
127
128	pg = devm_kzalloc(dev, sizeof(*pg), GFP_KERNEL);
129	if (!pg)
130		return -ENOMEM;
131
132	pg->rpdev = rpdev;
133	INIT_DELAYED_WORK(&pg->ulog_work, pmic_pdcharger_ulog_work);
134
135	dev_set_drvdata(dev, pg);
136
137	pmic_pdcharger_ulog_request(pg);
138
139	return 0;
140}
141
142static void pmic_pdcharger_ulog_rpmsg_remove(struct rpmsg_device *rpdev)
143{
144	struct pmic_pdcharger_ulog *pg = dev_get_drvdata(&rpdev->dev);
145
146	cancel_delayed_work_sync(&pg->ulog_work);
147}
148
149static const struct rpmsg_device_id pmic_pdcharger_ulog_rpmsg_id_match[] = {
150	{ "PMIC_LOGS_ADSP_APPS" },
151	{}
152};
153/*
154 * No MODULE_DEVICE_TABLE intentionally: that's a debugging module, to be
155 * loaded manually only.
156 */
157
158static struct rpmsg_driver pmic_pdcharger_ulog_rpmsg_driver = {
159	.probe = pmic_pdcharger_ulog_rpmsg_probe,
160	.remove = pmic_pdcharger_ulog_rpmsg_remove,
161	.callback = pmic_pdcharger_ulog_rpmsg_callback,
162	.id_table = pmic_pdcharger_ulog_rpmsg_id_match,
163	.drv  = {
164		.name  = "qcom_pmic_pdcharger_ulog_rpmsg",
165	},
166};
167
168module_rpmsg_driver(pmic_pdcharger_ulog_rpmsg_driver);
169MODULE_DESCRIPTION("Qualcomm PMIC ChargerPD ULOG driver");
170MODULE_LICENSE("GPL");