mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-25 06:03:36 -04:00
Merge "defconfig: enable memlat hw log driver"
This commit is contained in:
commit
9948a5bf34
11 changed files with 2597 additions and 0 deletions
6
arch/arm64/configs/vendor/holi_QGKI.config
vendored
6
arch/arm64/configs/vendor/holi_QGKI.config
vendored
|
|
@ -169,3 +169,9 @@ CONFIG_QCOM_QFPROM_SYSFS=y
|
|||
CONFIG_ARM_QCOM_CPUFREQ_HW_DEBUG=y
|
||||
CONFIG_STM_PROTO_BASIC=y
|
||||
CONFIG_INTERCONNECT_QCOM_CPUCP_L3=y
|
||||
CONFIG_QCOM_RIMPS=y
|
||||
CONFIG_QTI_SCMI_MEMLAT_PROTOCOL=y
|
||||
CONFIG_QTI_HW_MEMLAT=y
|
||||
CONFIG_QTI_HW_MEMLAT_SCMI_CLIENT=y
|
||||
CONFIG_SRAM=y
|
||||
CONFIG_QTI_HW_MEMLAT_LOG=y
|
||||
|
|
|
|||
|
|
@ -27,6 +27,18 @@ config ARM_SCMI_PROTOCOL
|
|||
This protocol library provides interface for all the client drivers
|
||||
making use of the features offered by the SCMI.
|
||||
|
||||
config QTI_SCMI_MEMLAT_PROTOCOL
|
||||
tristate "Qualcomm Technologies, Inc. SCMI MEMLAT vendor Protocol"
|
||||
depends on ARM || ARM64 || COMPILE_TEST
|
||||
depends on ARM_SCMI_PROTOCOL && QCOM_RIMPS
|
||||
help
|
||||
System Control and Management Interface (SCMI) memlat vendor protocol
|
||||
this protocol provides interface to communicate with micro controller
|
||||
which is executing the hw memlat governor
|
||||
|
||||
This driver defines the comands or message ID's used for this
|
||||
communication and also exposes the ops used by clients.
|
||||
|
||||
config ARM_SCMI_POWER_DOMAIN
|
||||
tristate "SCMI power domain driver"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ scmi-bus-y = bus.o
|
|||
scmi-driver-y = driver.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
|
||||
obj-$(CONFIG_QTI_SCMI_MEMLAT_PROTOCOL) += memlat_vendor.o
|
||||
|
|
|
|||
335
drivers/firmware/arm_scmi/memlat_vendor.c
Normal file
335
drivers/firmware/arm_scmi/memlat_vendor.c
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_MAP_ENTRIES 14
|
||||
#define MAX_PMU_ENTRIES 24
|
||||
|
||||
#define SCMI_VENDOR_MSG_START (3)
|
||||
#define SCMI_VENDOR_MSG_MODULE_START (16)
|
||||
#define SCMI_MAX_RX_SIZE 128
|
||||
#define SCMI_MAX_GET_DATA_SIZE 124
|
||||
|
||||
enum scmi_memlat_protocol_cmd {
|
||||
MEMLAT_SET_LOG_LEVEL = SCMI_VENDOR_MSG_START,
|
||||
MEMLAT_SET_CPU_GROUP = SCMI_VENDOR_MSG_MODULE_START,
|
||||
MEMLAT_SET_MONITOR,
|
||||
MEMLAT_COMMON_PMU_MAP,
|
||||
MEMLAT_MON_PMU_MAP,
|
||||
MEMLAT_RATIO_CEIL,
|
||||
MEMLAT_STALL_FLOOR,
|
||||
MEMLAT_L3_L2WB_PCT,
|
||||
MEMLAT_L3_IPM_FILTER,
|
||||
MEMLAT_SAMPLE_MS,
|
||||
MEMLAT_MON_FREQ_MAP,
|
||||
MEMLAT_SET_MIN_FREQ,
|
||||
MEMLAT_SET_MAX_FREQ,
|
||||
MEMLAT_START_MONITOR,
|
||||
MEMLAT_STOP_MONITOR,
|
||||
MEMLAT_GET_DATA = 0xFF,
|
||||
MEMLAT_MAX_MSG
|
||||
};
|
||||
|
||||
struct node_msg {
|
||||
uint32_t cpumask;
|
||||
uint32_t mon_type;
|
||||
};
|
||||
|
||||
struct scalar_param_msg {
|
||||
uint32_t cpumask;
|
||||
uint32_t mon_type;
|
||||
uint32_t val;
|
||||
};
|
||||
|
||||
struct map_table {
|
||||
uint32_t v1;
|
||||
uint32_t v2;
|
||||
};
|
||||
|
||||
struct map_param_msg {
|
||||
uint32_t cpumask;
|
||||
uint32_t mon_type;
|
||||
uint32_t nr_rows;
|
||||
struct map_table tbl[MAX_MAP_ENTRIES];
|
||||
};
|
||||
|
||||
struct pmu_map_msg {
|
||||
uint32_t cpumask;
|
||||
uint32_t mon_type;
|
||||
uint32_t nr_entries;
|
||||
uint32_t pmu[MAX_PMU_ENTRIES];
|
||||
};
|
||||
|
||||
static int scmi_set_cpugrp_mon(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 msg_id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scmi_xfer *t;
|
||||
struct node_msg *msg;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, msg_id,
|
||||
SCMI_PROTOCOL_MEMLAT,
|
||||
sizeof(*msg), sizeof(*msg), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg = t->tx.buf;
|
||||
msg->cpumask = cpu_to_le32(cpus_mpidr);
|
||||
msg->mon_type = cpu_to_le32(mon_type);
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
scmi_xfer_put(handle, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_set_mon(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type)
|
||||
{
|
||||
return scmi_set_cpugrp_mon(handle, cpus_mpidr,
|
||||
mon_type, MEMLAT_SET_MONITOR);
|
||||
}
|
||||
|
||||
static int scmi_set_cpu_grp(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type)
|
||||
{
|
||||
return scmi_set_cpugrp_mon(handle, cpus_mpidr,
|
||||
mon_type, MEMLAT_SET_CPU_GROUP);
|
||||
}
|
||||
|
||||
static int scmi_send_pmu_map_command(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 nr_entries,
|
||||
void *buf, u32 msg_id)
|
||||
{
|
||||
int ret, i = 0;
|
||||
struct scmi_xfer *t;
|
||||
struct pmu_map_msg *msg;
|
||||
u32 *dst;
|
||||
struct map_table *src = buf;
|
||||
|
||||
if (nr_entries > MAX_PMU_ENTRIES)
|
||||
return -EINVAL;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, msg_id,
|
||||
SCMI_PROTOCOL_MEMLAT,
|
||||
sizeof(*msg), sizeof(*msg), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg = t->tx.buf;
|
||||
msg->cpumask = cpu_to_le32(cpus_mpidr);
|
||||
msg->mon_type = cpu_to_le32(mon_type);
|
||||
msg->nr_entries = cpu_to_le32(nr_entries);
|
||||
dst = msg->pmu;
|
||||
|
||||
for (i = 0; i < nr_entries; i++)
|
||||
dst[i] = cpu_to_le32(src[i].v2);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_common_pmu_map(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type,
|
||||
u32 nr_entries, void *buf)
|
||||
{
|
||||
return scmi_send_pmu_map_command(handle, cpus_mpidr,
|
||||
mon_type, nr_entries, buf,
|
||||
MEMLAT_COMMON_PMU_MAP);
|
||||
}
|
||||
|
||||
static int scmi_mon_pmu_map(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type,
|
||||
u32 nr_entries, void *buf)
|
||||
{
|
||||
return scmi_send_pmu_map_command(handle, cpus_mpidr,
|
||||
mon_type, nr_entries, buf,
|
||||
MEMLAT_MON_PMU_MAP);
|
||||
}
|
||||
|
||||
static int scmi_freq_map(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type,
|
||||
u32 nr_rows, void *buf)
|
||||
{
|
||||
int ret, i = 0;
|
||||
struct scmi_xfer *t;
|
||||
struct map_param_msg *msg;
|
||||
struct map_table *tbl, *src = buf;
|
||||
|
||||
if (nr_rows > MAX_MAP_ENTRIES)
|
||||
return -EINVAL;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, MEMLAT_MON_FREQ_MAP,
|
||||
SCMI_PROTOCOL_MEMLAT,
|
||||
sizeof(*msg), sizeof(*msg), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg = t->tx.buf;
|
||||
msg->cpumask = cpu_to_le32(cpus_mpidr);
|
||||
msg->mon_type = cpu_to_le32(mon_type);
|
||||
msg->nr_rows = cpu_to_le32(nr_rows);
|
||||
tbl = msg->tbl;
|
||||
|
||||
for (i = 0; i < nr_rows; i++) {
|
||||
tbl[i].v1 = cpu_to_le32(src[i].v1);
|
||||
tbl[i].v2 = cpu_to_le32(src[i].v2);
|
||||
}
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define scmi_send_cmd(name, _msg_id) \
|
||||
static int scmi_##name(const struct scmi_handle *handle, \
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val) \
|
||||
{ \
|
||||
int ret = 0; \
|
||||
struct scmi_xfer *t; \
|
||||
struct scalar_param_msg *msg; \
|
||||
ret = scmi_xfer_get_init(handle, _msg_id, \
|
||||
SCMI_PROTOCOL_MEMLAT, \
|
||||
sizeof(*msg), sizeof(*msg), &t); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
msg = t->tx.buf; \
|
||||
msg->cpumask = cpu_to_le32(cpus_mpidr); \
|
||||
msg->mon_type = cpu_to_le32(mon_type); \
|
||||
msg->val = cpu_to_le32(val); \
|
||||
ret = scmi_do_xfer(handle, t); \
|
||||
scmi_xfer_put(handle, t); \
|
||||
return ret; \
|
||||
} \
|
||||
|
||||
scmi_send_cmd(ratio_ceil, MEMLAT_RATIO_CEIL);
|
||||
scmi_send_cmd(stall_floor, MEMLAT_STALL_FLOOR);
|
||||
scmi_send_cmd(l2wb_pct, MEMLAT_L3_L2WB_PCT);
|
||||
scmi_send_cmd(l2wb_filter, MEMLAT_L3_IPM_FILTER);
|
||||
scmi_send_cmd(sample_ms, MEMLAT_SAMPLE_MS);
|
||||
scmi_send_cmd(min_freq, MEMLAT_SET_MIN_FREQ);
|
||||
scmi_send_cmd(max_freq, MEMLAT_SET_MAX_FREQ);
|
||||
|
||||
static int scmi_send_start_stop(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 msg_id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scmi_xfer *t;
|
||||
struct scalar_param_msg *msg;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, msg_id,
|
||||
SCMI_PROTOCOL_MEMLAT,
|
||||
sizeof(*msg), sizeof(*msg), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
msg = t->tx.buf;
|
||||
msg->cpumask = cpu_to_le32(cpus_mpidr);
|
||||
msg->mon_type = cpu_to_le32(mon_type);
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
scmi_xfer_put(handle, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_stop_mon(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type)
|
||||
{
|
||||
return scmi_send_start_stop(handle, cpus_mpidr,
|
||||
mon_type, MEMLAT_STOP_MONITOR);
|
||||
}
|
||||
|
||||
static int scmi_start_mon(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type)
|
||||
{
|
||||
return scmi_send_start_stop(handle, cpus_mpidr,
|
||||
mon_type, MEMLAT_START_MONITOR);
|
||||
}
|
||||
|
||||
static int scmi_get_data(const struct scmi_handle *handle, u8 *buf)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scmi_xfer *t;
|
||||
u32 prev_cnt = 0;
|
||||
struct scalar_param_msg *msg;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, MEMLAT_GET_DATA,
|
||||
SCMI_PROTOCOL_MEMLAT, sizeof(*msg),
|
||||
SCMI_MAX_RX_SIZE, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
do {
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (ret == -ETIMEDOUT)
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
memcpy((buf + prev_cnt), t->rx.buf, t->rx.len);
|
||||
prev_cnt += t->rx.len;
|
||||
} while (t->rx.len >= SCMI_MAX_GET_DATA_SIZE);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_set_log_level(const struct scmi_handle *handle, u32 val)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scmi_xfer *t;
|
||||
u32 *ptr;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, MEMLAT_SET_LOG_LEVEL,
|
||||
SCMI_PROTOCOL_MEMLAT, sizeof(u32),
|
||||
sizeof(u32), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
ptr = (u32 *)t->tx.buf;
|
||||
*ptr = cpu_to_le32(val);
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
scmi_xfer_put(handle, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct scmi_memlat_vendor_ops memlat_ops = {
|
||||
.set_cpu_grp = scmi_set_cpu_grp,
|
||||
.freq_map = scmi_freq_map,
|
||||
.set_mon = scmi_set_mon,
|
||||
.common_pmu_map = scmi_common_pmu_map,
|
||||
.mon_pmu_map = scmi_mon_pmu_map,
|
||||
.ratio_ceil = scmi_ratio_ceil,
|
||||
.stall_floor = scmi_stall_floor,
|
||||
.sample_ms = scmi_sample_ms,
|
||||
.l2wb_filter = scmi_l2wb_filter,
|
||||
.l2wb_pct = scmi_l2wb_pct,
|
||||
.min_freq = scmi_min_freq,
|
||||
.max_freq = scmi_max_freq,
|
||||
.start_monitor = scmi_start_mon,
|
||||
.stop_monitor = scmi_stop_mon,
|
||||
.set_log_level = scmi_set_log_level,
|
||||
.get_data = scmi_get_data,
|
||||
};
|
||||
|
||||
static int scmi_memlat_vendor_protocol_init(struct scmi_handle *handle)
|
||||
{
|
||||
u32 version;
|
||||
|
||||
scmi_version_get(handle, SCMI_PROTOCOL_MEMLAT, &version);
|
||||
|
||||
dev_dbg(handle->dev, "memlat version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
handle->memlat_ops = &memlat_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init scmi_memlat_init(void)
|
||||
{
|
||||
return scmi_protocol_register(SCMI_PROTOCOL_MEMLAT,
|
||||
&scmi_memlat_vendor_protocol_init);
|
||||
}
|
||||
subsys_initcall(scmi_memlat_init);
|
||||
|
|
@ -148,6 +148,16 @@ config QCOM_IPCC
|
|||
Say Y here to compile the driver as a part of kernel or M to compile
|
||||
as a module.
|
||||
|
||||
config QCOM_RIMPS
|
||||
tristate "Qualcomm Technologies, Inc. Rimps driver"
|
||||
depends on MAILBOX
|
||||
help
|
||||
Qualcomm Technologies, Inc. RIMPS driver for MSM devices. This driver
|
||||
acts as a mailbox controller to do doorbell between Apss and Rimps
|
||||
subsystem. Say yes here to enable rx and tx channel between both
|
||||
the subsystems.
|
||||
If unsure, say n.
|
||||
|
||||
config QCOM_LLCC
|
||||
tristate "Qualcomm Technologies, Inc. LLCC driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
|
|
@ -710,6 +720,42 @@ config QTI_SYSTEM_PM
|
|||
config QTI_SYSTEM_PM_RPM
|
||||
bool
|
||||
|
||||
config QTI_HW_MEMLAT
|
||||
tristate "Qualcomm Technologies Inc. RIMPS memlat interface driver"
|
||||
depends on PERF_EVENTS
|
||||
default n
|
||||
help
|
||||
Interface driver between RIMPS memlat and userspace.
|
||||
|
||||
This driver is responsible for handling the communication with
|
||||
RIMPS memlat such as setting up PMU events, passing tunables, frequency
|
||||
table, PMU counters and saving the values of PMU counters during LPM and
|
||||
hotplug.
|
||||
|
||||
config QTI_HW_MEMLAT_SCMI_CLIENT
|
||||
tristate "Qualcomm Technologies Inc. SCMI client driver for HW MEMLAT"
|
||||
depends on QTI_HW_MEMLAT
|
||||
default n
|
||||
help
|
||||
SCMI client driver registers itself with SCMI framework for memlat
|
||||
vendor protocol, and also registers with the memlat hw interface
|
||||
driver.
|
||||
|
||||
This driver deliver the memlat vendor protocol handle to interface
|
||||
driver, and interface driver will use this handle to communicate with
|
||||
memlat HW.
|
||||
|
||||
config QTI_HW_MEMLAT_LOG
|
||||
tristate "Qualcomm Technologies Inc. HW MEMLAT Logging"
|
||||
depends on IPC_LOGGING && QCOM_RIMPS
|
||||
default n
|
||||
help
|
||||
Memlat hw logging driver, this driver has the infra to collect logs
|
||||
generated in MEMLAT HW and log the buffers.
|
||||
|
||||
This driver register with IPC_Logging framework, to have dedicated
|
||||
buffer for memlat hw device.
|
||||
|
||||
if MSM_PM
|
||||
menuconfig MSM_IDLE_STATS
|
||||
bool "Collect idle statistics"
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o
|
|||
obj-$(CONFIG_QCOM_PROXY_OF_CONSUMER) += qcom_proxy_of_consumer.o
|
||||
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
|
||||
obj-$(CONFIG_QCOM_IPCC) += qcom_ipcc.o
|
||||
obj-$(CONFIG_QCOM_RIMPS) += qcom_rimps.o
|
||||
obj-$(CONFIG_QCOM_RUN_QUEUE_STATS) += rq_stats.o
|
||||
obj-$(CONFIG_QCOM_APR) += apr.o
|
||||
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
|
||||
|
|
@ -92,3 +93,6 @@ obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd-debug.o
|
|||
endif
|
||||
obj-$(CONFIG_QTI_SYS_PM_VX) += sys_pm_vx.o
|
||||
obj-$(CONFIG_ICNSS2) += icnss2/
|
||||
obj-$(CONFIG_QTI_HW_MEMLAT_SCMI_CLIENT) += memlat_scmi.o
|
||||
obj-$(CONFIG_QTI_HW_MEMLAT) += rimps_memlat.o
|
||||
obj-$(CONFIG_QTI_HW_MEMLAT_LOG) += rimps_log.o
|
||||
|
|
|
|||
40
drivers/soc/qcom/memlat_scmi.c
Normal file
40
drivers/soc/qcom/memlat_scmi.c
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
extern void rimps_memlat_init(struct scmi_handle *handle);
|
||||
|
||||
static int scmi_memlat_probe(struct scmi_device *sdev)
|
||||
{
|
||||
struct scmi_handle *handle = sdev->handle;
|
||||
|
||||
if (!handle || !handle->memlat_ops)
|
||||
return -ENODEV;
|
||||
|
||||
rimps_memlat_init(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_MEMLAT },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_memlat_drv = {
|
||||
.name = "scmi-memlat-driver",
|
||||
.probe = scmi_memlat_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_memlat_drv);
|
||||
|
||||
MODULE_DESCRIPTION("ARM SCMI Memlat driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
244
drivers/soc/qcom/qcom_rimps.c
Normal file
244
drivers/soc/qcom/qcom_rimps.c
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mailbox_controller.h>
|
||||
|
||||
/* RIMPS Register offsets */
|
||||
#define RIMPS_IPC_CHAN_SUPPORTED 2
|
||||
#define RIMPS_SEND_IRQ_OFFSET 0xC
|
||||
#define RIMPS_SEND_IRQ_VAL BIT(28)
|
||||
#define RIMPS_CLEAR_IRQ_OFFSET 0x308
|
||||
#define RIMPS_STATUS_IRQ_OFFSET 0x30C
|
||||
#define RIMPS_CLEAR_IRQ_VAL BIT(3)
|
||||
#define RIMPS_STATUS_IRQ_VAL BIT(3)
|
||||
#define RIMPS_CLOCK_DOMAIN_OFFSET 0x1000
|
||||
|
||||
|
||||
/**
|
||||
* struct rimps_ipc ipc per channel
|
||||
* @mbox: mailbox-controller interface
|
||||
* @chans: The mailbox clients' channel array
|
||||
* @dev: Device associated with this instance
|
||||
* @irq: Rimps to HLOS irq
|
||||
*/
|
||||
struct qcom_rimps_ipc {
|
||||
struct mbox_controller mbox;
|
||||
struct mbox_chan chans[RIMPS_IPC_CHAN_SUPPORTED];
|
||||
void __iomem *tx_irq_base;
|
||||
void __iomem *rx_irq_base;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
int num_chan;
|
||||
};
|
||||
|
||||
static irqreturn_t qcom_rimps_rx_interrupt(int irq, void *p)
|
||||
{
|
||||
struct qcom_rimps_ipc *rimps_ipc;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
rimps_ipc = p;
|
||||
|
||||
for (i = 0; i < rimps_ipc->num_chan; i++) {
|
||||
|
||||
val = readl_no_log(rimps_ipc->rx_irq_base +
|
||||
RIMPS_STATUS_IRQ_OFFSET + (i * RIMPS_CLOCK_DOMAIN_OFFSET));
|
||||
if (val & RIMPS_STATUS_IRQ_VAL) {
|
||||
|
||||
val = RIMPS_CLEAR_IRQ_VAL;
|
||||
writel_no_log(val, rimps_ipc->rx_irq_base +
|
||||
RIMPS_CLEAR_IRQ_OFFSET +
|
||||
(i * RIMPS_CLOCK_DOMAIN_OFFSET));
|
||||
/* Make sure register write is complete before proceeding */
|
||||
mb();
|
||||
|
||||
if (rimps_ipc->chans[i].con_priv)
|
||||
mbox_chan_received_data(&rimps_ipc->chans[i]
|
||||
, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void qcom_rimps_mbox_shutdown(struct mbox_chan *chan)
|
||||
{
|
||||
chan->con_priv = NULL;
|
||||
}
|
||||
|
||||
static int qcom_rimps_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||
{
|
||||
struct qcom_rimps_ipc *rimps_ipc = container_of(chan->mbox,
|
||||
struct qcom_rimps_ipc, mbox);
|
||||
|
||||
writel_no_log(RIMPS_SEND_IRQ_VAL,
|
||||
rimps_ipc->tx_irq_base + RIMPS_SEND_IRQ_OFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mbox_chan *qcom_rimps_mbox_xlate(struct mbox_controller *mbox,
|
||||
const struct of_phandle_args *sp)
|
||||
{
|
||||
struct qcom_rimps_ipc *rimps_ipc = container_of(mbox,
|
||||
struct qcom_rimps_ipc, mbox);
|
||||
unsigned long ind = sp->args[0];
|
||||
|
||||
if (sp->args_count != 1)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (ind >= mbox->num_chans)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (mbox->chans[ind].con_priv)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
mbox->chans[ind].con_priv = rimps_ipc;
|
||||
return &mbox->chans[ind];
|
||||
}
|
||||
|
||||
static const struct mbox_chan_ops rimps_mbox_chan_ops = {
|
||||
.send_data = qcom_rimps_mbox_send_data,
|
||||
.shutdown = qcom_rimps_mbox_shutdown
|
||||
};
|
||||
|
||||
static int qcom_rimps_ipc_setup_mbox(struct qcom_rimps_ipc *rimps_ipc)
|
||||
{
|
||||
struct mbox_controller *mbox;
|
||||
struct device *dev = rimps_ipc->dev;
|
||||
unsigned long i;
|
||||
|
||||
/* Initialize channel identifiers */
|
||||
for (i = 0; i < ARRAY_SIZE(rimps_ipc->chans); i++)
|
||||
rimps_ipc->chans[i].con_priv = NULL;
|
||||
|
||||
mbox = &rimps_ipc->mbox;
|
||||
mbox->dev = dev;
|
||||
mbox->num_chans = rimps_ipc->num_chan;
|
||||
mbox->chans = rimps_ipc->chans;
|
||||
mbox->ops = &rimps_mbox_chan_ops;
|
||||
mbox->of_xlate = qcom_rimps_mbox_xlate;
|
||||
mbox->txdone_irq = false;
|
||||
mbox->txdone_poll = false;
|
||||
|
||||
return mbox_controller_register(mbox);
|
||||
}
|
||||
|
||||
static int qcom_rimps_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_rimps_ipc *rimps_ipc;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
rimps_ipc = devm_kzalloc(&pdev->dev, sizeof(*rimps_ipc), GFP_KERNEL);
|
||||
if (!rimps_ipc)
|
||||
return -ENOMEM;
|
||||
|
||||
rimps_ipc->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get the device base address\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rimps_ipc->tx_irq_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!rimps_ipc->tx_irq_base) {
|
||||
dev_err(&pdev->dev, "Failed to ioremap the rimps tx irq addr\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get the device base address\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rimps_ipc->rx_irq_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!rimps_ipc->rx_irq_base) {
|
||||
dev_err(&pdev->dev, "Failed to ioremap the rimps rx irq addr\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rimps_ipc->irq = platform_get_irq(pdev, 0);
|
||||
if (rimps_ipc->irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get the IRQ\n");
|
||||
return rimps_ipc->irq;
|
||||
}
|
||||
|
||||
rimps_ipc->num_chan = RIMPS_IPC_CHAN_SUPPORTED;
|
||||
ret = qcom_rimps_ipc_setup_mbox(rimps_ipc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to create mailbox\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, rimps_ipc->irq,
|
||||
qcom_rimps_rx_interrupt, IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "qcom_rimps", rimps_ipc);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret);
|
||||
goto err_mbox;
|
||||
}
|
||||
platform_set_drvdata(pdev, rimps_ipc);
|
||||
|
||||
return 0;
|
||||
|
||||
err_mbox:
|
||||
mbox_controller_unregister(&rimps_ipc->mbox);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_rimps_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_rimps_ipc *rimps_ipc = platform_get_drvdata(pdev);
|
||||
|
||||
mbox_controller_unregister(&rimps_ipc->mbox);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_rimps_of_match[] = {
|
||||
{ .compatible = "qcom,rimps"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_rimps_of_match);
|
||||
|
||||
static struct platform_driver qcom_rimps_driver = {
|
||||
.probe = qcom_rimps_probe,
|
||||
.remove = qcom_rimps_remove,
|
||||
.driver = {
|
||||
.name = "qcom_rimps",
|
||||
.of_match_table = qcom_rimps_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init qcom_rimps_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&qcom_rimps_driver);
|
||||
if (ret)
|
||||
pr_err("%s: qcom_rimps register failed %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
#if IS_MODULE(CONFIG_QCOM_RIMPS)
|
||||
module_init(qcom_rimps_init);
|
||||
#else
|
||||
arch_initcall(qcom_rimps_init);
|
||||
#endif
|
||||
|
||||
static __exit void qcom_rimps_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&qcom_rimps_driver);
|
||||
}
|
||||
module_exit(qcom_rimps_exit);
|
||||
|
||||
MODULE_DESCRIPTION("QTI RIMPS Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
373
drivers/soc/qcom/rimps_log.c
Normal file
373
drivers/soc/qcom/rimps_log.c
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define NUM_LOG_PAGES 10
|
||||
#define MAX_PRINT_SIZE 200
|
||||
#define MAX_BUF_NUM 10
|
||||
#define MAX_RESIDUAL_SIZE MAX_PRINT_SIZE
|
||||
#define LOG_ID_MARKER 0x474F4C
|
||||
#define LOG_ID_MARKER_SHIFT 24
|
||||
#define LOG_ID_MARKER_MASK 0xFFFFFF
|
||||
#define LOG_ID_MASK 0xFF
|
||||
#define SIZE_ADJUST 4
|
||||
#define SRC_OFFSET 4
|
||||
|
||||
enum sync_mode {
|
||||
ping_pong,
|
||||
log_id
|
||||
};
|
||||
|
||||
struct remote_mem {
|
||||
void __iomem *start;
|
||||
unsigned long long size;
|
||||
};
|
||||
|
||||
struct rimps_buf {
|
||||
struct list_head node;
|
||||
char *buf;
|
||||
u32 size;
|
||||
u32 cpy_idx;
|
||||
};
|
||||
|
||||
struct rimps_log_info {
|
||||
struct remote_mem *rmem;
|
||||
struct mbox_client cl;
|
||||
struct mbox_chan *ch;
|
||||
struct delayed_work work;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
void *ipc_log_ctxt;
|
||||
enum sync_mode sync_mode;
|
||||
unsigned int rmem_idx;
|
||||
unsigned int num_bufs;
|
||||
char *rem_buf;
|
||||
char *glb_buf;
|
||||
int rem_len;
|
||||
spinlock_t free_list_lock;
|
||||
spinlock_t full_list_lock;
|
||||
};
|
||||
|
||||
static LIST_HEAD(full_buffers_list);
|
||||
static LIST_HEAD(free_buffers_list);
|
||||
static struct workqueue_struct *rimps_wq;
|
||||
|
||||
static inline int get_next_logbuf(char *buf, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = (size - 1); i >= 0 ; i--) {
|
||||
if (buf[i] == '\n')
|
||||
break;
|
||||
}
|
||||
if (i >= 0)
|
||||
buf[i] = '\0';
|
||||
else
|
||||
return size;
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
static void rimps_log_work(struct work_struct *work)
|
||||
{
|
||||
struct rimps_log_info *info = container_of(work,
|
||||
struct rimps_log_info,
|
||||
work.work);
|
||||
char *src;
|
||||
int buf_start = 0;
|
||||
int cnt = 0, print_size = 0, buf_size = 0;
|
||||
char c;
|
||||
struct rimps_buf *buf_node;
|
||||
unsigned long flags;
|
||||
|
||||
while (1) {
|
||||
spin_lock_irqsave(&info->full_list_lock, flags);
|
||||
if (list_empty(&full_buffers_list)) {
|
||||
spin_unlock_irqrestore(&info->full_list_lock, flags);
|
||||
return;
|
||||
}
|
||||
buf_node = list_first_entry(&full_buffers_list,
|
||||
struct rimps_buf, node);
|
||||
list_del(&buf_node->node);
|
||||
spin_unlock_irqrestore(&info->full_list_lock, flags);
|
||||
buf_start = buf_node->cpy_idx - info->rem_len;
|
||||
if (info->rem_len)
|
||||
memcpy(&buf_node->buf[buf_start],
|
||||
info->rem_buf, info->rem_len);
|
||||
src = &buf_node->buf[buf_start];
|
||||
buf_size = buf_node->size + info->rem_len;
|
||||
do {
|
||||
print_size = (buf_size >= MAX_PRINT_SIZE) ?
|
||||
MAX_PRINT_SIZE : buf_size;
|
||||
cnt = get_next_logbuf(src, print_size);
|
||||
if (cnt == print_size) {
|
||||
if (buf_size < MAX_PRINT_SIZE) {
|
||||
info->rem_len = buf_size;
|
||||
memcpy(info->rem_buf, src, buf_size);
|
||||
goto out;
|
||||
} else {
|
||||
c = src[cnt - 1];
|
||||
src[cnt - 1] = '\0';
|
||||
ipc_log_string(info->ipc_log_ctxt,
|
||||
"%s%c\n", src, c);
|
||||
}
|
||||
} else {
|
||||
ipc_log_string(info->ipc_log_ctxt,
|
||||
"%s\n", src, c);
|
||||
}
|
||||
|
||||
buf_start += cnt;
|
||||
buf_size -= cnt;
|
||||
src = &buf_node->buf[buf_start];
|
||||
} while (buf_size > 0);
|
||||
out:
|
||||
spin_lock_irqsave(&info->free_list_lock, flags);
|
||||
list_add_tail(&buf_node->node, &free_buffers_list);
|
||||
spin_unlock_irqrestore(&info->free_list_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static struct rimps_buf *get_free_buffer(struct rimps_log_info *info)
|
||||
{
|
||||
struct rimps_buf *buf_node;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->free_list_lock, flags);
|
||||
if (list_empty(&free_buffers_list)) {
|
||||
spin_unlock_irqrestore(&info->free_list_lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf_node = list_first_entry(&free_buffers_list,
|
||||
struct rimps_buf, node);
|
||||
list_del(&buf_node->node);
|
||||
spin_unlock_irqrestore(&info->free_list_lock, flags);
|
||||
return buf_node;
|
||||
}
|
||||
|
||||
|
||||
static void rimps_log_rx(struct mbox_client *client, void *msg)
|
||||
{
|
||||
struct rimps_log_info *info = dev_get_drvdata(client->dev);
|
||||
struct device *dev = info->dev;
|
||||
struct rimps_buf *buf_node;
|
||||
struct remote_mem *rmem;
|
||||
void __iomem *src;
|
||||
u32 marker;
|
||||
unsigned long long rmem_size;
|
||||
unsigned long flags;
|
||||
int src_offset = 0;
|
||||
int size_adj = 0;
|
||||
|
||||
buf_node = get_free_buffer(info);
|
||||
if (!buf_node) {
|
||||
dev_err(dev, "global buffer full dropping buffers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->sync_mode == log_id) {
|
||||
marker = *(u32 *)(info->rmem)->start;
|
||||
if ((marker & LOG_ID_MARKER_MASK) != LOG_ID_MARKER) {
|
||||
pr_err("%s: Log signature incorrect\n", __func__);
|
||||
return;
|
||||
}
|
||||
info->rmem_idx = ((marker >> LOG_ID_MARKER_SHIFT)
|
||||
& LOG_ID_MASK);
|
||||
if (info->rmem_idx >= info->num_bufs) {
|
||||
dev_err(dev, "wrong index id dropping\n");
|
||||
return;
|
||||
}
|
||||
if (info->rmem_idx == 0) {
|
||||
size_adj = SIZE_ADJUST;
|
||||
src_offset = SRC_OFFSET;
|
||||
}
|
||||
}
|
||||
|
||||
rmem = info->rmem + info->rmem_idx;
|
||||
rmem_size = rmem->size - size_adj;
|
||||
src = rmem->start + src_offset;
|
||||
memcpy_fromio(&buf_node->buf[buf_node->cpy_idx],
|
||||
src, rmem_size);
|
||||
buf_node->size = rmem_size;
|
||||
spin_lock_irqsave(&info->full_list_lock, flags);
|
||||
list_add_tail(&buf_node->node, &full_buffers_list);
|
||||
spin_unlock_irqrestore(&info->full_list_lock, flags);
|
||||
|
||||
if (!delayed_work_pending(&info->work))
|
||||
queue_delayed_work(rimps_wq, &info->work, 0);
|
||||
|
||||
if (info->sync_mode == ping_pong) {
|
||||
info->rmem_idx++;
|
||||
if (info->rmem_idx == info->num_bufs)
|
||||
info->rmem_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int populate_free_buffers(struct rimps_log_info *info,
|
||||
int rmem_size)
|
||||
{
|
||||
int i = 0;
|
||||
struct rimps_buf *buf_nodes;
|
||||
|
||||
buf_nodes = devm_kzalloc(info->dev,
|
||||
MAX_BUF_NUM * sizeof(struct rimps_buf),
|
||||
GFP_KERNEL);
|
||||
if (!buf_nodes)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < MAX_BUF_NUM; i++) {
|
||||
buf_nodes[i].buf = &info->glb_buf[i * (rmem_size + MAX_PRINT_SIZE)];
|
||||
buf_nodes[i].size = rmem_size;
|
||||
buf_nodes[i].cpy_idx = MAX_PRINT_SIZE;
|
||||
list_add_tail(&buf_nodes[i].node, &free_buffers_list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rimps_log_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rimps_log_info *info;
|
||||
struct mbox_client *cl;
|
||||
int ret, i = 0;
|
||||
struct resource *res;
|
||||
void __iomem *mem_base;
|
||||
struct remote_mem *rmem;
|
||||
int prev_size = 0;
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->dev = dev;
|
||||
|
||||
rmem = kcalloc(pdev->num_resources, sizeof(struct remote_mem),
|
||||
GFP_KERNEL);
|
||||
if (!rmem)
|
||||
return -ENOMEM;
|
||||
|
||||
info->rmem = rmem;
|
||||
|
||||
for (i = 0; i < pdev->num_resources; i++) {
|
||||
struct remote_mem *rmem = &info->rmem[i];
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
if (!res) {
|
||||
dev_err(dev,
|
||||
"Failed to get the device base address\n");
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mem_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (IS_ERR(mem_base)) {
|
||||
ret = PTR_ERR(mem_base);
|
||||
goto exit;
|
||||
}
|
||||
rmem->start = mem_base;
|
||||
rmem->size = resource_size(res);
|
||||
if (prev_size && (rmem->size != prev_size)) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
} else if (!prev_size) {
|
||||
prev_size = rmem->size;
|
||||
}
|
||||
info->num_bufs++;
|
||||
}
|
||||
info->glb_buf = devm_kzalloc(dev, MAX_BUF_NUM *
|
||||
(rmem->size + MAX_PRINT_SIZE),
|
||||
GFP_KERNEL);
|
||||
if (!info->glb_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
info->rem_buf = devm_kzalloc(dev, MAX_RESIDUAL_SIZE, GFP_KERNEL);
|
||||
if (!info->rem_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = populate_free_buffers(info, rmem->size);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
info->sync_mode = log_id;
|
||||
|
||||
cl = &info->cl;
|
||||
cl->dev = dev;
|
||||
cl->tx_block = false;
|
||||
cl->knows_txdone = true;
|
||||
cl->rx_callback = rimps_log_rx;
|
||||
|
||||
info->ch = mbox_request_channel(cl, 0);
|
||||
if (IS_ERR(info->ch)) {
|
||||
ret = PTR_ERR(info->ch);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to request mbox info: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
info->ipc_log_ctxt = ipc_log_context_create(NUM_LOG_PAGES,
|
||||
dev_name(dev), 0);
|
||||
if (!info->ipc_log_ctxt) {
|
||||
dev_err(dev, "failed to create log context\n");
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, info);
|
||||
INIT_DEFERRABLE_WORK(&info->work, &rimps_log_work);
|
||||
spin_lock_init(&info->free_list_lock);
|
||||
spin_lock_init(&info->full_list_lock);
|
||||
rimps_wq = create_freezable_workqueue("rimps_wq");
|
||||
dev_info(dev, "RIMPS logging initialized\n");
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
kfree(info->rmem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rimps_log_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rimps_log_info *info;
|
||||
|
||||
info = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
mbox_free_channel(info->ch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rimps_log[] = {
|
||||
{.compatible = "qcom,rimps-log"},
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
static struct platform_driver rimps_log_driver = {
|
||||
.driver = {
|
||||
.name = "rimps-log",
|
||||
.of_match_table = rimps_log,
|
||||
},
|
||||
.probe = rimps_log_probe,
|
||||
.remove = rimps_log_remove,
|
||||
};
|
||||
builtin_platform_driver(rimps_log_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1476
drivers/soc/qcom/rimps_memlat.c
Normal file
1476
drivers/soc/qcom/rimps_memlat.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -207,6 +207,64 @@ struct scmi_reset_ops {
|
|||
int (*deassert)(const struct scmi_handle *handle, u32 domain);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_memlat_vendor_ops - represents the various operations provided
|
||||
* by SCMI HW Memlat Protocol
|
||||
*
|
||||
* @cpu_grp: set the cpugrp
|
||||
* @set_mon: set the supported monitors
|
||||
* @common_pmu_map: sets the common PMU map supported by gov
|
||||
* @mon_pmu_map: sets the common PMU map supported by gov
|
||||
* @ratio_ceil: sets the ratio_ceil needed for hw memlat governor
|
||||
* @stall_floor: sets the stall_floor needed for hw memlat governor
|
||||
* @l2wb_pct: sets the stall_floor needed for hw memlat governor
|
||||
* @l2wb_filter: sets the l2wb_filter needed for hw memlat governor
|
||||
* @sample_ms: sets the sample_ms at this interval governor will poll
|
||||
* @freq_map: sets the freq_map of the monitor
|
||||
* @min_freq: sets the min_freq of monitor
|
||||
* @max_freq: sets the max_freq of monitor
|
||||
* @start_mon: starts monitor in rimps
|
||||
* @stop_mon: stops monitor in rimps
|
||||
* @log_level: configure the supported log_level in rimps
|
||||
* @get_data: added for debug purpose gets the data structure information
|
||||
*/
|
||||
struct scmi_memlat_vendor_ops {
|
||||
int (*set_cpu_grp)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type);
|
||||
int (*set_mon)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type);
|
||||
int (*common_pmu_map)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type,
|
||||
u32 nr_rows, void *buf);
|
||||
int (*mon_pmu_map)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type,
|
||||
u32 nr_rows, void *buf);
|
||||
int (*ratio_ceil)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val);
|
||||
int (*stall_floor)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val);
|
||||
int (*l2wb_pct)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val);
|
||||
int (*l2wb_filter)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val);
|
||||
int (*sample_ms)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val);
|
||||
int (*freq_map)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type,
|
||||
u32 nr_rows, void *buf);
|
||||
int (*min_freq)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val);
|
||||
int (*max_freq)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type, u32 val);
|
||||
int (*start_monitor)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type);
|
||||
int (*stop_monitor)(const struct scmi_handle *handle,
|
||||
u32 cpus_mpidr, u32 mon_type);
|
||||
int (*set_log_level)(const struct scmi_handle *handle, u32 val);
|
||||
int (*get_data)(const struct scmi_handle *handle, u8 *buf);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct scmi_handle - Handle returned to ARM SCMI clients for usage.
|
||||
*
|
||||
|
|
@ -236,6 +294,7 @@ struct scmi_handle {
|
|||
struct scmi_power_ops *power_ops;
|
||||
struct scmi_sensor_ops *sensor_ops;
|
||||
struct scmi_reset_ops *reset_ops;
|
||||
struct scmi_memlat_vendor_ops *memlat_ops;
|
||||
/* for protocol internal use */
|
||||
void *perf_priv;
|
||||
void *clk_priv;
|
||||
|
|
@ -252,6 +311,7 @@ enum scmi_std_protocol {
|
|||
SCMI_PROTOCOL_CLOCK = 0x14,
|
||||
SCMI_PROTOCOL_SENSOR = 0x15,
|
||||
SCMI_PROTOCOL_RESET = 0x16,
|
||||
SCMI_PROTOCOL_MEMLAT = 0x80,
|
||||
};
|
||||
|
||||
struct scmi_device {
|
||||
|
|
|
|||
Loading…
Reference in a new issue