Commit 0049eb1a authored by Stephen Boyd's avatar Stephen Boyd
Browse files

Merge tag 'clk-imx-6.1' of...

Merge tag 'clk-imx-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelvesa/linux into clk-imx

Pull i.MX clk driver updates from Abel Vesa:

 - Change order between 'sim_enet_root_clk' and 'enet_qos_root_clk'
   clocks for i.MX8MP
 - Drop unnecessary newline in i.MX8MM dt-bindings
 - Add more MU1 and SAI clocks dt-bindings Ids
 - Introduce slice busy bit check for i.MX93 composite clock
 - Introduce white list bit check for i.MX93 composite clock
 - Add new i.MX93 clock gate
 - Add MU1 and MU2 clocks to i.MX93 clock provider
 - Add SAI IPG clocks to i.MX93 clock provider

* tag 'clk-imx-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelvesa/linux:
  clk: imx93: add SAI IPG clk
  clk: imx93: add MU1/2 clock
  clk: imx93: switch to use new clk gate API
  clk: imx: add i.MX93 clk gate
  clk: imx: clk-composite-93: check white_list
  clk: imx: clk-composite-93: check slice busy
  dt-bindings: clock: imx93-clock: add more MU/SAI clocks
  dt-bindings: clock: imx8mm: don't use multiple blank lines
  clk: imx8mp: tune the order of enet_qos_root_clk
parents 568035b0 67e16ac1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ mxc-clk-objs += clk-fixup-div.o
mxc-clk-objs += clk-fixup-mux.o
mxc-clk-objs += clk-frac-pll.o
mxc-clk-objs += clk-gate2.o
mxc-clk-objs += clk-gate-93.o
mxc-clk-objs += clk-gate-exclusive.o
mxc-clk-objs += clk-pfd.o
mxc-clk-objs += clk-pfdv2.o
+166 −5
Original line number Diff line number Diff line
@@ -9,22 +9,180 @@
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/slab.h>

#include "clk.h"

#define TIMEOUT_US	500U

#define CCM_DIV_SHIFT	0
#define CCM_DIV_WIDTH	8
#define CCM_MUX_SHIFT	8
#define CCM_MUX_MASK	3
#define CCM_OFF_SHIFT	24
#define CCM_BUSY_SHIFT	28

#define STAT_OFFSET	0x4
#define AUTHEN_OFFSET	0x30
#define TZ_NS_SHIFT	9
#define TZ_NS_MASK	BIT(9)

#define WHITE_LIST_SHIFT	16

static int imx93_clk_composite_wait_ready(struct clk_hw *hw, void __iomem *reg)
{
	int ret;
	u32 val;

	ret = readl_poll_timeout_atomic(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)),
					0, TIMEOUT_US);
	if (ret)
		pr_err("Slice[%s] busy timeout\n", clk_hw_get_name(hw));

	return ret;
}

static void imx93_clk_composite_gate_endisable(struct clk_hw *hw, int enable)
{
	struct clk_gate *gate = to_clk_gate(hw);
	unsigned long flags;
	u32 reg;

	if (gate->lock)
		spin_lock_irqsave(gate->lock, flags);

	reg = readl(gate->reg);

	if (enable)
		reg &= ~BIT(gate->bit_idx);
	else
		reg |= BIT(gate->bit_idx);

	writel(reg, gate->reg);

	imx93_clk_composite_wait_ready(hw, gate->reg);

	if (gate->lock)
		spin_unlock_irqrestore(gate->lock, flags);
}

static int imx93_clk_composite_gate_enable(struct clk_hw *hw)
{
	imx93_clk_composite_gate_endisable(hw, 1);

	return 0;
}

static void imx93_clk_composite_gate_disable(struct clk_hw *hw)
{
	imx93_clk_composite_gate_endisable(hw, 0);
}

static const struct clk_ops imx93_clk_composite_gate_ops = {
	.enable = imx93_clk_composite_gate_enable,
	.disable = imx93_clk_composite_gate_disable,
	.is_enabled = clk_gate_is_enabled,
};

