/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
 */

#ifndef _PWM_MESON_H
#define _PWM_MESON_H

#include <linux/bitops.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/time.h>
#include <linux/of_address.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
/* for pwm channel index*/
#include <dt-bindings/pwm/meson.h>

#define MESON_NUM_PWMS		2
#define MESON_DOUBLE_NUM_PWMS	4
#define DEFAULT_CLK		24000000
#define OSIN_CLK		24000000
#define FCLK_DIV4_CLK	500000000
#define FCLK_DIV3_CLK	667000000

/*a group pwm registers offset address
 * for example:
 * PWM A B
 * PWM C D
 * PWM E F
 * PWM AO A
 * PWM AO B
 */
#define REG_PWM_A				0x0
#define REG_PWM_B				0x4
#define REG_MISC_AB				0x8
#define REG_DS_AB				0xc
#define REG_TIME_AB				0x10
#define REG_PWM_A2				0x14
#define REG_PWM_B2				0x18
#define REG_BLINK_AB				0x1c

#define PWM_LOW_MASK				GENMASK(15, 0)
#define PWM_HIGH_MASK				GENMASK(31, 16)

/* pwm output enable */
#define MISC_A_EN				BIT(0)
#define MISC_B_EN				BIT(1)
#define MISC_A2_EN				BIT(25)
#define MISC_B2_EN				BIT(24)
/* pwm polarity enable */
#define MISC_A_INVERT				BIT(26)
#define MISC_B_INVERT				BIT(27)
/* when you want 0% or 100% waveform
 * constant bit should be set.
 */
#define MISC_A_CONSTANT				BIT(28)
#define MISC_B_CONSTANT				BIT(29)
/*
 * pwm a and b clock enable/disable
 */
#define MISC_A_CLK_EN				BIT(15)
#define MISC_B_CLK_EN				BIT(23)
/*
 * blink control bit
 */
#define BLINK_A					BIT(8)
#define BLINK_B					BIT(9)

#define PWM_HIGH_SHIFT				16
#define MISC_CLK_DIV_MASK			0x7f
#define MISC_B_CLK_DIV_SHIFT			16
#define MISC_A_CLK_DIV_SHIFT			8
#define MISC_B_CLK_SEL_SHIFT			6
#define MISC_A_CLK_SEL_SHIFT			4
#define MISC_CLK_SEL_WIDTH			2
#define PWM_CHANNELS_PER_GROUP			2
#define PWM_DISABLE				0
#define MISC_CLK_SEL_MASK			0x3

/*
 * external clk reg field
 */
#define EXT_CLK_A_EN				BIT(8)
#define EXT_CLK_B_EN				BIT(24)
#define EXT_CLK_A_DIV_SHIFT			0
#define EXT_CLK_B_DIV_SHIFT			16
#define EXT_CLK_A_SEL_SHIFT			9
#define EXT_CLK_B_SEL_SHIFT			25
#define EXT_CLK_DIV_MASK			0xff
#define EXT_CLK_SEL_MASK			0x3

static const unsigned int mux_reg_shifts[] = {
	MISC_A_CLK_SEL_SHIFT,
	MISC_B_CLK_SEL_SHIFT,
	MISC_A_CLK_SEL_SHIFT,
	MISC_B_CLK_SEL_SHIFT
};

/*pwm register att*/
struct meson_pwm_variant {
	unsigned int times;
	unsigned int constant;
	unsigned int blink_enable;
	unsigned int blink_times;
};

/*for soc data:
 *double channel enable
 * double_channel = false ,could use PWM A
 * double_channel = true , could use PWM A and PWM A2
 * extern_clk = false , clk div, gate, mux in pwm controller
 * extern_clk = true , clk div, gate, mux in external clk controller
 */
struct meson_pwm_data {
	char **parent_names;
	unsigned int num_parents;
	unsigned int double_channel;
	unsigned int extern_clk;
};

struct meson_pwm_channel {
	unsigned int hi;
	unsigned int lo;
	unsigned int clk_rate;
	u8 clk_div;
	u8 pre_div;

	struct clk *clk_parent;
	struct clk_mux mux;
	struct clk *clk;
};

struct meson_pwm {
	struct pwm_chip chip;
	struct meson_pwm_data *data;
	struct meson_pwm_channel channels[MESON_DOUBLE_NUM_PWMS];
	struct meson_pwm_variant variant;
	void __iomem *base;
	void __iomem *ext_clk_base;
	/*
	 * Protects register (write) access to the REG_MISC_AB register
	 * that is shared between the two PWMs.
	 */
	spinlock_t lock;
	struct regmap *regmap_base;
};

/*the functions only use for meson pwm driver*/
int meson_pwm_sysfs_init(struct device *dev);
void meson_pwm_sysfs_exit(struct device *dev);

/*the functions use for special function in meson pwm driver*/
int pwm_register_debug(struct meson_pwm *meson);
struct meson_pwm *to_meson_pwm(struct pwm_chip *chip);
int pwm_constant_enable(struct meson_pwm *meson, int index);
int pwm_constant_disable(struct meson_pwm *meson, int index);
int pwm_blink_enable(struct meson_pwm *meson, int index);
int pwm_blink_disable(struct meson_pwm *meson, int index);
int pwm_set_blink_times(struct meson_pwm *meson, int index, int value);
int pwm_set_times(struct meson_pwm *meson, int index, int value);
#endif   /* _PWM_MESON_H_ */
