Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62ac983b authored by Zhangfei Gao's avatar Zhangfei Gao Committed by Mike Turquette
Browse files

clk: hisilicon: add hi3620_mmc_clks



Suggest by Arnd: abstract mmc tuning as clock behavior,
also because different soc have different tuning method and registers.
hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.

Signed-off-by: default avatarZhangfei Gao <zhangfei.gao@linaro.org>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Acked-by: default avatarJaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: default avatarMike Turquette <mturquette@linaro.org>
parent b89cd950
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -30,3 +30,17 @@ Example:
		resume-offset = <0x308>;
		reboot-offset = <0x4>;
	};

PCTRL: Peripheral misc control register

Required Properties:
- compatible: "hisilicon,pctrl"
- reg: Address and size of pctrl.

Example:

	/* for Hi3620 */
	pctrl: pctrl@fca09000 {
		compatible = "hisilicon,pctrl";
		reg = <0xfca09000 0x1000>;
	};
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ Required Properties:

- compatible: should be one of the following.
  - "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
  - "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc.

- reg: physical base address of the controller and length of memory mapped
  region.
+274 −0
Original line number Diff line number Diff line
@@ -240,3 +240,277 @@ static void __init hi3620_clk_init(struct device_node *np)
				   base);
}
CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);

struct hisi_mmc_clock {
	unsigned int		id;
	const char		*name;
	const char		*parent_name;
	unsigned long		flags;
	u32			clken_reg;
	u32			clken_bit;
	u32			div_reg;
	u32			div_off;
	u32			div_bits;
	u32			drv_reg;
	u32			drv_off;
	u32			drv_bits;
	u32			sam_reg;
	u32			sam_off;
	u32			sam_bits;
};

struct clk_mmc {
	struct clk_hw	hw;
	u32		id;
	void __iomem	*clken_reg;
	u32		clken_bit;
	void __iomem	*div_reg;
	u32		div_off;
	u32		div_bits;
	void __iomem	*drv_reg;
	u32		drv_off;
	u32		drv_bits;
	void __iomem	*sam_reg;
	u32		sam_off;
	u32		sam_bits;
};

#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)

static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
	{ HI3620_SD_CIUCLK,	"sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4},
	{ HI3620_MMC_CIUCLK1,   "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4},
	{ HI3620_MMC_CIUCLK2,   "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4},
	{ HI3620_MMC_CIUCLK3,   "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4},
};

static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
		       unsigned long parent_rate)
{
	switch (parent_rate) {
	case 26000000:
		return 13000000;
	case 180000000:
		return 25000000;
	case 360000000:
		return 50000000;
	case 720000000:
		return 100000000;
	case 1440000000:
		return 180000000;
	default:
		return parent_rate;
	}
}

static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
			      unsigned long *best_parent_rate,
			      struct clk **best_parent_p)
{
	struct clk_mmc *mclk = to_mmc(hw);
	unsigned long best = 0;

	if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
		rate = 13000000;
		best = 26000000;
	} else if (rate <= 26000000) {
		rate = 25000000;
		best = 180000000;
	} else if (rate <= 52000000) {
		rate = 50000000;
		best = 360000000;
	} else if (rate <= 100000000) {
		rate = 100000000;
		best = 720000000;
	} else {
		/* max is 180M */
		rate = 180000000;
		best = 1440000000;
	}
	*best_parent_rate = best;
	return rate;
}

static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
{
	u32 i;

	if (para >= 0) {
		for (i = 0; i < len; i++) {
			if (para % 2)
				val |= 1 << (off + i);
			else
				val &= ~(1 << (off + i));
			para = para >> 1;
		}
	}
	return val;
}