static unsigned long
imx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
	return clk_divider_ops.recalc_rate(hw, parent_rate);
}

static long
imx93_clk_composite_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
{
	return clk_divider_ops.round_rate(hw, rate, prate);
}

static int
imx93_clk_composite_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
	return clk_divider_ops.determine_rate(hw, req);
}

static int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate,
						unsigned long parent_rate)
{
	struct clk_divider *divider = to_clk_divider(hw);
	int value;
	unsigned long flags = 0;
	u32 val;
	int ret;

	value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags);
	if (value < 0)
		return value;

	if (divider->lock)
		spin_lock_irqsave(divider->lock, flags);

	val = readl(divider->reg);
	val &= ~(clk_div_mask(divider->width) << divider->shift);
	val |= (u32)value << divider->shift;
	writel(val, divider->reg);

	ret = imx93_clk_composite_wait_ready(hw, divider->reg);

	if (divider->lock)
		spin_unlock_irqrestore(divider->lock, flags);

	return ret;
}

static const struct clk_ops imx93_clk_composite_divider_ops = {
	.recalc_rate = imx93_clk_composite_divider_recalc_rate,
	.round_rate = imx93_clk_composite_divider_round_rate,
	.determine_rate = imx93_clk_composite_divider_determine_rate,
	.set_rate = imx93_clk_composite_divider_set_rate,
};

static u8 imx93_clk_composite_mux_get_parent(struct clk_hw *hw)
{
	return clk_mux_ops.get_parent(hw);
}

static int imx93_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index)
{
	struct clk_mux *mux = to_clk_mux(hw);
	u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
	unsigned long flags = 0;
	u32 reg;
	int ret;

	if (mux->lock)
		spin_lock_irqsave(mux->lock, flags);

	reg = readl(mux->reg);
	reg &= ~(mux->mask << mux->shift);
	val = val << mux->shift;
	reg |= val;
	writel(reg, mux->reg);

	ret = imx93_clk_composite_wait_ready(hw, mux->reg);

	if (mux->lock)
		spin_unlock_irqrestore(mux->lock, flags);

	return ret;
}

static int
imx93_clk_composite_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
	return clk_mux_ops.determine_rate(hw, req);
}

static const struct clk_ops imx93_clk_composite_mux_ops = {
	.get_parent = imx93_clk_composite_mux_get_parent,
	.set_parent = imx93_clk_composite_mux_set_parent,
	.determine_rate = imx93_clk_composite_mux_determine_rate,
};

struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *parent_names,
					 int num_parents, void __iomem *reg,
					 int num_parents, void __iomem *reg, u32 domain_id,
					 unsigned long flags)
{
	struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
@@ -33,6 +191,7 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p
	struct clk_gate *gate = NULL;
	struct clk_mux *mux = NULL;
	bool clk_ro = false;
	u32 authen;

	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
	if (!mux)
@@ -55,7 +214,8 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p
	div->lock = &imx_ccm_lock;
	div->flags = CLK_DIVIDER_ROUND_CLOSEST;

	if (!(readl(reg + AUTHEN_OFFSET) & TZ_NS_MASK))
	authen = readl(reg + AUTHEN_OFFSET);
	if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
		clk_ro = true;

	if (clk_ro) {
@@ -74,9 +234,10 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p
		gate->flags = CLK_GATE_SET_TO_DISABLE;

		hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
					       mux_hw, &clk_mux_ops, div_hw,
					       &clk_divider_ops, gate_hw,
					       &clk_gate_ops, flags | CLK_SET_RATE_NO_REPARENT);
					       mux_hw, &imx93_clk_composite_mux_ops, div_hw,
					       &imx93_clk_composite_divider_ops, gate_hw,
					       &imx93_clk_composite_gate_ops,
					       flags | CLK_SET_RATE_NO_REPARENT);
	}

	if (IS_ERR(hw))
