PM / devfreq: Refactor Cache HWmon governor to be more generic
The refactor allows the governor to support multiple devfreq devices. This is done by having different HW monitor instances register their capability to monitor different devfreq devices and then picking the right HW monitor based on which devfreq device is using this governor. Change-Id: I72c0542ce97f3965e422df521e0ce86cad218d93 Signed-off-by: Junjie Wu <junjiew@codeaurora.org>
This commit is contained in:
parent
9be0c841a1
commit
822ccc45e4
4 changed files with 224 additions and 80 deletions
20
Documentation/devicetree/bindings/arm/msm/kraitbw-l2pm.txt
Normal file
20
Documentation/devicetree/bindings/arm/msm/kraitbw-l2pm.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
MSM Krait L2 performance monitor counters for bandwidth measurement device
|
||||
|
||||
krait-l2pm is a device that represents the Krait L2 PM counters that can be
|
||||
used to measure the bandwidth of read/write traffic from the Krait CPU
|
||||
subsystem.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "qcom,kraitbw-l2pm"
|
||||
- interrupts: Lists the L2 PM counter overflow IRQ.
|
||||
- qcom,bytes-per-beat: The number of bytes transferred in one data beat from
|
||||
the Krait CPU subsystem.
|
||||
- qcom,target-dev: Target device for cache scaling
|
||||
|
||||
Example:
|
||||
qcom,kraitbw-l2pm {
|
||||
compatible = "qcom,kraitbw-l2pm";
|
||||
interrupts = <0 1 1>;
|
||||
qcom,bytes-per-beat = <8>;
|
||||
qcom,target-dev = <&cache>;
|
||||
};
|
|
@ -31,11 +31,36 @@
|
|||
#include "governor.h"
|
||||
#include "governor_cache_hwmon.h"
|
||||
|
||||
struct cache_hwmon_node {
|
||||
unsigned int cycles_per_low_req;
|
||||
unsigned int cycles_per_med_req;
|
||||
unsigned int cycles_per_high_req;
|
||||
unsigned int min_busy;
|
||||
unsigned int max_busy;
|
||||
unsigned int tolerance_mrps;
|
||||
unsigned int guard_band_mhz;
|
||||
unsigned int decay_rate;
|
||||
unsigned long prev_mhz;
|
||||
ktime_t prev_ts;
|
||||
struct list_head list;
|
||||
void *orig_data;
|
||||
struct cache_hwmon *hw;
|
||||
struct attribute_group *attr_grp;
|
||||
};
|
||||
|
||||
static LIST_HEAD(cache_hwmon_list);
|
||||
static DEFINE_MUTEX(list_lock);
|
||||
|
||||
static int use_cnt;
|
||||
static DEFINE_MUTEX(state_lock);
|
||||
|
||||
#define show_attr(name) \
|
||||
static ssize_t show_##name(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", name); \
|
||||
struct devfreq *df = to_devfreq(dev); \
|
||||
struct cache_hwmon_node *hw = df->data; \
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", hw->name); \
|
||||
}
|
||||
|
||||
#define store_attr(name, _min, _max) \
|
||||
|
@ -45,12 +70,14 @@ static ssize_t store_##name(struct device *dev, \
|
|||
{ \
|
||||
int ret; \
|
||||
unsigned int val; \
|
||||
struct devfreq *df = to_devfreq(dev); \
|
||||
struct cache_hwmon_node *hw = df->data; \
|
||||
ret = sscanf(buf, "%u", &val); \
|
||||
if (ret != 1) \
|
||||
return -EINVAL; \
|
||||
val = max(val, _min); \
|
||||
val = min(val, _max); \
|
||||
name = val; \
|
||||
hw->name = val; \
|
||||
return count; \
|
||||
}
|
||||
|
||||
|
@ -59,28 +86,31 @@ show_attr(__attr) \
|
|||
store_attr(__attr, min, max) \
|
||||
static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr)
|
||||
|
||||
|
||||
static struct cache_hwmon *hw;
|
||||
static unsigned int cycles_per_low_req;
|
||||
static unsigned int cycles_per_med_req = 20;
|
||||
static unsigned int cycles_per_high_req = 35;
|
||||
static unsigned int min_busy = 100;
|
||||
static unsigned int max_busy = 100;
|
||||
static unsigned int tolerance_mrps = 5;
|
||||
static unsigned int guard_band_mhz = 100;
|
||||
static unsigned int decay_rate = 90;
|
||||
|
||||
#define MIN_MS 10U
|
||||
#define MAX_MS 500U
|
||||
static unsigned int sample_ms = 50;
|
||||
static unsigned long prev_mhz;
|
||||
static ktime_t prev_ts;
|
||||
|
||||
static unsigned long measure_mrps_and_set_irq(struct devfreq *df,
|
||||
static struct cache_hwmon_node *find_hwmon_node(struct devfreq *df)
|
||||
{
|
||||
struct cache_hwmon_node *node, *found = NULL;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
list_for_each_entry(node, &cache_hwmon_list, list)
|
||||
if (node->hw->dev == df->dev.parent ||
|
||||
node->hw->of_node == df->dev.parent->of_node) {
|
||||
found = node;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static unsigned long measure_mrps_and_set_irq(struct cache_hwmon_node *node,
|
||||
struct mrps_stats *stat)
|
||||
{
|
||||
ktime_t ts;
|
||||
unsigned int us;
|
||||
struct cache_hwmon *hw = node->hw;
|
||||
|
||||
/*
|
||||
* Since we are stopping the counters, we don't want this short work
|
||||
|
@ -92,59 +122,63 @@ static unsigned long measure_mrps_and_set_irq(struct devfreq *df,
|
|||
preempt_disable();
|
||||
|
||||
ts = ktime_get();
|
||||
us = ktime_to_us(ktime_sub(ts, prev_ts));
|
||||
us = ktime_to_us(ktime_sub(ts, node->prev_ts));
|
||||
if (!us)
|
||||
us = 1;
|
||||
|
||||
hw->meas_mrps_and_set_irq(df, tolerance_mrps, us, stat);
|
||||
prev_ts = ts;
|
||||
hw->meas_mrps_and_set_irq(hw, node->tolerance_mrps, us, stat);
|
||||
node->prev_ts = ts;
|
||||
|
||||
preempt_enable();
|
||||
|
||||
pr_debug("stat H=%3lu, M=%3lu, T=%3lu, b=%3u, f=%4lu, us=%d\n",
|
||||
dev_dbg(hw->df->dev.parent,
|
||||
"stat H=%3lu, M=%3lu, T=%3lu, b=%3u, f=%4lu, us=%d\n",
|
||||
stat->high, stat->med, stat->high + stat->med,
|
||||
stat->busy_percent, df->previous_freq / 1000, us);
|
||||
stat->busy_percent, hw->df->previous_freq / 1000, us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void compute_cache_freq(struct mrps_stats *mrps, unsigned long *freq)
|
||||
static void compute_cache_freq(struct cache_hwmon_node *node,
|
||||
struct mrps_stats *mrps, unsigned long *freq)
|
||||
{
|
||||
unsigned long new_mhz;
|
||||
unsigned int busy;
|
||||
|
||||
new_mhz = mrps->high * cycles_per_high_req
|
||||
+ mrps->med * cycles_per_med_req
|
||||
+ mrps->low * cycles_per_low_req;
|
||||
new_mhz = mrps->high * node->cycles_per_high_req
|
||||
+ mrps->med * node->cycles_per_med_req
|
||||
+ mrps->low * node->cycles_per_low_req;
|
||||
|
||||
busy = max(min_busy, mrps->busy_percent);
|
||||
busy = min(max_busy, busy);
|
||||
busy = max(node->min_busy, mrps->busy_percent);
|
||||
busy = min(node->max_busy, busy);
|
||||
|
||||
new_mhz *= 100;
|
||||
new_mhz /= busy;
|
||||
|
||||
if (new_mhz < prev_mhz) {
|
||||
new_mhz = new_mhz * decay_rate + prev_mhz * (100 - decay_rate);
|
||||
if (new_mhz < node->prev_mhz) {
|
||||
new_mhz = new_mhz * node->decay_rate + node->prev_mhz
|
||||
* (100 - node->decay_rate);
|
||||
new_mhz /= 100;
|
||||
}
|
||||
prev_mhz = new_mhz;
|
||||
node->prev_mhz = new_mhz;
|
||||
|
||||
new_mhz += guard_band_mhz;
|
||||
new_mhz += node->guard_band_mhz;
|
||||
*freq = new_mhz * 1000;
|
||||
}
|
||||
|
||||
#define TOO_SOON_US (1 * USEC_PER_MSEC)
|
||||
static irqreturn_t mon_intr_handler(int irq, void *dev)
|
||||
{
|
||||
struct devfreq *df = dev;
|
||||
struct cache_hwmon_node *node = dev;
|
||||
struct devfreq *df = node->hw->df;
|
||||
ktime_t ts;
|
||||
unsigned int us;
|
||||
int ret;
|
||||
|
||||
if (!hw->is_valid_irq(df))
|
||||
if (!node->hw->is_valid_irq(node->hw))
|
||||
return IRQ_NONE;
|
||||
|
||||
pr_debug("Got interrupt\n");
|
||||
dev_dbg(df->dev.parent, "Got interrupt\n");
|
||||
devfreq_monitor_stop(df);
|
||||
|
||||
/*
|
||||
|
@ -160,12 +194,13 @@ static irqreturn_t mon_intr_handler(int irq, void *dev)
|
|||
* readjusted.
|
||||
*/
|
||||
ts = ktime_get();
|
||||
us = ktime_to_us(ktime_sub(ts, prev_ts));
|
||||
us = ktime_to_us(ktime_sub(ts, node->prev_ts));
|
||||
if (us > TOO_SOON_US) {
|
||||
mutex_lock(&df->lock);
|
||||
ret = update_devfreq(df);
|
||||
if (ret)
|
||||
pr_err("Unable to update freq on IRQ!\n");
|
||||
dev_err(df->dev.parent,
|
||||
"Unable to update freq on IRQ!\n");
|
||||
mutex_unlock(&df->lock);
|
||||
}
|
||||
|
||||
|
@ -179,9 +214,11 @@ static int devfreq_cache_hwmon_get_freq(struct devfreq *df,
|
|||
u32 *flag)
|
||||
{
|
||||
struct mrps_stats stat;
|
||||
struct cache_hwmon_node *node = df->data;
|
||||
|
||||
measure_mrps_and_set_irq(df, &stat);
|
||||
compute_cache_freq(&stat, freq);
|
||||
memset(&stat, 0, sizeof(stat));
|
||||
measure_mrps_and_set_irq(node, &stat);
|
||||
compute_cache_freq(node, &stat, freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -216,58 +253,88 @@ static int start_monitoring(struct devfreq *df)
|
|||
{
|
||||
int ret;
|
||||
struct mrps_stats mrps;
|
||||
struct device *dev = df->dev.parent;
|
||||
struct cache_hwmon_node *node;
|
||||
struct cache_hwmon *hw;
|
||||
|
||||
prev_ts = ktime_get();
|
||||
prev_mhz = 0;
|
||||
mrps.high = (df->previous_freq / 1000) - guard_band_mhz;
|
||||
mrps.high /= cycles_per_high_req;
|
||||
node = find_hwmon_node(df);
|
||||
if (!node) {
|
||||
dev_err(dev, "Unable to find HW monitor!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
hw = node->hw;
|
||||
hw->df = df;
|
||||
node->orig_data = df->data;
|
||||
df->data = node;
|
||||
|
||||
ret = hw->start_hwmon(df, &mrps);
|
||||
node->prev_ts = ktime_get();
|
||||
node->prev_mhz = 0;
|
||||
mrps.high = (df->previous_freq / 1000) - node->guard_band_mhz;
|
||||
mrps.high /= node->cycles_per_high_req;
|
||||
mrps.med = mrps.low = 0;
|
||||
|
||||
ret = hw->start_hwmon(hw, &mrps);
|
||||
if (ret) {
|
||||
pr_err("Unable to start HW monitor!\n");
|
||||
return ret;
|
||||
dev_err(dev, "Unable to start HW monitor!\n");
|
||||
goto err_start;
|
||||
}
|
||||
|
||||
devfreq_monitor_start(df);
|
||||
|
||||
if (hw->irq)
|
||||
ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler,
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
"cache_hwmon", df);
|
||||
"cache_hwmon", node);
|
||||
if (ret) {
|
||||
pr_err("Unable to register interrupt handler!\n");
|
||||
dev_err(dev, "Unable to register interrupt handler!\n");
|
||||
goto req_irq_fail;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group);
|
||||
if (ret) {
|
||||
pr_err("Error creating sys entries!\n");
|
||||
dev_err(dev, "Error creating sys entries!\n");
|
||||
goto sysfs_fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sysfs_fail:
|
||||
if (hw->irq) {
|
||||
disable_irq(hw->irq);
|
||||
free_irq(hw->irq, df);
|
||||
free_irq(hw->irq, node);
|
||||
}
|
||||
req_irq_fail:
|
||||
devfreq_monitor_stop(df);
|
||||
hw->stop_hwmon(df);
|
||||
hw->stop_hwmon(hw);
|
||||
err_start:
|
||||
df->data = node->orig_data;
|
||||
node->orig_data = NULL;
|
||||
hw->df = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stop_monitoring(struct devfreq *df)
|
||||
{
|
||||
struct cache_hwmon_node *node = df->data;
|
||||
struct cache_hwmon *hw = node->hw;
|
||||
|
||||
sysfs_remove_group(&df->dev.kobj, &dev_attr_group);
|
||||
if (hw->irq) {
|
||||
disable_irq(hw->irq);
|
||||
free_irq(hw->irq, df);
|
||||
free_irq(hw->irq, node);
|
||||
}
|
||||
devfreq_monitor_stop(df);
|
||||
hw->stop_hwmon(df);
|
||||
hw->stop_hwmon(hw);
|
||||
df->data = node->orig_data;
|
||||
node->orig_data = NULL;
|
||||
hw->df = NULL;
|
||||
}
|
||||
|
||||
static int devfreq_cache_hwmon_ev_handler(struct devfreq *df,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret;
|
||||
unsigned int sample_ms;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
|
@ -280,12 +347,12 @@ static int devfreq_cache_hwmon_ev_handler(struct devfreq *df,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
pr_debug("Enabled Cache HW monitor governor\n");
|
||||
dev_dbg(df->dev.parent, "Enabled Cache HW monitor governor\n");
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_STOP:
|
||||
stop_monitoring(df);
|
||||
pr_debug("Disabled Cache HW monitor governor\n");
|
||||
dev_dbg(df->dev.parent, "Disabled Cache HW monitor governor\n");
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_INTERVAL:
|
||||
|
@ -305,18 +372,50 @@ static struct devfreq_governor devfreq_cache_hwmon = {
|
|||
.event_handler = devfreq_cache_hwmon_ev_handler,
|
||||
};
|
||||
|
||||
int register_cache_hwmon(struct cache_hwmon *hwmon)
|
||||
int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
struct cache_hwmon_node *node;
|
||||
|
||||
hw = hwmon;
|
||||
if (!hwmon->dev && !hwmon->of_node)
|
||||
return -EINVAL;
|
||||
|
||||
node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
|
||||
if (!node) {
|
||||
dev_err(dev, "Unable to register gov. Out of memory!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
node->cycles_per_med_req = 20;
|
||||
node->cycles_per_high_req = 35;
|
||||
node->min_busy = 100;
|
||||
node->max_busy = 100;
|
||||
node->tolerance_mrps = 5;
|
||||
node->guard_band_mhz = 100;
|
||||
node->decay_rate = 90;
|
||||
node->hw = hwmon;
|
||||
node->attr_grp = &dev_attr_group;
|
||||
|
||||
mutex_lock(&state_lock);
|
||||
if (!use_cnt) {
|
||||
ret = devfreq_add_governor(&devfreq_cache_hwmon);
|
||||
if (ret) {
|
||||
pr_err("devfreq governor registration failed\n");
|
||||
if (!ret)
|
||||
use_cnt++;
|
||||
}
|
||||
mutex_unlock(&state_lock);
|
||||
|
||||
if (!ret) {
|
||||
dev_info(dev, "Cache HWmon governor registered.\n");
|
||||
} else {
|
||||
dev_err(dev, "Failed to add Cache HWmon governor\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
mutex_lock(&list_lock);
|
||||
list_add_tail(&node->list, &cache_hwmon_list);
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("HW monitor based cache freq driver");
|
||||
|
|
|
@ -24,20 +24,40 @@ struct mrps_stats {
|
|||
unsigned int busy_percent;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cache_hwmon - devfreq Cache HW monitor info
|
||||
* @start_hwmon: Start the HW monitoring
|
||||
* @stop_hwmon: Stop the HW monitoring
|
||||
* @is_valid_irq: Check whether the IRQ was triggered by the counter
|
||||
* used to monitor cache activity.
|
||||
* @meas_mrps_and_set_irq: Return the measured count and set up the
|
||||
* IRQ to fire if usage exceeds current
|
||||
* measurement by @tol percent.
|
||||
* @irq: IRQ number that corresponds to this HW monitor.
|
||||
* @dev: device that this HW monitor can monitor.
|
||||
* @of_node: OF node of device that this HW monitor can monitor.
|
||||
* @df: Devfreq node that this HW montior is being used
|
||||
* for. NULL when not actively in use, and non-NULL
|
||||
* when in use.
|
||||
*/
|
||||
struct cache_hwmon {
|
||||
int (*start_hwmon)(struct devfreq *df, struct mrps_stats *mrps);
|
||||
void (*stop_hwmon)(struct devfreq *df);
|
||||
bool (*is_valid_irq)(struct devfreq *df);
|
||||
unsigned long (*meas_mrps_and_set_irq)(struct devfreq *df,
|
||||
int (*start_hwmon)(struct cache_hwmon *hw, struct mrps_stats *mrps);
|
||||
void (*stop_hwmon)(struct cache_hwmon *hw);
|
||||
bool (*is_valid_irq)(struct cache_hwmon *hw);
|
||||
unsigned long (*meas_mrps_and_set_irq)(struct cache_hwmon *hw,
|
||||
unsigned int tol, unsigned int us,
|
||||
struct mrps_stats *mrps);
|
||||
int irq;
|
||||
struct device *dev;
|
||||
struct device_node *of_node;
|
||||
struct devfreq *df;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_GOV_MSM_CACHE_HWMON
|
||||
int register_cache_hwmon(struct cache_hwmon *hwmon);
|
||||
int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon);
|
||||
#else
|
||||
static inline int register_cache_hwmon(struct cache_hwmon *hwmon)
|
||||
static inline int register_cache_hwmon(struct device *dev,
|
||||
struct cache_hwmon *hwmon)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -298,13 +298,13 @@ static unsigned int mrps_to_count(unsigned int mrps, unsigned int ms,
|
|||
return mrps;
|
||||
}
|
||||
|
||||
static unsigned long meas_mrps_and_set_irq(struct devfreq *df,
|
||||
static unsigned long meas_mrps_and_set_irq(struct cache_hwmon *hw,
|
||||
unsigned int tol, unsigned int us,
|
||||
struct mrps_stats *mrps)
|
||||
{
|
||||
u32 limit;
|
||||
unsigned int sample_ms = df->profile->polling_ms;
|
||||
unsigned long f = df->previous_freq;
|
||||
unsigned int sample_ms = hw->df->profile->polling_ms;
|
||||
unsigned long f = hw->df->previous_freq;
|
||||
unsigned long t_mrps, m_mrps, l2_cyc;
|
||||
|
||||
mon_disable(L2_H_REQ_MON);
|
||||
|
@ -335,12 +335,12 @@ static unsigned long meas_mrps_and_set_irq(struct devfreq *df,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool is_valid_mrps_irq(struct devfreq *df)
|
||||
static bool is_valid_mrps_irq(struct cache_hwmon *hw)
|
||||
{
|
||||
return mon_overflow(L2_H_REQ_MON) || mon_overflow(L2_M_REQ_MON);
|
||||
}
|
||||
|
||||
static int start_mrps_hwmon(struct devfreq *df, struct mrps_stats *mrps)
|
||||
static int start_mrps_hwmon(struct cache_hwmon *hw, struct mrps_stats *mrps)
|
||||
{
|
||||
u32 limit;
|
||||
|
||||
|
@ -349,7 +349,7 @@ static int start_mrps_hwmon(struct devfreq *df, struct mrps_stats *mrps)
|
|||
mon_disable(L2_M_REQ_MON);
|
||||
mon_disable(L2_CYC_MON);
|
||||
|
||||
limit = mrps_to_count(mrps->high, df->profile->polling_ms, 0);
|
||||
limit = mrps_to_count(mrps->high, hw->df->profile->polling_ms, 0);
|
||||
prev_req_start_val = mon_set_limit(L2_H_REQ_MON, limit);
|
||||
mon_set_limit(L2_M_REQ_MON, 0xFFFFFFFF);
|
||||
mon_set_limit(L2_CYC_MON, 0xFFFFFFFF);
|
||||
|
@ -364,7 +364,7 @@ static int start_mrps_hwmon(struct devfreq *df, struct mrps_stats *mrps)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void stop_mrps_hwmon(struct devfreq *df)
|
||||
static void stop_mrps_hwmon(struct cache_hwmon *hw)
|
||||
{
|
||||
global_mon_enable(false);
|
||||
mon_disable(L2_H_REQ_MON);
|
||||
|
@ -393,7 +393,6 @@ static int krait_l2pm_driver_probe(struct platform_device *pdev)
|
|||
pr_err("Unable to get IRQ number\n");
|
||||
return bw_irq;
|
||||
}
|
||||
mrps_hwmon.irq = bw_irq;
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "qcom,bytes-per-beat",
|
||||
&bytes_per_beat);
|
||||
|
@ -406,7 +405,13 @@ static int krait_l2pm_driver_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
pr_err("CPUBW hwmon registration failed\n");
|
||||
|
||||
ret2 = register_cache_hwmon(&mrps_hwmon);
|
||||
mrps_hwmon.irq = bw_irq;
|
||||
mrps_hwmon.of_node = of_parse_phandle(dev->of_node, "qcom,target-dev",
|
||||
0);
|
||||
if (!mrps_hwmon.of_node)
|
||||
return -EINVAL;
|
||||
|
||||
ret2 = register_cache_hwmon(dev, &mrps_hwmon);
|
||||
if (ret2)
|
||||
pr_err("Cache hwmon registration failed\n");
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue