Loading Documentation/devicetree/bindings/devfreq/msmcci-hwmon.txt +3 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,9 @@ Required properties: - qcom,target-dev: The DT device that is monitored by this MSM CCI counter configuration. Optional properties: - qcom,secure_io Indicates register access are secured. Example: qcom,msmcci-hwmon { compatible = "qcom,msmcci-hwmon"; Loading drivers/devfreq/governor_cache_hwmon.c +18 −5 Original line number Diff line number Diff line /* * Copyright (c) 2014, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -53,7 +53,9 @@ static LIST_HEAD(cache_hwmon_list); static DEFINE_MUTEX(list_lock); static int use_cnt; static DEFINE_MUTEX(state_lock); static DEFINE_MUTEX(register_lock); static DEFINE_MUTEX(monitor_lock); #define show_attr(name) \ static ssize_t show_##name(struct device *dev, \ Loading Loading @@ -185,8 +187,12 @@ int update_cache_hwmon(struct cache_hwmon *hwmon) node = df->data; if (!node) return -ENODEV; if (!node->mon_started) mutex_lock(&monitor_lock); if (!node->mon_started) { mutex_unlock(&monitor_lock); return -EBUSY; } dev_dbg(df->dev.parent, "Got update request\n"); devfreq_monitor_stop(df); Loading Loading @@ -216,6 +222,7 @@ int update_cache_hwmon(struct cache_hwmon *hwmon) devfreq_monitor_start(df); mutex_unlock(&monitor_lock); return 0; } Loading Loading @@ -289,8 +296,10 @@ static int start_monitoring(struct devfreq *df) goto err_start; } mutex_lock(&monitor_lock); devfreq_monitor_start(df); node->mon_started = true; mutex_unlock(&monitor_lock); ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group); if (ret) { Loading @@ -301,8 +310,10 @@ static int start_monitoring(struct devfreq *df) return 0; sysfs_fail: mutex_lock(&monitor_lock); node->mon_started = false; devfreq_monitor_stop(df); mutex_unlock(&monitor_lock); hw->stop_hwmon(hw); err_start: df->data = node->orig_data; Loading @@ -317,8 +328,10 @@ static void stop_monitoring(struct devfreq *df) struct cache_hwmon *hw = node->hw; sysfs_remove_group(&df->dev.kobj, &dev_attr_group); mutex_lock(&monitor_lock); node->mon_started = false; devfreq_monitor_stop(df); mutex_unlock(&monitor_lock); hw->stop_hwmon(hw); df->data = node->orig_data; node->orig_data = NULL; Loading Loading @@ -391,13 +404,13 @@ int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon) node->hw = hwmon; node->attr_grp = &dev_attr_group; mutex_lock(&state_lock); mutex_lock(®ister_lock); if (!use_cnt) { ret = devfreq_add_governor(&devfreq_cache_hwmon); if (!ret) use_cnt++; } mutex_unlock(&state_lock); mutex_unlock(®ister_lock); if (!ret) { dev_info(dev, "Cache HWmon governor registered.\n"); Loading drivers/devfreq/msmcci-hwmon.c +63 −23 Original line number Diff line number Diff line Loading @@ -29,14 +29,14 @@ #include <soc/qcom/scm.h> #include "governor_cache_hwmon.h" #define EVNT_SEL(m, i) ((m)->base[i] + 0x0) #define EVNT_CNT_MATCH_VAL(m, i) ((m)->base[i] + 0x18) #define MATCH_FLG(m, i) ((m)->base[i] + 0x30) #define MATCH_FLG_CLR(m, i) ((m)->base[i] + 0x48) #define OVR_FLG(m, i) ((m)->base[i] + 0x60) #define OVR_FLG_CLR(m, i) ((m)->base[i] + 0x78) #define CNT_CTRL(m, i) ((m)->base[i] + 0x94) #define CNT_VALUE(m, i) ((m)->base[i] + 0xAC) #define EVNT_SEL 0x0 #define EVNT_CNT_MATCH_VAL 0x18 #define MATCH_FLG 0x30 #define MATCH_FLG_CLR 0x48 #define OVR_FLG 0x60 #define OVR_FLG_CLR 0x78 #define CNT_CTRL 0x94 #define CNT_VALUE 0xAC #define ENABLE_OVR_FLG BIT(4) #define ENABLE_MATCH_FLG BIT(5) Loading @@ -50,7 +50,11 @@ struct msmcci_hwmon { struct list_head list; phys_addr_t base[MAX_NUM_GROUPS]; union { phys_addr_t phys_base[MAX_NUM_GROUPS]; void * __iomem virt_base[MAX_NUM_GROUPS]; }; int irq[MAX_NUM_GROUPS]; u32 event_sel[MAX_NUM_GROUPS]; int num_counters; Loading @@ -68,6 +72,7 @@ struct msmcci_hwmon { struct cache_hwmon hw; struct device *dev; bool secure_io; }; #define to_mon(ptr) container_of(ptr, struct msmcci_hwmon, hw) Loading @@ -78,12 +83,34 @@ static DEFINE_MUTEX(list_lock); static int use_cnt; static DEFINE_MUTEX(notifier_reg_lock); static inline int write_mon_reg(struct msmcci_hwmon *m, int idx, unsigned long offset, u32 value) { int ret = 0; if (m->secure_io) ret = scm_io_write(m->phys_base[idx] + offset, value); else writel_relaxed(value, m->virt_base[idx] + offset); return ret; } static inline u32 read_mon_reg(struct msmcci_hwmon *m, int idx, unsigned long offset) { if (m->secure_io) return scm_io_read(m->phys_base[idx] + offset); else return readl_relaxed(m->virt_base[idx] + offset); } static int mon_init(struct msmcci_hwmon *m) { int ret, i; for (i = 0; i < m->num_counters; i++) { ret = scm_io_write(EVNT_SEL(m, i), m->event_sel[i]); ret = write_mon_reg(m, i, EVNT_SEL, m->event_sel[i]); if (ret) return ret; } Loading @@ -95,7 +122,7 @@ static void mon_enable(struct msmcci_hwmon *m) int i; for (i = 0; i < m->num_counters; i++) scm_io_write(CNT_CTRL(m, i), CNT_ENABLE); write_mon_reg(m, i, CNT_CTRL, CNT_ENABLE); } static void mon_disable(struct msmcci_hwmon *m) Loading @@ -103,30 +130,30 @@ static void mon_disable(struct msmcci_hwmon *m) int i; for (i = 0; i < m->num_counters; i++) scm_io_write(CNT_CTRL(m, i), CNT_DISABLE); write_mon_reg(m, i, CNT_CTRL, CNT_DISABLE); } static bool mon_is_match_flag_set(struct msmcci_hwmon *m, int idx) { return (bool)scm_io_read(MATCH_FLG(m, idx)); return (bool)read_mon_reg(m, idx, MATCH_FLG); } /* mon_clear_single() can only be called when monitor is disabled */ static void mon_clear_single(struct msmcci_hwmon *m, int idx) { scm_io_write(CNT_CTRL(m, idx), CNT_RESET); scm_io_write(CNT_CTRL(m, idx), CNT_RESET_CLR); write_mon_reg(m, idx, CNT_CTRL, CNT_RESET); write_mon_reg(m, idx, CNT_CTRL, CNT_RESET_CLR); /* reset counter before match/overflow flags are cleared */ mb(); scm_io_write(MATCH_FLG_CLR(m, idx), 1); scm_io_write(MATCH_FLG_CLR(m, idx), 0); scm_io_write(OVR_FLG_CLR(m, idx), 1); scm_io_write(OVR_FLG_CLR(m, idx), 0); write_mon_reg(m, idx, MATCH_FLG_CLR, 1); write_mon_reg(m, idx, MATCH_FLG_CLR, 0); write_mon_reg(m, idx, OVR_FLG_CLR, 1); write_mon_reg(m, idx, OVR_FLG_CLR, 0); } static void mon_set_limit_single(struct msmcci_hwmon *m, int idx, u32 limit) { scm_io_write(EVNT_CNT_MATCH_VAL(m, idx), limit); write_mon_reg(m, idx, EVNT_CNT_MATCH_VAL, limit); } static irqreturn_t msmcci_hwmon_intr_handler(int irq, void *dev) Loading Loading @@ -164,8 +191,8 @@ static unsigned long mon_read_count_single(struct msmcci_hwmon *m, int idx) { unsigned long count, ovr; count = scm_io_read(CNT_VALUE(m, idx)); ovr = scm_io_read(OVR_FLG(m, idx)); count = read_mon_reg(m, idx, CNT_VALUE); ovr = read_mon_reg(m, idx, OVR_FLG); if (ovr == 1) { count += 0xFFFFFFFFUL; dev_warn(m->dev, "Counter[%d]: overflowed\n", idx); Loading Loading @@ -424,7 +451,17 @@ static int msmcci_hwmon_parse_dt(struct platform_device *pdev, res = platform_get_resource(pdev, IORESOURCE_MEM, idx); if (!res) return (idx == HIGH) ? -EINVAL : 0; m->base[idx] = res->start; if (m->secure_io) m->phys_base[idx] = res->start; else { m->virt_base[idx] = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!m->virt_base[idx]) { dev_err(dev, "failed to ioremap\n"); return -ENOMEM; } } ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,counter-event-sel", idx, &sel); Loading Loading @@ -455,6 +492,9 @@ static int msmcci_hwmon_driver_probe(struct platform_device *pdev) return -ENOMEM; m->dev = &pdev->dev; m->secure_io = of_property_read_bool(pdev->dev.of_node, "qcom,secure-io"); ret = msmcci_hwmon_parse_dt(pdev, m, HIGH); if (ret) return ret; Loading Loading
Documentation/devicetree/bindings/devfreq/msmcci-hwmon.txt +3 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,9 @@ Required properties: - qcom,target-dev: The DT device that is monitored by this MSM CCI counter configuration. Optional properties: - qcom,secure_io Indicates register access are secured. Example: qcom,msmcci-hwmon { compatible = "qcom,msmcci-hwmon"; Loading
drivers/devfreq/governor_cache_hwmon.c +18 −5 Original line number Diff line number Diff line /* * Copyright (c) 2014, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -53,7 +53,9 @@ static LIST_HEAD(cache_hwmon_list); static DEFINE_MUTEX(list_lock); static int use_cnt; static DEFINE_MUTEX(state_lock); static DEFINE_MUTEX(register_lock); static DEFINE_MUTEX(monitor_lock); #define show_attr(name) \ static ssize_t show_##name(struct device *dev, \ Loading Loading @@ -185,8 +187,12 @@ int update_cache_hwmon(struct cache_hwmon *hwmon) node = df->data; if (!node) return -ENODEV; if (!node->mon_started) mutex_lock(&monitor_lock); if (!node->mon_started) { mutex_unlock(&monitor_lock); return -EBUSY; } dev_dbg(df->dev.parent, "Got update request\n"); devfreq_monitor_stop(df); Loading Loading @@ -216,6 +222,7 @@ int update_cache_hwmon(struct cache_hwmon *hwmon) devfreq_monitor_start(df); mutex_unlock(&monitor_lock); return 0; } Loading Loading @@ -289,8 +296,10 @@ static int start_monitoring(struct devfreq *df) goto err_start; } mutex_lock(&monitor_lock); devfreq_monitor_start(df); node->mon_started = true; mutex_unlock(&monitor_lock); ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group); if (ret) { Loading @@ -301,8 +310,10 @@ static int start_monitoring(struct devfreq *df) return 0; sysfs_fail: mutex_lock(&monitor_lock); node->mon_started = false; devfreq_monitor_stop(df); mutex_unlock(&monitor_lock); hw->stop_hwmon(hw); err_start: df->data = node->orig_data; Loading @@ -317,8 +328,10 @@ static void stop_monitoring(struct devfreq *df) struct cache_hwmon *hw = node->hw; sysfs_remove_group(&df->dev.kobj, &dev_attr_group); mutex_lock(&monitor_lock); node->mon_started = false; devfreq_monitor_stop(df); mutex_unlock(&monitor_lock); hw->stop_hwmon(hw); df->data = node->orig_data; node->orig_data = NULL; Loading Loading @@ -391,13 +404,13 @@ int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon) node->hw = hwmon; node->attr_grp = &dev_attr_group; mutex_lock(&state_lock); mutex_lock(®ister_lock); if (!use_cnt) { ret = devfreq_add_governor(&devfreq_cache_hwmon); if (!ret) use_cnt++; } mutex_unlock(&state_lock); mutex_unlock(®ister_lock); if (!ret) { dev_info(dev, "Cache HWmon governor registered.\n"); Loading
drivers/devfreq/msmcci-hwmon.c +63 −23 Original line number Diff line number Diff line Loading @@ -29,14 +29,14 @@ #include <soc/qcom/scm.h> #include "governor_cache_hwmon.h" #define EVNT_SEL(m, i) ((m)->base[i] + 0x0) #define EVNT_CNT_MATCH_VAL(m, i) ((m)->base[i] + 0x18) #define MATCH_FLG(m, i) ((m)->base[i] + 0x30) #define MATCH_FLG_CLR(m, i) ((m)->base[i] + 0x48) #define OVR_FLG(m, i) ((m)->base[i] + 0x60) #define OVR_FLG_CLR(m, i) ((m)->base[i] + 0x78) #define CNT_CTRL(m, i) ((m)->base[i] + 0x94) #define CNT_VALUE(m, i) ((m)->base[i] + 0xAC) #define EVNT_SEL 0x0 #define EVNT_CNT_MATCH_VAL 0x18 #define MATCH_FLG 0x30 #define MATCH_FLG_CLR 0x48 #define OVR_FLG 0x60 #define OVR_FLG_CLR 0x78 #define CNT_CTRL 0x94 #define CNT_VALUE 0xAC #define ENABLE_OVR_FLG BIT(4) #define ENABLE_MATCH_FLG BIT(5) Loading @@ -50,7 +50,11 @@ struct msmcci_hwmon { struct list_head list; phys_addr_t base[MAX_NUM_GROUPS]; union { phys_addr_t phys_base[MAX_NUM_GROUPS]; void * __iomem virt_base[MAX_NUM_GROUPS]; }; int irq[MAX_NUM_GROUPS]; u32 event_sel[MAX_NUM_GROUPS]; int num_counters; Loading @@ -68,6 +72,7 @@ struct msmcci_hwmon { struct cache_hwmon hw; struct device *dev; bool secure_io; }; #define to_mon(ptr) container_of(ptr, struct msmcci_hwmon, hw) Loading @@ -78,12 +83,34 @@ static DEFINE_MUTEX(list_lock); static int use_cnt; static DEFINE_MUTEX(notifier_reg_lock); static inline int write_mon_reg(struct msmcci_hwmon *m, int idx, unsigned long offset, u32 value) { int ret = 0; if (m->secure_io) ret = scm_io_write(m->phys_base[idx] + offset, value); else writel_relaxed(value, m->virt_base[idx] + offset); return ret; } static inline u32 read_mon_reg(struct msmcci_hwmon *m, int idx, unsigned long offset) { if (m->secure_io) return scm_io_read(m->phys_base[idx] + offset); else return readl_relaxed(m->virt_base[idx] + offset); } static int mon_init(struct msmcci_hwmon *m) { int ret, i; for (i = 0; i < m->num_counters; i++) { ret = scm_io_write(EVNT_SEL(m, i), m->event_sel[i]); ret = write_mon_reg(m, i, EVNT_SEL, m->event_sel[i]); if (ret) return ret; } Loading @@ -95,7 +122,7 @@ static void mon_enable(struct msmcci_hwmon *m) int i; for (i = 0; i < m->num_counters; i++) scm_io_write(CNT_CTRL(m, i), CNT_ENABLE); write_mon_reg(m, i, CNT_CTRL, CNT_ENABLE); } static void mon_disable(struct msmcci_hwmon *m) Loading @@ -103,30 +130,30 @@ static void mon_disable(struct msmcci_hwmon *m) int i; for (i = 0; i < m->num_counters; i++) scm_io_write(CNT_CTRL(m, i), CNT_DISABLE); write_mon_reg(m, i, CNT_CTRL, CNT_DISABLE); } static bool mon_is_match_flag_set(struct msmcci_hwmon *m, int idx) { return (bool)scm_io_read(MATCH_FLG(m, idx)); return (bool)read_mon_reg(m, idx, MATCH_FLG); } /* mon_clear_single() can only be called when monitor is disabled */ static void mon_clear_single(struct msmcci_hwmon *m, int idx) { scm_io_write(CNT_CTRL(m, idx), CNT_RESET); scm_io_write(CNT_CTRL(m, idx), CNT_RESET_CLR); write_mon_reg(m, idx, CNT_CTRL, CNT_RESET); write_mon_reg(m, idx, CNT_CTRL, CNT_RESET_CLR); /* reset counter before match/overflow flags are cleared */ mb(); scm_io_write(MATCH_FLG_CLR(m, idx), 1); scm_io_write(MATCH_FLG_CLR(m, idx), 0); scm_io_write(OVR_FLG_CLR(m, idx), 1); scm_io_write(OVR_FLG_CLR(m, idx), 0); write_mon_reg(m, idx, MATCH_FLG_CLR, 1); write_mon_reg(m, idx, MATCH_FLG_CLR, 0); write_mon_reg(m, idx, OVR_FLG_CLR, 1); write_mon_reg(m, idx, OVR_FLG_CLR, 0); } static void mon_set_limit_single(struct msmcci_hwmon *m, int idx, u32 limit) { scm_io_write(EVNT_CNT_MATCH_VAL(m, idx), limit); write_mon_reg(m, idx, EVNT_CNT_MATCH_VAL, limit); } static irqreturn_t msmcci_hwmon_intr_handler(int irq, void *dev) Loading Loading @@ -164,8 +191,8 @@ static unsigned long mon_read_count_single(struct msmcci_hwmon *m, int idx) { unsigned long count, ovr; count = scm_io_read(CNT_VALUE(m, idx)); ovr = scm_io_read(OVR_FLG(m, idx)); count = read_mon_reg(m, idx, CNT_VALUE); ovr = read_mon_reg(m, idx, OVR_FLG); if (ovr == 1) { count += 0xFFFFFFFFUL; dev_warn(m->dev, "Counter[%d]: overflowed\n", idx); Loading Loading @@ -424,7 +451,17 @@ static int msmcci_hwmon_parse_dt(struct platform_device *pdev, res = platform_get_resource(pdev, IORESOURCE_MEM, idx); if (!res) return (idx == HIGH) ? -EINVAL : 0; m->base[idx] = res->start; if (m->secure_io) m->phys_base[idx] = res->start; else { m->virt_base[idx] = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!m->virt_base[idx]) { dev_err(dev, "failed to ioremap\n"); return -ENOMEM; } } ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,counter-event-sel", idx, &sel); Loading Loading @@ -455,6 +492,9 @@ static int msmcci_hwmon_driver_probe(struct platform_device *pdev) return -ENOMEM; m->dev = &pdev->dev; m->secure_io = of_property_read_bool(pdev->dev.of_node, "qcom,secure-io"); ret = msmcci_hwmon_parse_dt(pdev, m, HIGH); if (ret) return ret; Loading