+199 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2022 NXP
 *
 * Peng Fan <peng.fan@nxp.com>
 */

#include <linux/clk-provider.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/slab.h>

#include "clk.h"

#define DIRECT_OFFSET		0x0

/*
 * 0b000 - LPCG will be OFF in any CPU mode.
 * 0b100 - LPCG will be ON in any CPU mode.
 */
#define LPM_SETTING_OFF		0x0
#define LPM_SETTING_ON		0x4

#define LPM_CUR_OFFSET		0x1c

#define AUTHEN_OFFSET		0x30
#define CPULPM_EN		BIT(2)
#define TZ_NS_SHIFT		9
#define TZ_NS_MASK		BIT(9)

#define WHITE_LIST_SHIFT	16

struct imx93_clk_gate {
	struct clk_hw hw;
	void __iomem	*reg;
	u32		bit_idx;
	u32		val;
	u32		mask;
	spinlock_t	*lock;
	unsigned int	*share_count;
};

#define to_imx93_clk_gate(_hw) container_of(_hw, struct imx93_clk_gate, hw)

static void imx93_clk_gate_do_hardware(struct clk_hw *hw, bool enable)
{
	struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
	u32 val;

	val = readl(gate->reg + AUTHEN_OFFSET);
	if (val & CPULPM_EN) {
		val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF;
		writel(val, gate->reg + LPM_CUR_OFFSET);
	} else {
		val = readl(gate->reg + DIRECT_OFFSET);
		val &= ~(gate->mask << gate->bit_idx);
		if (enable)
			val |= (gate->val & gate->mask) << gate->bit_idx;
		writel(val, gate->reg + DIRECT_OFFSET);
	}
}

static int imx93_clk_gate_enable(struct clk_hw *hw)
{
	struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
	unsigned long flags;

	spin_lock_irqsave(gate->lock, flags);

	if (gate->share_count && (*gate->share_count)++ > 0)
		goto out;

	imx93_clk_gate_do_hardware(hw, true);
out:
	spin_unlock_irqrestore(gate->lock, flags);

	return 0;
}

static void imx93_clk_gate_disable(struct clk_hw *hw)
{
	struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
	unsigned long flags;

	spin_lock_irqsave(gate->lock, flags);

	if (gate->share_count) {
		if (WARN_ON(*gate->share_count == 0))
			goto out;
		else if (--(*gate->share_count) > 0)
			goto out;
	}

	imx93_clk_gate_do_hardware(hw, false);
out:
	spin_unlock_irqrestore(gate->lock, flags);
}

static int imx93_clk_gate_reg_is_enabled(struct imx93_clk_gate *gate)
{
	u32 val = readl(gate->reg + AUTHEN_OFFSET);

	if (val & CPULPM_EN) {
		val = readl(gate->reg + LPM_CUR_OFFSET);
		if (val == LPM_SETTING_ON)
			return 1;
	} else {
		val = readl(gate->reg);
		if (((val >> gate->bit_idx) & gate->mask) == gate->val)
			return 1;
	}

	return 0;
}

static int imx93_clk_gate_is_enabled(struct clk_hw *hw)
{
	struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
	unsigned long flags;
	int ret;

	spin_lock_irqsave(gate->lock, flags);

	ret = imx93_clk_gate_reg_is_enabled(gate);

	spin_unlock_irqrestore(gate->lock, flags);

	return ret;
}

static void imx93_clk_gate_disable_unused(struct clk_hw *hw)
{
	struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
	unsigned long flags;

	spin_lock_irqsave(gate->lock, flags);

	if (!gate->share_count || *gate->share_count == 0)
		imx93_clk_gate_do_hardware(hw, false);

	spin_unlock_irqrestore(gate->lock, flags);
}

static const struct clk_ops imx93_clk_gate_ops = {
	.enable = imx93_clk_gate_enable,
	.disable = imx93_clk_gate_disable,
	.disable_unused = imx93_clk_gate_disable_unused,
	.is_enabled = imx93_clk_gate_is_enabled,
};

