/* Copyright (c) 2014-2019, 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 * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #define NR_LPM_LEVELS 8 #define MAXSAMPLES 5 #define CLUST_SMPL_INVLD_TIME 40000 #define DEFAULT_PREMATURE_CNT 3 #define DEFAULT_STDDEV 100 #define DEFAULT_IPI_STDDEV 400 #define DEFAULT_TIMER_ADD 100 #define DEFAULT_IPI_TIMER_ADD 900 #define TIMER_ADD_LOW 100 #define TIMER_ADD_HIGH 1500 #define STDDEV_LOW 100 #define STDDEV_HIGH 1000 #define PREMATURE_CNT_LOW 1 #define PREMATURE_CNT_HIGH 5 struct power_params { uint32_t entry_latency; /* Entry latency */ uint32_t exit_latency; /* Exit latency */ uint32_t min_residency; uint32_t max_residency; }; struct lpm_cpu_level { const char *name; bool use_bc_timer; struct power_params pwr; unsigned int psci_id; bool is_reset; int reset_level; }; struct lpm_cpu { struct list_head list; struct cpumask related_cpus; struct lpm_cpu_level levels[NR_LPM_LEVELS]; int nlevels; unsigned int psci_mode_shift; unsigned int psci_mode_mask; uint32_t ref_stddev; uint32_t ref_premature_cnt; uint32_t tmr_add; bool lpm_prediction; bool ipi_prediction; uint64_t bias; struct cpuidle_driver *drv; struct lpm_cluster *parent; }; struct lpm_level_avail { bool idle_enabled; bool suspend_enabled; uint32_t exit_latency; struct kobject *kobj; struct kobj_attribute idle_enabled_attr; struct kobj_attribute suspend_enabled_attr; struct kobj_attribute latency_attr; void *data; int idx; bool cpu_node; }; struct lpm_cluster_level { const char *level_name; int min_child_level; struct cpumask num_cpu_votes; struct power_params pwr; bool notify_rpm; bool sync_level; struct lpm_level_avail available; unsigned int psci_id; bool is_reset; int reset_level; }; struct cluster_history { uint32_t resi[MAXSAMPLES]; int mode[MAXSAMPLES]; int64_t stime[MAXSAMPLES]; uint32_t hptr; uint32_t hinvalid; uint32_t htmr_wkup; uint64_t entry_time; int entry_idx; int nsamp; int flag; }; struct lpm_cluster { struct list_head list; struct list_head child; const char *cluster_name; unsigned long aff_level; /* Affinity level of the node */ struct lpm_cluster_level levels[NR_LPM_LEVELS]; int nlevels; int min_child_level; int default_level; int last_level; uint32_t tmr_add; bool lpm_prediction; struct list_head cpu; spinlock_t sync_lock; struct cpumask child_cpus; struct cpumask num_children_in_sync; struct lpm_cluster *parent; struct lpm_stats *stats; unsigned int psci_mode_shift; unsigned int psci_mode_mask; struct cluster_history history; struct hrtimer histtimer; }; struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev); void free_cluster_node(struct lpm_cluster *cluster); void cluster_dt_walkthrough(struct lpm_cluster *cluster); int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj); bool lpm_cpu_mode_allow(unsigned int cpu, unsigned int mode, bool from_idle); bool lpm_cluster_mode_allow(struct lpm_cluster *cluster, unsigned int mode, bool from_idle); uint32_t *get_per_cpu_max_residency(int cpu); uint32_t *get_per_cpu_min_residency(int cpu); extern struct lpm_cluster *lpm_root_node; #if defined(CONFIG_SMP) extern DEFINE_PER_CPU(bool, pending_ipi); static inline bool is_IPI_pending(const struct cpumask *mask) { unsigned int cpu; for_each_cpu(cpu, mask) { if per_cpu(pending_ipi, cpu) return true; } return false; } #else static inline bool is_IPI_pending(const struct cpumask *mask) { return false; } #endif