/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
 */

#ifndef __QCOM_LPM_H__
#define __QCOM_LPM_H__

#define MAX_LPM_CPUS		8
#define MAXSAMPLES		5
#define PRED_TIMER_ADD		100
#define PRED_PREMATURE_CNT	3
#define PRED_REF_STDDEV		500
#define CLUST_SMPL_INVLD_TIME	40000
#define MAX_CLUSTER_STATES	4

extern bool sleep_disabled;
extern bool prediction_disabled;

struct qcom_cluster_node {
	struct lpm_cluster *cluster;
	struct kobject *kobj;
	int state_idx;
	struct kobj_attribute disable_attr;
	struct attribute_group *attr_group;
	struct attribute **attrs;
};

struct history_lpm {
	int mode[MAXSAMPLES];
	uint32_t resi[MAXSAMPLES];
	int nsamp;
	uint32_t samples_idx;
};

struct history_ipi {
	uint32_t interval[MAXSAMPLES];
	uint32_t current_ptr;
	ktime_t cpu_idle_resched_ts;
};

struct lpm_cpu {
	int cpu;
	int enable;
	int last_idx;
	struct notifier_block nb;
	struct cpuidle_driver *drv;
	struct cpuidle_device *dev;
	ktime_t next_wakeup;
	uint64_t predicted;
	uint32_t history_invalid;
	bool predict_started;
	bool htmr_wkup;
	struct hrtimer histtimer;
	struct hrtimer biastimer;
	struct history_lpm lpm_history;
	struct history_ipi ipi_history;
	ktime_t now;
	uint64_t bias;
	int64_t next_pred_time;
	uint32_t pred_type;
	bool ipi_pending;
	spinlock_t lock;
};

struct cluster_history {
	uint64_t residency;
	int mode;
	uint64_t entry_time;
};

struct lpm_cluster {
	struct device *dev;
	uint32_t samples_idx;
	bool history_invalid;
	bool htmr_wkup;
	int entry_idx;
	int nsamp;
	struct cluster_history history[MAXSAMPLES];
	struct generic_pm_domain *genpd;
	struct qcom_cluster_node *dev_node[MAX_CLUSTER_STATES];
	struct kobject *dev_kobj;
	struct notifier_block genpd_nb;
	struct work_struct work;
	struct hrtimer histtimer;
	ktime_t entry_time;
	ktime_t next_wakeup;
	ktime_t pred_wakeup;
	ktime_t now;
	ktime_t cpu_next_wakeup[MAX_LPM_CPUS];
	bool state_allowed[MAX_CLUSTER_STATES];
	struct list_head list;
	spinlock_t lock;
	bool predicted;
	bool initialized;
};

struct cluster_governor {
	void (*select)(struct lpm_cpu *cpu_gov);
	void (*enable)(void);
	void (*disable)(void);
	void (*reflect)(void);
};

DECLARE_PER_CPU(struct lpm_cpu, lpm_cpu_data);

int qcom_cluster_lpm_governor_init(void);
void qcom_cluster_lpm_governor_deinit(void);
void update_cluster_select(struct lpm_cpu *cpu_gov);
void clear_cpu_predict_history(void);
int create_global_sysfs_nodes(void);
int create_cluster_sysfs_nodes(struct lpm_cluster *cluster_gov);
void register_cluster_governor_ops(struct cluster_governor *ops);
void unregister_cluster_governor_ops(struct cluster_governor *ops);
void remove_global_sysfs_nodes(void);
void remove_cluster_sysfs_nodes(struct lpm_cluster *cluster_gov);

#endif /* __QCOM_LPM_H__ */
