Currently each driver type sets up own CPU power notifications such as CPU hotplug etc. Need a common mechanism for all driver types to use.
Adds a notification mechanism for CPU power events such as CPU hotplug as a general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-priv.h | 26 +++ drivers/hwtracing/coresight/coresight.c | 167 ++++++++++++++++++- 2 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9c4fd7eb56eb..5ea2f86f71e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -214,4 +214,30 @@ void coresight_release_platform_data(struct coresight_platform_data *pdata);
int coresight_device_fwnode_match(struct device *dev, void *fwnode);
+/* + * cpu power management - allow any CPU bound Coresight device + * to use a common registration infrastructure for notifications. + */ + +/* cpu pm event types */ +enum cs_cpu_pm_events { + CS_CPUPM_SMP_ON_REG_CPU, /* smp call on cpu at registration */ + CS_CPUPM_CPUHP_CS_START, /* hotplug CoreSight starting CPU */ + CS_CPUPM_CPUHP_CS_STOP, /* hotplug CoreSight stopping CPU */ + CS_CPUPM_CPUHP_AP_DYN_ONLINE, /* hotplug AP dynamic online CPU */ + CS_CPUPM_CPUHP_AP_DYN_OFFLINE, /* hotplug AP dynamic offline CPU */ +}; + +/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev, + unsigned int cpuid, + enum cs_cpu_pm_events event); + +/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev, + unsigned int cpuid, + p_cs_cpu_pm_event callback); +void coresight_cpupm_unregister(struct coresight_device *csdev, + unsigned int cpuid); + #endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 74661fabe098..509fc0b8329d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1347,7 +1347,6 @@ void coresight_unregister(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(coresight_unregister);
- /* * coresight_search_device_idx - Search the fwnode handle of a device * in the given dev_idx list. Must be called with the coresight_mutex held. @@ -1404,3 +1403,169 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name); + +/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item { + struct coresight_device *csdev; + p_cs_cpu_pm_event callback; + struct list_head node; +}; + +/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier); + +static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt; + +static int coresight_iterate_cpu_event_list(unsigned int cpu, + enum cs_cpu_pm_events event) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item; + + mutex_lock(&coresight_mutex); + cpu_list = per_cpu(cs_cpupm_notifier, cpu); + if (!list_empty(cpu_list)) { + list_for_each_entry(item, cpu_list, node) { + item->callback(item->csdev, cpu, event); + } + } + mutex_unlock(&coresight_mutex); + return 0; +} + +static int coresight_hp_starting_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_START); +} + +static int coresight_hp_stopping_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_STOP); +} + +static int coresight_hp_dyn_online_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, + CS_CPUPM_CPUHP_AP_DYN_ONLINE); +} + +static int coresight_hp_dyn_offline_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, + CS_CPUPM_CPUHP_AP_DYN_OFFLINE); +} + +struct cs_smp_reg_cb_info { + unsigned int cpu; + struct coresight_cpupm_item *item; +}; + +static void coresight_smp_onreg_callback(void *info) +{ + struct cs_smp_reg_cb_info *cb_info = info; + + cb_info->item->callback(cb_info->item->csdev, cb_info->cpu, + CS_CPUPM_SMP_ON_REG_CPU); +} + +int coresight_cpupm_register(struct coresight_device *csdev, + unsigned int cpuid, + p_cs_cpu_pm_event callback) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item; + int err = 0; + struct cs_smp_reg_cb_info on_reg_cb_info; + + mutex_lock(&coresight_mutex); + + cpu_list = per_cpu(cs_cpupm_notifier, cpuid); + + /* init list if no head yet */ + if (!cpu_list) { + cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (!cpu_list) + goto cpupm_reg_done; + else + INIT_LIST_HEAD(cpu_list); + } + + /* create a list item */ + item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL); + if (!item) { + err = -ENOMEM; + goto cpupm_reg_done; + } + + item->csdev = csdev; + item->callback = callback; + + /* init the cpu hotplug notification */ + cpus_read_lock(); + if (!cscpu_dev_reg_cnt) { + cpuhp_setup_state_nocalls_cpuslocked( + CPUHP_AP_ARM_CORESIGHT_STARTING, + "arm/coresightcpu:starting", + coresight_hp_starting_cpu, + coresight_hp_stopping_cpu); + + cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked( + CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online", + coresight_hp_dyn_online_cpu, + coresight_hp_dyn_offline_cpu); + + if (cscpu_hp_online < 0) { + err = cscpu_hp_online; + cpuhp_remove_state_nocalls( + CPUHP_AP_ARM_CORESIGHT_STARTING); + kfree(item); + cscpu_hp_online = 0; + goto cpupm_hp_done; + } + } + cscpu_dev_reg_cnt++; + list_add(&item->node, cpu_list); + + /* smp callback on dev cpu @ registration */ + on_reg_cb_info.item = item; + on_reg_cb_info.cpu = cpuid; + smp_call_function_single(cpuid, coresight_smp_onreg_callback, + &on_reg_cb_info, 1); + +cpupm_hp_done: + cpus_read_unlock(); + +cpupm_reg_done: + mutex_unlock(&coresight_mutex); + return err; +} +EXPORT_SYMBOL_GPL(coresight_cpupm_register); + +void coresight_cpupm_unregister(struct coresight_device *csdev, + unsigned int cpuid) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item, *tmp; + + mutex_lock(&coresight_mutex); + cpu_list = per_cpu(cs_cpupm_notifier, cpuid); + if (!list_empty(cpu_list)) { + list_for_each_entry_safe(item, tmp, cpu_list, node) { + if (item->csdev == csdev) { + list_del(&item->node); + cscpu_dev_reg_cnt--; + kfree(item); + goto cs_cpupm_item_removed; + } + } + } +cs_cpupm_item_removed: + if (!cscpu_dev_reg_cnt) { + cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); + if (cscpu_hp_online) + cpuhp_remove_state_nocalls(cscpu_hp_online); + } + mutex_unlock(&coresight_mutex); +} +EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);