/*
 * Copyright (c) 2013-2016, 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.
 */

#ifndef __MSM_CLOCK_GENERIC_H
#define __MSM_CLOCK_GENERIC_H

#include <linux/clk/msm-clk-provider.h>
#include <linux/of.h>

/**
 * struct fixed_clk - fixed rate clock
 * @c: clk
 */
struct fixed_clk {
	struct clk c;
};

/* ==================== Mux clock ==================== */

struct mux_clk;

struct clk_mux_ops {
	int (*set_mux_sel)(struct mux_clk *clk, int sel);
	int (*get_mux_sel)(struct mux_clk *clk);

	/* Optional */
	bool (*is_enabled)(struct mux_clk *clk);
	int (*enable)(struct mux_clk *clk);
	void (*disable)(struct mux_clk *clk);
	void __iomem *(*list_registers)(struct mux_clk *clk, int n,
				struct clk_register_data **regs, u32 *size);
};

#define MUX_SRC_LIST(...) \
	.parents = (struct clk_src[]){__VA_ARGS__}, \
	.num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))

#define MUX_REC_SRC_LIST(...) \
	.rec_parents = (struct clk * []){__VA_ARGS__}, \
	.num_rec_parents = ARRAY_SIZE(((struct clk * []){__VA_ARGS__}))

struct mux_clk {
	/* Parents in decreasing order of preference for obtaining rates. */
	struct clk_src	*parents;
	int		num_parents;
	/* Recursively search for the requested parent in rec_parents. */
	struct clk	**rec_parents;
	int		num_rec_parents;
	struct clk	*safe_parent;
	int		safe_sel;
	unsigned long	safe_freq;
	/*
	 * Before attempting a clk_round_rate on available sources, attempt a
	 * clk_get_rate on all those sources. If one of them is already at the
	 * necessary rate, that source will be used.
	 */
	bool		try_get_rate;
	struct clk_mux_ops *ops;
	/*
	 * Set if you need the mux to try a new parent before falling back to
	 * the current parent. If the safe_parent field above is set, then the
	 * safe_sel intermediate source will only be used if we fall back to
	 * to the current parent during mux_set_rate.
	 */
	bool		try_new_parent;

	/* Fields not used by helper function. */
	void *const __iomem *base;
	u32		offset;
	u32		en_offset;
	u32		mask;
	u32		shift;
	u32		en_mask;
	/*
	 * Set post divider for debug mux in order to divide the clock
	 * by post_div + 1.
	 */
	u32		post_div;
	int		low_power_sel;
	void		*priv;

	struct clk	c;
};

static inline struct mux_clk *to_mux_clk(struct clk *c)
{
	return container_of(c, struct mux_clk, c);
}

extern struct clk_ops clk_ops_gen_mux;

/* ==================== Divider clock ==================== */

struct div_clk;

struct clk_div_ops {
	int (*set_div)(struct div_clk *clk, int div);
	int (*get_div)(struct div_clk *clk);
	bool (*is_enabled)(struct div_clk *clk);
	int (*enable)(struct div_clk *clk);
	void (*disable)(struct div_clk *clk);
	void __iomem *(*list_registers)(struct div_clk *clk, int n,
				struct clk_register_data **regs, u32 *size);
};

struct div_data {
	unsigned int div;
	unsigned int min_div;
	unsigned int max_div;
	unsigned long rate_margin;
	/*
	 * Indicate whether this divider clock supports half-interger divider.
	 * If it is, all the min_div and max_div have been doubled. It means
	 * they are 2*N.
	 */
	bool is_half_divider;
	/*
	 * Skip odd dividers since the hardware may not support them.
	 */
	bool skip_odd_div;
	bool skip_even_div;
	bool allow_div_one;
	unsigned int cached_div;
};

struct div_clk {
	struct div_data data;

	/*
	 * Some implementations may require the divider to be set to a "safe"
	 * value that allows reprogramming of upstream clocks without violating
	 * voltage constraints.
	 */
	unsigned long safe_freq;

	/* Optional */
	struct clk_div_ops *ops;

	/* Fields not used by helper function. */
	void *const __iomem *base;
	u32		offset;
	u32		mask;
	u32		shift;
	u32		en_mask;
	void		*priv;
	struct clk	c;
};

