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

#ifndef _QCOM_BWMON_H
#define _QCOM_BWMON_H

#include <linux/kernel.h>
#include <linux/notifier.h>
#include <soc/qcom/dcvs.h>

#define NUM_MBPS_ZONES		10
#define UP_WAKE			1
#define DOWN_WAKE		2
#define MBYTE			(1ULL << 20)
#define MBPS_TO_KHZ(mbps, w)	(mult_frac(mbps, MBYTE, w * 1000ULL))
#define KHZ_TO_MBPS(khz, w)	(mult_frac(w * 1000ULL, khz, MBYTE))
#define to_bwmon(ptr)		container_of(ptr, struct bwmon, hw)

enum mon_reg_type {
	MON1,
	MON2,
	MON3,
};

struct bwmon_spec {
	bool wrap_on_thres;
	bool overflow;
	bool throt_adj;
	bool hw_sampling;
	bool has_global_base;
	enum mon_reg_type reg_type;
};

struct bwmon_second_map {
	u32 src_freq;
	u32 dst_freq;
};

/**
 * struct bw_hwmon - dev BW HW monitor info
 * @start_hwmon:		Start the HW monitoring of the dev BW
 * @stop_hwmon:			Stop the HW monitoring of dev BW
 * @set_thres:			Set the count threshold to generate an IRQ
 * @get_bytes_and_clear:	Get the bytes transferred since the last call
 *				and reset the counter to start over.
 * @set_throttle_adj:		Set throttle adjust field to the given value
 * @get_throttle_adj:		Get the value written to throttle adjust field
 * @dev:			Pointer to device that this HW monitor can
 *				monitor.
 */
struct bw_hwmon {
	int			(*start_hwmon)(struct bw_hwmon *hw,
					unsigned long mbps);
	void			(*stop_hwmon)(struct bw_hwmon *hw);
	unsigned long		(*set_thres)(struct bw_hwmon *hw,
					unsigned long bytes);
	unsigned long		(*set_hw_events)(struct bw_hwmon *hw,
					unsigned int sample_ms);
	unsigned long		(*get_bytes_and_clear)(struct bw_hwmon *hw);
	int			(*set_throttle_adj)(struct bw_hwmon *hw,
					uint adj);
	u32			(*get_throttle_adj)(struct bw_hwmon *hw);
	struct device		*dev;
	enum dcvs_hw_type	dcvs_hw;
	enum dcvs_path_type	dcvs_path;
	u32			dcvs_width;
	enum dcvs_hw_type	second_dcvs_hw;
	u32			second_dcvs_width;
	bool			second_vote_supported;
	u32			second_vote_limit;
	struct bwmon_second_map	*second_map;
	struct hwmon_node	*node;
	ktime_t			last_update_ts;
	struct work_struct	work;
	struct notifier_block	pm_nb;
	bool			is_active;
	unsigned long		up_wake_mbps;
	unsigned long		down_wake_mbps;
	unsigned int		down_cnt;
};

struct bwmon {
	void __iomem		*base;
	void __iomem		*global_base;
	unsigned int		mport;
	int			irq;
	const struct bwmon_spec	*spec;
	struct device		*dev;
	struct bw_hwmon		hw;
	u32			hw_timer_hz;
	u32			throttle_adj;
	u32			sample_size_ms;
	u32			intr_status;
	u8			count_shift;
	u32			thres_lim;
	u32			byte_mask;
	u32			byte_match;
};

