/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2023 Linaro Ltd.
 * Sam Protsenko <semen.protsenko@linaro.org>
 *
 * Common Clock Framework support for all Samsung platforms.
 */

#ifndef __EXYNOS_CLK_H
#define __EXYNOS_CLK_H

#include <clk.h>
#include <errno.h>
#include <linux/clk-provider.h>
#include "clk-pll.h"

int samsung_clk_request(struct clk *clk);

#define _SAMSUNG_CLK_OPS(_name, _cmu)					\
static int _name##_of_xlate(struct clk *clk,				\
			    struct ofnode_phandle_args *args)		\
{									\
	if (args->args_count > 1) {					\
		debug("Invalid args_count: %d\n", args->args_count);	\
		return -EINVAL;						\
	}								\
									\
	if (args->args_count)						\
		clk->id = SAMSUNG_TO_CLK_ID(_cmu, args->args[0]);	\
	else								\
		clk->id = 0;						\
									\
	return 0;							\
}									\
									\
static const struct clk_ops _name##_clk_ops = {				\
	.set_rate = ccf_clk_set_rate,					\
	.get_rate = ccf_clk_get_rate,					\
	.set_parent = ccf_clk_set_parent,				\
	.enable = ccf_clk_enable,					\
	.disable = ccf_clk_disable,					\
	.of_xlate = _name##_of_xlate,					\
	.request = samsung_clk_request,					\
}

/**
 * SAMSUNG_CLK_OPS - Define clock operations structure for specified CMU.
 * @name: name of generated structure
 * @cmu: CMU index
 *
 * Like ccf_clk_ops, but with custom .of_xlate callback.
 */
#define SAMSUNG_CLK_OPS(name, cmu) _SAMSUNG_CLK_OPS(name, cmu)

/**
 * SAMSUNG_TO_CLK_ID - Calculate a global clock index.
 * @_cmu: CMU index
 * @_id: local clock index (unique across @_cmu)
 *
 * Return: A global clock index unique across all CMUs.
 * Keeps a range of 256 available clocks for every CMU.
 */
#define SAMSUNG_TO_CLK_ID(_cmu, _id)	(((_cmu) << 8) | ((_id) & 0xff))

/**
 * struct samsung_fixed_rate_clock - information about fixed-rate clock
 * @id: platform specific id of the clock
 * @name: name of this fixed-rate clock
 * @fixed_rate: fixed clock rate of this clock
 */
struct samsung_fixed_rate_clock {
	unsigned int		id;
	const char		*name;
	unsigned long		fixed_rate;
};

#define FRATE(_id, cname, frate)			\
	{						\
		.id		= _id,			\
		.name		= cname,		\
		.fixed_rate	= frate,		\
	}

/**
 * struct samsung_fixed_factor_clock - information about fixed-factor clock
 * @id: platform specific id of the clock
 * @name: name of this fixed-factor clock
 * @parent_name: parent clock name
 * @mult: fixed multiplication factor
 * @div: fixed division factor
 * @flags: optional fixed-factor clock flags
 */
struct samsung_fixed_factor_clock {
	unsigned int		id;
	const char		*name;
	const char		*parent_name;
	unsigned long		mult;
	unsigned long		div;
	unsigned long		flags;
};

#define FFACTOR(_id, cname, pname, m, d, f)		\
	{						\
		.id		= _id,			\
		.name		= cname,		\
		.parent_name	= pname,		\
		.mult		= m,			\
		.div		= d,			\
		.flags		= f,			\
	}

/**
 * struct samsung_mux_clock - information about mux clock
 * @id: platform specific id of the clock
 * @name: name of this mux clock
 * @parent_names: array of pointer to parent clock names
 * @num_parents: number of parents listed in @parent_names
 * @flags: optional flags for basic clock
 * @offset: offset of the register for configuring the mux
 * @shift: starting bit location of the mux control bit-field in @reg
 * @width: width of the mux control bit-field in @reg
 * @mux_flags: flags for mux-type clock
 */
struct samsung_mux_clock {
	unsigned int		id;
	const char		*name;
	const char		* const *parent_names;
	u8			num_parents;
	unsigned long		flags;
	unsigned long		offset;
	u8			shift;
	u8			width;
	u8			mux_flags;
};

#define PNAME(x) static const char * const x[]

#define __MUX(_id, cname, pnames, o, s, w, f, mf)			\
	{								\
		.id		= _id,					\
		.name		= cname,				\
		.parent_names	= pnames,				\
		.num_parents	= ARRAY_SIZE(pnames),			\
		.flags		= (f) | CLK_SET_RATE_NO_REPARENT,	\
		.offset		= o,					\
		.shift		= s,					\
		.width		= w,					\
		.mux_flags	= mf,					\
	}

#define MUX(_id, cname, pnames, o, s, w)				\
	__MUX(_id, cname, pnames, o, s, w, 0, 0)