static const struct clk_ops imx93_clk_gate_ro_ops = {
	.is_enabled = imx93_clk_gate_is_enabled,
};

struct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
			      unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
			      u32 mask, u32 domain_id, unsigned int *share_count)
{
	struct imx93_clk_gate *gate;
	struct clk_hw *hw;
	struct clk_init_data init;
	int ret;
	u32 authen;

	gate = kzalloc(sizeof(struct imx93_clk_gate), GFP_KERNEL);
	if (!gate)
		return ERR_PTR(-ENOMEM);

	gate->reg = reg;
	gate->lock = &imx_ccm_lock;
	gate->bit_idx = bit_idx;
	gate->val = val;
	gate->mask = mask;
	gate->share_count = share_count;

	init.name = name;
	init.ops = &imx93_clk_gate_ops;
	init.flags = flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE;
	init.parent_names = parent_name ? &parent_name : NULL;
	init.num_parents = parent_name ? 1 : 0;

	gate->hw.init = &init;
	hw = &gate->hw;

	authen = readl(reg + AUTHEN_OFFSET);
	if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
		init.ops = &imx93_clk_gate_ro_ops;

	ret = clk_hw_register(dev, hw);
	if (ret) {
		kfree(gate);
		return ERR_PTR(ret);
	}

	return hw;
}
EXPORT_SYMBOL_GPL(imx93_clk_gate);
+1 −1
Original line number Diff line number Diff line
@@ -665,8 +665,8 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
	hws[IMX8MP_CLK_CAN1_ROOT] = imx_clk_hw_gate2("can1_root_clk", "can1", ccm_base + 0x4350, 0);
	hws[IMX8MP_CLK_CAN2_ROOT] = imx_clk_hw_gate2("can2_root_clk", "can2", ccm_base + 0x4360, 0);
	hws[IMX8MP_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_root_clk", "ipg_root", ccm_base + 0x43a0, 0);
	hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0);
	hws[IMX8MP_CLK_SIM_ENET_ROOT] = imx_clk_hw_gate4("sim_enet_root_clk", "enet_axi", ccm_base + 0x4400, 0);
	hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0);
	hws[IMX8MP_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_core", ccm_base + 0x4450, 0);
	hws[IMX8MP_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core", ccm_base + 0x4460, 0);
	hws[IMX8MP_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", ccm_base + 0x4490, 0);
+20 −10
Original line number Diff line number Diff line
@@ -28,6 +28,11 @@ enum clk_sel {
	MAX_SEL
};

static u32 share_count_sai1;
static u32 share_count_sai2;
static u32 share_count_sai3;
static u32 share_count_mub;

static const char *parent_names[MAX_SEL][4] = {
	{"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "video_pll"},
	{"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "sys_pll_pfd2_div2"},
@@ -146,6 +151,7 @@ static const struct imx93_clk_ccgr {
	char *parent_name;
	u32 off;
	unsigned long flags;
	u32 *shared_count;
} ccgr_array[] = {
	{ IMX93_CLK_A55_GATE,		"a55",		"a55_root",		0x8000, },
	/* M33 critical clk for system run */
@@ -158,8 +164,10 @@ static const struct imx93_clk_ccgr {
	{ IMX93_CLK_WDOG5_GATE,		"wdog5",	"osc_24m",		0x8400, },
	{ IMX93_CLK_SEMA1_GATE,		"sema1",	"bus_aon_root",		0x8440, },
	{ IMX93_CLK_SEMA2_GATE,		"sema2",	"bus_wakeup_root",	0x8480, },
	{ IMX93_CLK_MU_A_GATE,		"mu_a",		"bus_aon_root",		0x84c0, },
	{ IMX93_CLK_MU_B_GATE,		"mu_b",		"bus_aon_root",		0x8500, },
	{ IMX93_CLK_MU1_A_GATE,		"mu1_a",	"bus_aon_root",		0x84c0, CLK_IGNORE_UNUSED },
	{ IMX93_CLK_MU2_A_GATE,		"mu2_a",	"bus_wakeup_root",	0x84c0, CLK_IGNORE_UNUSED },
	{ IMX93_CLK_MU1_B_GATE,		"mu1_b",	"bus_aon_root",		0x8500, 0, &share_count_mub },
	{ IMX93_CLK_MU2_B_GATE,		"mu2_b",	"bus_wakeup_root",	0x8500, 0, &share_count_mub },
	{ IMX93_CLK_EDMA1_GATE,		"edma1",	"m33_root",		0x8540, },
	{ IMX93_CLK_EDMA2_GATE,		"edma2",	"wakeup_axi_root",	0x8580, },
	{ IMX93_CLK_FLEXSPI1_GATE,	"flexspi",	"flexspi_root",		0x8640, },
@@ -210,9 +218,12 @@ static const struct imx93_clk_ccgr {
	{ IMX93_CLK_USDHC1_GATE,	"usdhc1",	"usdhc1_root",		0x9380, },
	{ IMX93_CLK_USDHC2_GATE,	"usdhc2",	"usdhc2_root",		0x93c0, },
	{ IMX93_CLK_USDHC3_GATE,	"usdhc3",	"usdhc3_root",		0x9400, },
	{ IMX93_CLK_SAI1_GATE,		"sai1",		"sai1_root",		0x9440, },
	{ IMX93_CLK_SAI2_GATE,		"sai2",		"sai2_root",		0x9480, },
	{ IMX93_CLK_SAI3_GATE,		"sai3",		"sai3_root",		0x94c0, },
	{ IMX93_CLK_SAI1_GATE,          "sai1",         "sai1_root",            0x9440, 0, &share_count_sai1},
	{ IMX93_CLK_SAI1_IPG,		"sai1_ipg_clk", "bus_aon_root",		0x9440, 0, &share_count_sai1},
	{ IMX93_CLK_SAI2_GATE,          "sai2",         "sai2_root",            0x9480, 0, &share_count_sai2},
	{ IMX93_CLK_SAI2_IPG,		"sai2_ipg_clk", "bus_wakeup_root",	0x9480, 0, &share_count_sai2},
	{ IMX93_CLK_SAI3_GATE,          "sai3",         "sai3_root",            0x94c0, 0, &share_count_sai3},
	{ IMX93_CLK_SAI3_IPG,		"sai3_ipg_clk", "bus_wakeup_root",	0x94c0, 0, &share_count_sai3},
	{ IMX93_CLK_MIPI_CSI_GATE,	"mipi_csi",	"media_apb_root",	0x9580, },
	{ IMX93_CLK_MIPI_DSI_GATE,	"mipi_dsi",	"media_apb_root",	0x95c0, },
	{ IMX93_CLK_LVDS_GATE,		"lvds",		"media_ldb_root",	0x9600, },
@@ -293,16 +304,15 @@ static int imx93_clocks_probe(struct platform_device *pdev)
		root = &root_array[i];
		clks[root->clk] = imx93_clk_composite_flags(root->name,
							    parent_names[root->sel],
							    4, base + root->off,
							    4, base + root->off, 3,
							    root->flags);
	}

	for (i = 0; i < ARRAY_SIZE(ccgr_array); i++) {
		ccgr = &ccgr_array[i];
		clks[ccgr->clk] = imx_clk_hw_gate4_flags(ccgr->name,
							 ccgr->parent_name,
							 base + ccgr->off, 0,
							 ccgr->flags);
		clks[ccgr->clk] = imx93_clk_gate(NULL, ccgr->name, ccgr->parent_name,
						 ccgr->flags, base + ccgr->off, 0, 1, 1, 3,
						 ccgr->shared_count);
	}

	imx_check_clk_hws(clks, IMX93_CLK_END);
Loading