static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
{
	struct clk_mmc *mclk = to_mmc(hw);
	unsigned long flags;
	u32 sam, drv, div, val;
	static DEFINE_SPINLOCK(mmc_clk_lock);

	switch (rate) {
	case 13000000:
		sam = 3;
		drv = 1;
		div = 1;
		break;
	case 25000000:
		sam = 13;
		drv = 6;
		div = 6;
		break;
	case 50000000:
		sam = 3;
		drv = 6;
		div = 6;
		break;
	case 100000000:
		sam = 6;
		drv = 4;
		div = 6;
		break;
	case 180000000:
		sam = 6;
		drv = 4;
		div = 7;
		break;
	default:
		return -EINVAL;
	}

	spin_lock_irqsave(&mmc_clk_lock, flags);

	val = readl_relaxed(mclk->clken_reg);
	val &= ~(1 << mclk->clken_bit);
	writel_relaxed(val, mclk->clken_reg);

	val = readl_relaxed(mclk->sam_reg);
	val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
	writel_relaxed(val, mclk->sam_reg);

	val = readl_relaxed(mclk->drv_reg);
	val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
	writel_relaxed(val, mclk->drv_reg);

	val = readl_relaxed(mclk->div_reg);
	val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
	writel_relaxed(val, mclk->div_reg);

	val = readl_relaxed(mclk->clken_reg);
	val |= 1 << mclk->clken_bit;
	writel_relaxed(val, mclk->clken_reg);

	spin_unlock_irqrestore(&mmc_clk_lock, flags);

	return 0;
}

static int mmc_clk_prepare(struct clk_hw *hw)
{
	struct clk_mmc *mclk = to_mmc(hw);
	unsigned long rate;

	if (mclk->id == HI3620_MMC_CIUCLK1)
		rate = 13000000;
	else
		rate = 25000000;

	return mmc_clk_set_timing(hw, rate);
}

static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
			     unsigned long parent_rate)
{
	return mmc_clk_set_timing(hw, rate);
}

static struct clk_ops clk_mmc_ops = {
	.prepare = mmc_clk_prepare,
	.determine_rate = mmc_clk_determine_rate,
	.set_rate = mmc_clk_set_rate,
	.recalc_rate = mmc_clk_recalc_rate,
};

static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
			void __iomem *base, struct device_node *np)
{
	struct clk_mmc *mclk;
	struct clk *clk;
	struct clk_init_data init;

	mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
	if (!mclk) {
		pr_err("%s: fail to allocate mmc clk\n", __func__);
		return ERR_PTR(-ENOMEM);
	}

	init.name = mmc_clk->name;
	init.ops = &clk_mmc_ops;
	init.flags = mmc_clk->flags | CLK_IS_BASIC;
	init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
	init.num_parents = (mmc_clk->parent_name ? 1 : 0);
	mclk->hw.init = &init;

	mclk->id = mmc_clk->id;
	mclk->clken_reg = base + mmc_clk->clken_reg;
	mclk->clken_bit = mmc_clk->clken_bit;
	mclk->div_reg = base + mmc_clk->div_reg;
	mclk->div_off = mmc_clk->div_off;
	mclk->div_bits = mmc_clk->div_bits;
	mclk->drv_reg = base + mmc_clk->drv_reg;
	mclk->drv_off = mmc_clk->drv_off;
	mclk->drv_bits = mmc_clk->drv_bits;
	mclk->sam_reg = base + mmc_clk->sam_reg;
	mclk->sam_off = mmc_clk->sam_off;
	mclk->sam_bits = mmc_clk->sam_bits;

	clk = clk_register(NULL, &mclk->hw);
	if (WARN_ON(IS_ERR(clk)))
		kfree(mclk);
	return clk;
}

static void __init hi3620_mmc_clk_init(struct device_node *node)
{
	void __iomem *base;
	int i, num = ARRAY_SIZE(hi3620_mmc_clks);
	struct clk_onecell_data *clk_data;

	if (!node) {
		pr_err("failed to find pctrl node in DTS\n");
		return;
	}

	base = of_iomap(node, 0);
	if (!base) {
		pr_err("failed to map pctrl\n");
		return;
	}

	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
	if (WARN_ON(!clk_data))
		return;

	clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
	if (!clk_data->clks) {
		pr_err("%s: fail to allocate mmc clk\n", __func__);
		return;
	}

	for (i = 0; i < num; i++) {
		struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
		clk_data->clks[mmc_clk->id] =
			hisi_register_clk_mmc(mmc_clk, base, node);
	}

	clk_data->clk_num = num;
	of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
}

CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
+5 −0
Original line number Diff line number Diff line
@@ -147,6 +147,11 @@
#define HI3620_MMC_CLK3		217
#define HI3620_MCU_CLK		218

#define HI3620_SD_CIUCLK	0
#define HI3620_MMC_CIUCLK1	1
#define HI3620_MMC_CIUCLK2	2
#define HI3620_MMC_CIUCLK3	3

#define HI3620_NR_CLKS		219

#endif	/* __DTS_HI3620_CLOCK_H */