Create bm_adsp_ulog module

1. Provide debugfs interface to collect battery manager ADSP log  in AP side.
2. Provide debugfs interfaces to configure log level and category for log mask.
3. Provide exported APIs for other Moto battery charger DLKM to get battery
charger ADSP log and output to Kernel log or IPC logging.

Change-Id: I93cb18542414b21084a09218366d6d675beeb807
Signed-off-by: yanyh2 <yanyh2@motorola.com>
Reviewed-on: https://gerrit.mot.com/1971297
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Jianqi Yang <yangj@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
yanyh2 2021-06-07 01:16:47 +08:00 • committed by Yonghua Yan
commit d9aca754c7
6 changed files with 648 additions and 0 deletions

View file

@ -0,0 +1,9 @@
DLKM_DIR := motorola/kernel/modules
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := bm_adsp_ulog.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki
include $(DLKM_DIR)/AndroidKernelModule.mk

View file

@ -0,0 +1,6 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include
obj-m += bm_adsp_ulog.o

View file

@ -0,0 +1,10 @@
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
modules_install:
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
clean:
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean

View file

@ -0,0 +1,39 @@
Motorola Mobility. Battery Manager ULOG Specific Bindings
Why we need adsp battery manager log support in AP side?
1. Only diag_mdlog in bug2go can help to collect ADSP battery manager log.
2. ADSP battery manager mdlog is not enabled by default in Bug2Go configuration
and we have to enable it manually.
3. Diag mdlog has to be pulled out from phone and use QXDM tool to parse the log
even if the ADSP battery manger log is enabled in diag log configuration of bug2go.
4. Factory and user version will never use Bug2Go, so diag_mdlog may not be available.
What the new DLKM bm_adsp_ulog.ko benefits us?
1. Provide debugfs interface to collect battery manager ADSP log in AP side.
2. Provide debugfs interfaces to configure log level and category for log mask.
3. Provide exported APIs for other Moto battery charger DLKM to get battery charger
ADSP log and output to Kernel log or IPC logging.
Module Name: bm_adsp_ulog.ko
Log Tag: BM_ULOG
IPC Log: /d/ipc_logging/bm_ulog/log
Support Platform: SM8350/SM7325/SM8450
devicetree properties description:
- categories
Usage: optional
Value Type: <u64>
Definition: 64 bits ulog mask only to capture the bm ulog type in the mask.
- level
Usage: optional
Value Type: <u32>
Definition: BM ulog level to limit the bm ulog
Example:
bm_adsp_ulog: bm_adsp_ulog {
compatible = "qcom,bm-adsp-ulog";
categories = <0xFFBFFFFF>;
level = <3>;
};

View file