#define MUX_F(_id, cname, pnames, o, s, w, f, mf)			\
	__MUX(_id, cname, pnames, o, s, w, f, mf)

/**
 * struct samsung_div_clock - information about div clock
 * @id: platform specific id of the clock
 * @name: name of this div clock
 * @parent_name: name of the parent clock
 * @flags: optional flags for basic clock
 * @offset: offset of the register for configuring the div
 * @shift: starting bit location of the div control bit-field in @reg
 * @width: width of the bitfield
 * @div_flags: flags for div-type clock
 */
struct samsung_div_clock {
	unsigned int		id;
	const char		*name;
	const char		*parent_name;
	unsigned long		flags;
	unsigned long		offset;
	u8			shift;
	u8			width;
	u8			div_flags;
};

#define __DIV(_id, cname, pname, o, s, w, f, df)	\
	{						\
		.id		= _id,			\
		.name		= cname,		\
		.parent_name	= pname,		\
		.flags		= f,			\
		.offset		= o,			\
		.shift		= s,			\
		.width		= w,			\
		.div_flags	= df,			\
	}

#define DIV(_id, cname, pname, o, s, w)			\
	__DIV(_id, cname, pname, o, s, w, 0, 0)

#define DIV_F(_id, cname, pname, o, s, w, f, df)	\
	__DIV(_id, cname, pname, o, s, w, f, df)

/**
 * struct samsung_gate_clock - information about gate clock
 * @id: platform specific id of the clock
 * @name: name of this gate clock
 * @parent_name: name of the parent clock
 * @flags: optional flags for basic clock
 * @offset: offset of the register for configuring the gate
 * @bit_idx: bit index of the gate control bit-field in @reg
 * @gate_flags: flags for gate-type clock
 */
struct samsung_gate_clock {
	unsigned int		id;
	const char		*name;
	const char		*parent_name;
	unsigned long		flags;
	unsigned long		offset;
	u8			bit_idx;
	u8			gate_flags;
};

#define __GATE(_id, cname, pname, o, b, f, gf)			\
	{							\
		.id		= _id,				\
		.name		= cname,			\
		.parent_name	= pname,			\
		.flags		= f,				\
		.offset		= o,				\
		.bit_idx	= b,				\
		.gate_flags	= gf,				\
	}

#define GATE(_id, cname, pname, o, b, f, gf)			\
	__GATE(_id, cname, pname, o, b, f, gf)

/**
 * struct samsung_pll_clock - information about pll clock
 * @id: platform specific id of the clock
 * @name: name of this pll clock
 * @parent_name: name of the parent clock
 * @flags: optional flags for basic clock
 * @con_offset: offset of the register for configuring the PLL
 * @type: type of PLL to be registered
 */
struct samsung_pll_clock {
	unsigned int		id;
	const char		*name;
	const char		*parent_name;
	unsigned long		flags;
	int			con_offset;
	enum samsung_pll_type	type;
};

#define PLL(_typ, _id, _name, _pname, _con)		\
	{						\
		.id		= _id,			\
		.name		= _name,		\
		.parent_name	= _pname,		\
		.flags		= CLK_GET_RATE_NOCACHE,	\
		.con_offset	= _con,			\
		.type		= _typ,			\
	}

enum samsung_clock_type {
	S_CLK_FRATE,
	S_CLK_FFACTOR,
	S_CLK_MUX,
	S_CLK_DIV,
	S_CLK_GATE,
	S_CLK_PLL,
};

/**
 * struct samsung_clock_group - contains a list of clocks of one type
 * @type: type of clocks this structure contains
 * @clk_list: list of clocks
 * @nr_clk: count of clocks in @clk_list
 */
struct samsung_clk_group {
	enum samsung_clock_type type;
	const void *clk_list;
	unsigned int nr_clk;
};

int samsung_cmu_register_one(struct udevice *dev, unsigned int cmu_id,
			     const struct samsung_clk_group *clk_groups,
			     unsigned int nr_groups);

/**
 * samsung_register_cmu - Register CMU clocks ensuring parent CMU is present
 * @dev: CMU device
 * @cmu_id: CMU index number
 * @clk_groups: list of CMU clock groups
 * @parent_drv: name of parent CMU driver
 *
 * Register provided CMU clocks, but make sure CMU_TOP driver is instantiated
 * first.
 *
 * Return: 0 on success or negative value on error.
 */
#define samsung_register_cmu(dev, cmu_id, clk_groups, parent_drv)	\
({									\
	struct udevice *__parent;					\
	int __ret;							\
									\
	__ret = uclass_get_device_by_driver(UCLASS_CLK,			\
		DM_DRIVER_GET(parent_drv), &__parent);			\
	if (__ret || !__parent)						\
		__ret = -ENOENT;					\
	else								\
		__ret = samsung_cmu_register_one(dev, cmu_id,		\
			clk_groups, ARRAY_SIZE(clk_groups));		\
	__ret;								\
})

#endif /* __EXYNOS_CLK_H */