static inline struct div_clk *to_div_clk(struct clk *c)
{
	return container_of(c, struct div_clk, c);
}

extern struct clk_ops clk_ops_div;
extern struct clk_ops clk_ops_slave_div;

struct ext_clk {
	struct clk c;
	struct device *dev;
	char *clk_id;
};

long parent_round_rate(struct clk *c, unsigned long rate);
unsigned long parent_get_rate(struct clk *c);
int parent_set_rate(struct clk *c, unsigned long rate);

static inline struct ext_clk *to_ext_clk(struct clk *c)
{
	return container_of(c, struct ext_clk, c);
}

extern struct clk_ops clk_ops_ext;

#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \
static struct div_clk clk_name = {	\
	.data = {				\
		.max_div = _div,		\
		.min_div = _div,		\
		.div = _div,			\
	},					\
	.c = {					\
		.parent = _parent,		\
		.dbg_name = #clk_name,		\
		.ops = &clk_ops_div,		\
		CLK_INIT(clk_name.c),		\
	}					\
}

#define DEFINE_FIXED_SLAVE_DIV_CLK(clk_name, _div, _parent) \
static struct div_clk clk_name = {	\
	.data = {				\
		.max_div = _div,		\
		.min_div = _div,		\
		.div = _div,			\
	},					\
	.c = {					\
		.parent = _parent,		\
		.dbg_name = #clk_name,		\
		.ops = &clk_ops_slave_div,		\
		CLK_INIT(clk_name.c),		\
	}					\
}

#define DEFINE_EXT_CLK(clk_name, _parent) \
static struct ext_clk clk_name = {		\
	.c = {					\
		.parent = _parent,		\
		.dbg_name = #clk_name,		\
		.ops = &clk_ops_ext,		\
		CLK_INIT(clk_name.c),		\
	}					\
}

/* ==================== Mux Div clock ==================== */

struct mux_div_clk;

/*
 * struct mux_div_ops
 * the enable and disable ops are optional.
 */

struct mux_div_ops {
	int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div);
	void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div);
	int (*enable)(struct mux_div_clk *);
	void (*disable)(struct mux_div_clk *);
	bool (*is_enabled)(struct mux_div_clk *);
	void __iomem *(*list_registers)(struct mux_div_clk *md, int n,
				struct clk_register_data **regs, u32 *size);
};

/*
 * struct mux_div_clk - combined mux/divider clock
 * @priv
		parameters needed by ops
 * @safe_freq
		when switching rates from A to B, the mux div clock will
		instead switch from A -> safe_freq -> B. This allows the
		mux_div clock to change rates while enabled, even if this
		behavior is not supported by the parent clocks.

		If changing the rate of parent A also causes the rate of
		parent B to change, then safe_freq must be defined.

		safe_freq is expected to have a source clock which is always
		on and runs at only one rate.
 * @parents
		list of parents and mux indicies
 * @ops
		function pointers for hw specific operations
 * @src_sel
		the mux index which will be used if the clock is enabled.
 * @try_get_rate
		Set if you need the mux to directly jump to a source
		that is at the desired rate currently.
 * @force_enable_md
		Set if the mux-div needs to be force enabled/disabled during
		clk_enable/disable.
 */

struct mux_div_clk {
	/* Required parameters */
	struct mux_div_ops		*ops;
	struct div_data			data;
	struct clk_src			*parents;
	u32				num_parents;

	struct clk			c;

	/* Internal */
	u32				src_sel;

	/* Optional parameters */
	void				*priv;
	void __iomem			*base;
	u32				div_mask;
	u32				div_offset;
	u32				div_shift;
	u32				src_mask;
	u32				src_offset;
	u32				src_shift;
	u32				en_mask;
	u32				en_offset;

	u32				safe_div;
	struct clk			*safe_parent;
	unsigned long			safe_freq;
	bool				try_get_rate;
	bool				force_enable_md;
};

static inline struct mux_div_clk *to_mux_div_clk(struct clk *clk)
{
	return container_of(clk, struct mux_div_clk, c);
}

extern struct clk_ops clk_ops_mux_div_clk;

#endif