@ -0,0 +1,502 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "BM_ULOG: %s: " fmt, __func__
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/ipc_logging.h>
#include <linux/rpmsg.h>
#include <linux/soc/qcom/pmic_glink.h>
#include <linux/power/bm_adsp_ulog.h>
/* owner/type/opcodes for battery charger */
#define MSG_OWNER_BC 32778
#define MSG_TYPE_REQ_RESP 1
#define BM_ULOG_GET 0x18
#define BM_ULOG_PROP_SET 0x19
/* Generic definitions */
#define BM_ULOG_WAIT_TIME_MS 5000
#define MAX_ULOG_READ_BUFFER_SIZE 8192
#define BM_ULOG_PAGES (50)
#define bm_info(bmdev, fmt, ...) \
do { \
pr_info(fmt, ##__VA_ARGS__); \
ipc_log_string(bmdev->ipc_log, fmt, ##__VA_ARGS__); \
} while (0)
#define bm_dbg(bmdev, fmt, ...) \
do { \
if (*bmdev->debug_enabled) \
pr_info(fmt, ##__VA_ARGS__); \
else \
pr_debug(fmt, ##__VA_ARGS__); \
ipc_log_string(bmdev->ipc_log, fmt, ##__VA_ARGS__); \
} while (0)
static bool debug_enabled;
module_param(debug_enabled, bool, 0600);
MODULE_PARM_DESC(debug_enabled, "Enable ulog to Kernel debug log");
struct bm_ulog_prop_req {
struct pmic_glink_hdr hdr;
u64 categories;
u32 level;
};
struct bm_ulog_req {
struct pmic_glink_hdr hdr;
u32 max_logsize;
};
struct bm_ulog_resp {
struct pmic_glink_hdr hdr;
char read_buffer[MAX_ULOG_READ_BUFFER_SIZE];
};
struct bm_ulog_dev {
struct device *dev;
struct pmic_glink_client *client;
struct mutex lock;
struct completion ack;
u32 level;
u64 categories;
struct dentry *debugfs_dir;
bool *debug_enabled;
void *ipc_log;
char ulog_buffer[MAX_ULOG_READ_BUFFER_SIZE];
};
static struct bm_ulog_dev *g_bmdev = NULL;
static int bm_ulog_write(struct bm_ulog_dev *bmdev, void *data, size_t len)
{
int rc;
mutex_lock(&bmdev->lock);
reinit_completion(&bmdev->ack);
rc = pmic_glink_write(bmdev->client, data, len);
if (!rc) {
rc = wait_for_completion_timeout(&bmdev->ack,
msecs_to_jiffies(BM_ULOG_WAIT_TIME_MS));
if (!rc) {
pr_err("Error, timed out sending message\n");
mutex_unlock(&bmdev->lock);
return -ETIMEDOUT;
}
rc = 0;
}
mutex_unlock(&bmdev->lock);
return rc;
}
static int bm_ulog_callback(void *priv, void *data, size_t len)
{
struct pmic_glink_hdr *hdr = data;
struct bm_ulog_dev *bmdev = priv;
pr_debug("owner: %u type: %u opcode: %#x len: %zu\n", hdr->owner,
hdr->type, hdr->opcode, len);
switch (hdr->opcode) {
case BM_ULOG_PROP_SET:
complete(&bmdev->ack);
break;
case BM_ULOG_GET:
if (len != sizeof(struct bm_ulog_resp)) {
pr_err("Incorrect len %zu for bm ulog resp\n", len);
break;
}
memcpy(bmdev->ulog_buffer,
((struct bm_ulog_resp *)data)->read_buffer,
MAX_ULOG_READ_BUFFER_SIZE);
complete(&bmdev->ack);
break;
default:
pr_err("Unknown opcode %u\n", hdr->opcode);
break;
}
return 0;
}
static int bm_ulog_set_mask(struct bm_ulog_dev *bmdev,
enum bm_ulog_category_bitmap categories,
enum bm_ulog_level_type level)
{
int rc;
struct bm_ulog_prop_req prop_req = { { 0 } };
if (level > BM_LOG_LEVEL_ALL_LOGS)
level = BM_LOG_LEVEL_ALL_LOGS;
prop_req.level = level;
prop_req.categories = categories;
prop_req.hdr.owner = MSG_OWNER_BC;
prop_req.hdr.type = MSG_TYPE_REQ_RESP;
prop_req.hdr.opcode = BM_ULOG_PROP_SET;
rc = bm_ulog_write(bmdev, &prop_req, sizeof(prop_req));
if (rc) {
pr_err("BM ulog set mask failed\n");
return rc;
}
bmdev->level = level;
bmdev->categories = categories;
pr_debug("BM ulog categories: 0x%llx, level: %d\n",
(u64)categories, level);
return 0;
}
static int bm_ulog_request_log(struct bm_ulog_dev *bmdev, u32 size)
{
int rc;
u32 max_logsize;
struct bm_ulog_req ulog_req = { { 0 } };
if (size > 0 && size < MAX_ULOG_READ_BUFFER_SIZE)
max_logsize = size;
else
max_logsize = MAX_ULOG_READ_BUFFER_SIZE;
ulog_req.hdr.owner = MSG_OWNER_BC;
ulog_req.hdr.type = MSG_TYPE_REQ_RESP;
ulog_req.hdr.opcode = BM_ULOG_GET;
ulog_req.max_logsize = max_logsize;
rc = bm_ulog_write(bmdev, &ulog_req, sizeof(ulog_req));
return rc;
}
int bm_ulog_get_log(char *buf, u32 size)
{
int rc;
struct bm_ulog_dev *bmdev = g_bmdev;
if (!bmdev) {
pr_err("BM ulog has not initialized yet\n");
return -ENODEV;
}
if (!buf) {
pr_err("BM ulog invalid buf=%p\n", buf);
return -EINVAL;
}
if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) {
pr_err("BM ulog invalid size=%d\n", size);
return -EINVAL;
}
rc = bm_ulog_request_log(bmdev, size);
if (rc) {
pr_err("BM ulog failed to request log, rc=%d\n", rc);
return rc;
}
memcpy(buf, bmdev->ulog_buffer, size);
return 0;
}
EXPORT_SYMBOL(bm_ulog_get_log);
int bm_ulog_get_mask_log(enum bm_ulog_category_bitmap categories,
enum bm_ulog_level_type level,
char *buf, u32 size)
{
int rc;
struct bm_ulog_dev *bmdev = g_bmdev;
if (!bmdev) {
pr_err("BM ulog has not initialized yet\n");
return -ENODEV;
}
if (!buf) {
pr_err("BM ulog invalid buf=%p\n", buf);
return -EINVAL;
}
if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) {
pr_err("BM ulog invalid size=%d\n", size);
return -EINVAL;
}
rc = bm_ulog_set_mask(bmdev, categories, level);
if (rc) {
pr_err("BM ulog failed to set mask, rc=%d\n", rc);
return rc;
}
rc = bm_ulog_request_log(bmdev, size);
if (rc) {
pr_err("BM ulog failed to request log, rc=%d\n", rc);
return rc;
}
memcpy(buf, bmdev->ulog_buffer, size);
return 0;
}
EXPORT_SYMBOL(bm_ulog_get_mask_log);
static void bm_ulog_print_buffer(struct bm_ulog_dev *bmdev, u32 size)
{
int i;
int header = 0;
for (i = 0; i < size; i++) {
if (bmdev->ulog_buffer[i] == '\x0a') {
bmdev->ulog_buffer[i] = '\0';
bm_dbg(bmdev, "%s\n", &bmdev->ulog_buffer[header]);
header = i + 1;
} else if (bmdev->ulog_buffer[i] == '\0') {
if (header < i) {
bm_dbg(bmdev, "%s\n", &bmdev->ulog_buffer[header]);
}
break;
}
}
}
int bm_ulog_print_log(u32 size)
{
int rc;
struct bm_ulog_dev *bmdev = g_bmdev;
if (!bmdev) {
pr_err("BM ulog has not initialized yet\n");
return -ENODEV;
}
if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) {
pr_err("BM ulog invalid size=%d\n", size);
return -EINVAL;
}
rc = bm_ulog_request_log(bmdev, size);
if (rc) {
pr_err("BM ulog failed to request log, rc=%d\n", rc);
return rc;
}
bm_ulog_print_buffer(bmdev, size);
return 0;
}
EXPORT_SYMBOL(bm_ulog_print_log);
int bm_ulog_print_mask_log(enum bm_ulog_category_bitmap categories,
enum bm_ulog_level_type level, u32 size)
{
int rc;
struct bm_ulog_dev *bmdev = g_bmdev;
if (!bmdev) {
pr_err("BM ulog has not initialized yet\n");
return -ENODEV;
}
if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) {
pr_err("BM ulog invalid size=%d\n", size);
return -EINVAL;
}
rc = bm_ulog_set_mask(bmdev, categories, level);
if (rc) {
pr_err("BM ulog failed to set mask, rc=%d\n", rc);
return rc;
}
rc = bm_ulog_request_log(bmdev, size);
if (rc) {
pr_err("BM ulog failed to request log, rc=%d\n", rc);
return rc;
}
bm_ulog_print_buffer(bmdev, size);
return 0;
}
EXPORT_SYMBOL(bm_ulog_print_mask_log);
#ifdef CONFIG_DEBUG_FS
static int bm_ulog_dump_show(struct seq_file *s, void *unused)
{
int rc;
struct bm_ulog_dev *bmdev = s->private;
rc = bm_ulog_set_mask(bmdev, bmdev->categories, bmdev->level);
if (rc) {
pr_err("BM ulog failed to set mask, rc=%d\n", rc);
return rc;
}
rc = bm_ulog_request_log(bmdev, MAX_ULOG_READ_BUFFER_SIZE);
if (rc) {
pr_err("BM ulog failed to request log, rc=%d\n", rc);
return rc;
}
seq_puts(s, bmdev->ulog_buffer);
return 0;
}
static int bm_ulog_open(struct inode *inode, struct file *file)
{
return single_open(file, bm_ulog_dump_show, inode->i_private);
}
static const struct file_operations bm_ulog_fops = {
.open = bm_ulog_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void bm_ulog_add_debugfs(struct bm_ulog_dev *bmdev)
{
int rc;
struct dentry *dir, *file;
dir = debugfs_create_dir("bm_ulog", NULL);
if (IS_ERR(dir)) {
rc = PTR_ERR(dir);
pr_err("Failed to create bm ulog debugfs directory, rc=%d\n",
rc);
return;
}
file = debugfs_create_x64("categories", 0664, dir, &bmdev->categories);
if (IS_ERR(file)) {
rc = PTR_ERR(file);
pr_err("Failed to create ulog categories debugfs file, rc=%d\n",
rc);
goto error;
}
file = debugfs_create_x32("level", 0664, dir, &bmdev->level);
if (IS_ERR(file)) {
rc = PTR_ERR(file);
pr_err("Failed to create ulog level debugfs file, rc=%d\n",
rc);
goto error;
}
file = debugfs_create_file("dump", 0444, dir, bmdev,
&bm_ulog_fops);
if (IS_ERR(file)) {
rc = PTR_ERR(file);
pr_err("Failed to create ulog dump debugfs file, rc=%d\n",
rc);
goto error;
}
bmdev->debugfs_dir = dir;
return;
error:
debugfs_remove_recursive(dir);
}
#else
static void bm_ulog_add_debugfs(struct bm_ulog_dev *bmdev) { }
#endif
static int bm_ulog_probe(struct platform_device *pdev)
{
int rc;
struct bm_ulog_dev *bmdev;
struct pmic_glink_client_data client_data = { };
struct device_node *node = pdev->dev.of_node;
bmdev = devm_kzalloc(&pdev->dev, sizeof(*bmdev), GFP_KERNEL);
if (!bmdev)
return -ENOMEM;
rc = of_property_read_u64(node, "categories", &bmdev->categories);
if (rc)
bmdev->categories = BM_ALL;
rc = of_property_read_u32(node, "level", &bmdev->level);
if (rc)
bmdev->level = BM_LOG_LEVEL_INFO;
bmdev->dev = &pdev->dev;
client_data.id = MSG_OWNER_BC;
client_data.name = "battery_manager_adsp_ulog";
client_data.msg_cb = bm_ulog_callback;
client_data.priv = bmdev;
bmdev->client = pmic_glink_register_client(bmdev->dev, &client_data);
if (IS_ERR(bmdev->client)) {
rc = PTR_ERR(bmdev->client);
if (rc != -EPROBE_DEFER)
dev_err(bmdev->dev, "Error in registering with pmic_glink %d\n",
rc);
return rc;
}
mutex_init(&bmdev->lock);
init_completion(&bmdev->ack);
platform_set_drvdata(pdev, bmdev);
bmdev->debug_enabled = &debug_enabled;
g_bmdev = bmdev;
bmdev->ipc_log = ipc_log_context_create(BM_ULOG_PAGES, "bm_ulog", 0);
if (!bmdev->ipc_log)
dev_err(bmdev->dev, "Failed to create ipc log\n");
bm_ulog_print_log(MAX_ULOG_READ_BUFFER_SIZE);
bm_ulog_add_debugfs(bmdev);
bm_info(bmdev, "BM adsp ulog driver initialized successfully\n");
return 0;
}
static int bm_ulog_remove(struct platform_device *pdev)
{
struct bm_ulog_dev *bmdev = platform_get_drvdata(pdev);
int rc;
ipc_log_context_destroy(bmdev->ipc_log);
debugfs_remove_recursive(bmdev->debugfs_dir);
rc = pmic_glink_unregister_client(bmdev->client);
if (rc < 0) {
pr_err("Error unregistering from pmic_glink, rc=%d\n", rc);
return rc;
}
g_bmdev = NULL;
return 0;
}
static const struct of_device_id bm_ulog_match_table[] = {
{ .compatible = "qcom,bm-adsp-ulog" },
{},
};
static struct platform_driver bm_ulog_driver = {
.driver = {
.name = "bm_adsp_ulog",
.of_match_table = bm_ulog_match_table,
},
.probe = bm_ulog_probe,
.remove = bm_ulog_remove,
};
module_platform_driver(bm_ulog_driver);
MODULE_DESCRIPTION("QTI Glink battery manager adsp ulog driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#ifndef __BM_ADSP_ULOG_H__
#define __BM_ADSP_ULOG_H__
/* Generic definitions */
enum bm_ulog_level_type {
BM_LOG_LEVEL_NONE = 0,
BM_LOG_LEVEL_ERR = 1,
BM_LOG_LEVEL_WARN = 2,
BM_LOG_LEVEL_INFO = 3,
BM_LOG_LEVEL_DEBUG = 4,
BM_LOG_LEVEL_ALL_LOGS
};
enum bm_ulog_category_bitmap {
BM_WPP_SELF_HOST = 1ULL << 0,
BM_WPP_DEVICE = 1ULL << 1,
BM_WPP_IOIMPL = 1ULL << 2,
BM_PMIC_WPP_DEVICE = 1ULL << 3,
BM_WPP_NONE = 1ULL << 4,
BM_WPP_SMCHGFGGGE = 1ULL << 5,
BM_WPP_PLATFORM = 1ULL << 6,
BM_WPP_IRQ = 1ULL << 7,
BM_WPP_NOTIFY = 1ULL << 8,
BM_WPP_STATEMACHINE = 1ULL << 9,
BM_WPP_STATEMACHINELEVEL = 1ULL << 10,
BM_WPP_BATTERRHANDLE = 1ULL << 11,
BM_WPP_FGGGE = 1ULL << 12,
BM_WPP_PARALLELCHARGING = 1ULL << 13,
BM_WPP_SMCHG = 1ULL << 14,
BM_WPP_SWJEITA = 1ULL << 15,
BM_WPP_QBG = 1ULL << 16,
BM_QBG = 1ULL << 17,
BM_ADC = 1ULL << 18,
BM_CAD = 1ULL << 19,
BM_PMICPLATFORM = 1ULL << 20,
BM_PMICHAL = 1ULL << 21,
BM_AGGREGATOR = 1ULL << 22,
BM_SUMMARY = 1ULL << 23,
BM_USBC_COMMON = 1ULL << 24,
BM_USBC_DPM = 1ULL << 25,
BM_USBC_LPM = 1ULL << 26,
BM_USBC_PRL = 1ULL << 27,
BM_USBC_PE = 1ULL << 28,
BM_USBC_TCPC = 1ULL << 29,
BM_USBC_TCPCPLATFORM = 1ULL << 30,
BM_USBC_SUMMARY = 1ULL << 31,
BM_LOGCATEGORYMAX = 1ULL << 63,
};
#define BM_USBC_ALL ( \
BM_USBC_COMMON | BM_USBC_DPM | BM_USBC_LPM | BM_USBC_PRL | BM_USBC_PE | \
BM_USBC_TCPC | BM_USBC_TCPCPLATFORM | BM_USBC_SUMMARY)
#define BM_BM_ALL ( \
BM_WPP_SELF_HOST | BM_WPP_DEVICE | BM_WPP_IOIMPL | \
BM_PMIC_WPP_DEVICE | BM_WPP_NONE | BM_WPP_SMCHGFGGGE | \
BM_WPP_PLATFORM | BM_WPP_IRQ | BM_WPP_NOTIFY | \
BM_WPP_STATEMACHINE | BM_WPP_STATEMACHINELEVEL | \
BM_WPP_BATTERRHANDLE | BM_WPP_FGGGE | \
BM_WPP_PARALLELCHARGING | BM_WPP_SMCHG | \
BM_WPP_SWJEITA | BM_WPP_QBG | BM_QBG | \
BM_ADC | BM_CAD | BM_PMICPLATFORM | \
BM_PMICHAL | BM_SUMMARY)
#define BM_ALL (BM_USBC_ALL | BM_BM_ALL)
/* exported APIs */
int bm_ulog_get_log(char *buf, u32 size);
int bm_ulog_get_mask_log(enum bm_ulog_category_bitmap categories,
enum bm_ulog_level_type level,
char *buf, u32 size);
int bm_ulog_print_log(u32 size);
int bm_ulog_print_mask_log(enum bm_ulog_category_bitmap categories,
enum bm_ulog_level_type level, u32 size);
#endif /* __BM_ADSP_ULOG_H__ */