struct hwmon_node {
	u32			hw_min_freq;
	u32			hw_max_freq;
	u32			min_freq;
	u32			max_freq;
	struct dcvs_freq	cur_freqs[2];
	u32			window_ms;
	unsigned int		guard_band_mbps;
	unsigned int		decay_rate;
	unsigned int		io_percent;
	unsigned int		bw_step;
	unsigned int		sample_ms;
	unsigned int		up_scale;
	unsigned int		up_thres;
	unsigned int		down_thres;
	unsigned int		down_count;
	unsigned int		hist_memory;
	unsigned int		hyst_trigger_count;
	unsigned int		hyst_length;
	unsigned int		idle_length;
	unsigned int		idle_mbps;
	unsigned int		ab_scale;
	unsigned int		mbps_zones[NUM_MBPS_ZONES];
	unsigned long		prev_ab;
	unsigned long		bytes;
	unsigned long		max_mbps;
	unsigned long		hist_max_mbps;
	unsigned long		hist_mem;
	unsigned long		hyst_peak;
	unsigned long		hyst_mbps;
	unsigned long		hyst_trig_win;
	unsigned long		hyst_en;
	unsigned long		idle_en;
	unsigned long		prev_req;
	unsigned int		wake;
	unsigned int		down_cnt;
	ktime_t			prev_ts;
	ktime_t			hist_max_ts;
	bool			sampled;
	bool			mon_started;
	struct list_head	list;
	struct bw_hwmon		*hw;
	struct kobject		kobj;
	struct mutex		mon_lock;
	struct mutex		update_lock;
};

/* BWMON register offsets */
#define GLB_INT_STATUS(m)	((m)->global_base + 0x100)
#define GLB_INT_CLR(m)		((m)->global_base + 0x108)
#define	GLB_INT_EN(m)		((m)->global_base + 0x10C)
#define MON_INT_STATUS(m)	((m)->base + 0x100)
#define MON_INT_STATUS_MASK	0x03
#define MON2_INT_STATUS_MASK	0xF0
#define MON2_INT_STATUS_SHIFT	4
#define MON_INT_CLR(m)		((m)->base + 0x108)
#define	MON_INT_EN(m)		((m)->base + 0x10C)
#define MON_INT_ENABLE		0x1
#define	MON_EN(m)		((m)->base + 0x280)
#define MON_CLEAR(m)		((m)->base + 0x284)
#define MON_CNT(m)		((m)->base + 0x288)
#define MON_THRES(m)		((m)->base + 0x290)
#define MON_MASK(m)		((m)->base + 0x298)
#define MON_MATCH(m)		((m)->base + 0x29C)

#define MON2_EN(m)		((m)->base + 0x2A0)
#define MON2_CLEAR(m)		((m)->base + 0x2A4)
#define MON2_SW(m)		((m)->base + 0x2A8)
#define MON2_THRES_HI(m)	((m)->base + 0x2AC)
#define MON2_THRES_MED(m)	((m)->base + 0x2B0)
#define MON2_THRES_LO(m)	((m)->base + 0x2B4)
#define MON2_ZONE_ACTIONS(m)	((m)->base + 0x2B8)
#define MON2_ZONE_CNT_THRES(m)	((m)->base + 0x2BC)
#define MON2_BYTE_CNT(m)	((m)->base + 0x2D0)
#define MON2_WIN_TIMER(m)	((m)->base + 0x2D4)
#define MON2_ZONE_CNT(m)	((m)->base + 0x2D8)
#define MON2_ZONE_MAX(m, zone)	((m)->base + 0x2E0 + 0x4 * zone)

#define MON3_INT_STATUS(m)	((m)->base + 0x00)
#define MON3_INT_CLR(m)		((m)->base + 0x08)
#define MON3_INT_EN(m)		((m)->base + 0x0C)
#define MON3_INT_STATUS_MASK	0x0F
#define MON3_EN(m)		((m)->base + 0x10)
#define MON3_CLEAR(m)		((m)->base + 0x14)
#define MON3_MASK(m)		((m)->base + 0x18)
#define MON3_MATCH(m)		((m)->base + 0x1C)
#define MON3_SW(m)		((m)->base + 0x20)
#define MON3_THRES_HI(m)	((m)->base + 0x24)
#define MON3_THRES_MED(m)	((m)->base + 0x28)
#define MON3_THRES_LO(m)	((m)->base + 0x2C)
#define MON3_ZONE_ACTIONS(m)	((m)->base + 0x30)
#define MON3_ZONE_CNT_THRES(m)	((m)->base + 0x34)
#define MON3_BYTE_CNT(m)	((m)->base + 0x38)
#define MON3_WIN_TIMER(m)	((m)->base + 0x3C)
#define MON3_ZONE_CNT(m)	((m)->base + 0x40)
#define MON3_ZONE_MAX(m, zone)	((m)->base + 0x44 + 0x4 * zone)

#endif /* _QCOM_BWMON_H */
