mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-09-21 11:53:01 +00:00
Merge "PM / devfreq: Refactor Cache HWmon governor to be more generic"
This commit is contained in:
commit
839d832eec
|
@ -9,10 +9,12 @@ Required properties:
|
|||
- 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>;
|
||||
};
|
||||
|
|
|
@ -4214,6 +4214,7 @@
|
|||
compatible = "qcom,kraitbw-l2pm";
|
||||
interrupts = <0 1 1>;
|
||||
qcom,bytes-per-beat = <16>;
|
||||
qcom,target-dev = <&cache>;
|
||||
};
|
||||
|
||||
devfreq-cpufreq {
|
||||
|
|
|
@ -1496,6 +1496,7 @@
|
|||
compatible = "qcom,kraitbw-l2pm";
|
||||
interrupts = <0 1 1>;
|
||||
qcom,bytes-per-beat = <8>;
|
||||
qcom,target-dev = <&cache>;
|
||||
};
|
||||
|
||||
devfreq-cpufreq {
|
||||
|
|
|
@ -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);
|
||||
|
||||
ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler,
|
||||
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:
|
||||
disable_irq(hw->irq);
|
||||
free_irq(hw->irq, df);
|
||||
if (hw->irq) {
|
||||
disable_irq(hw->irq);
|
||||
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);
|
||||
disable_irq(hw->irq);
|
||||
free_irq(hw->irq, df);
|
||||
if (hw->irq) {
|
||||
disable_irq(hw->irq);
|
||||
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;
|
||||
ret = devfreq_add_governor(&devfreq_cache_hwmon);
|
||||
if (ret) {
|
||||
pr_err("devfreq governor registration failed\n");
|
||||
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)
|
||||
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…
Reference in a new issue