Commit 3477a72b authored by Stephen Boyd's avatar Stephen Boyd
Browse files

Merge tag 'meson-clk-for-4.14' of git://github.com/baylibre/clk-meson into clk-next

Pull Amlogic clock driver updates from Neil Armstrong:

 * meson8b: add the reset controller to the clkc
 * meson: expose all clk ids
 * gxbb-aoclk: Add CEC 32k clock
 * gxbb: add mmc input 0 clocks
 * meson: fix protection against undefined clks
 * gxbb: fix audio divider flags

* tag 'meson-clk-for-4.14' of git://github.com/baylibre/clk-meson:
  clk: meson: gxbb-aoclk: Add CEC 32k clock
  clk: meson: gxbb-aoclk: Switch to regmap for register access
  dt-bindings: clock: amlogic, gxbb-aoclkc: Update bindings
  clk: meson: gxbb: Add sd_emmc clk0 clocks
  clk: meson: gxbb: fix clk_mclk_i958 divider flags
  clk: meson: gxbb: fix meson cts_amclk divider flags
  clk: meson: meson8b: register the built-in reset controller
  dt-bindings: clock: gxbb-aoclk: Add CEC 32k clock
  clk: meson: gxbb: Add sd_emmc clk0 clkids
  clk: meson-gxbb: expose almost every clock in the bindings
  clk: meson8b: expose every clock in the bindings
  clk: meson: gxbb: fix protection against undefined clks
  clk: meson: meson8b: fix protection against undefined clks
  dt-bindings: clock: meson8b: describe the embedded reset controller
parents e66d57a9 62ec0b97
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -5,9 +5,11 @@ controllers within the Always-On part of the SoC.

Required Properties:

- compatible: should be "amlogic,gxbb-aoclkc"
- reg: physical base address of the clock controller and length of memory
       mapped region.
- compatible: value should be different for each SoC family as :
	- GXBB (S905) : "amlogic,meson-gxbb-aoclkc"
	- GXL (S905X, S905D) : "amlogic,meson-gxl-aoclkc"
	- GXM (S912) : "amlogic,meson-gxm-aoclkc"
	followed by the common "amlogic,meson-gx-aoclkc"

- #clock-cells: should be 1.

@@ -23,14 +25,22 @@ to specify the reset which they consume. All available resets are defined as
preprocessor macros in the dt-bindings/reset/gxbb-aoclkc.h header and can be
used in device tree sources.

Parent node should have the following properties :
- compatible: "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd"
- reg: base address and size of the AO system control register space.

Example: AO Clock controller node:

	clkc_AO: clock-controller@040 {
		compatible = "amlogic,gxbb-aoclkc";
		reg = <0x0 0x040 0x0 0x4>;
ao_sysctrl: sys-ctrl@0 {
	compatible = "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd";
	reg =  <0x0 0x0 0x0 0x100>;

	clkc_AO: clock-controller {
		compatible = "amlogic,meson-gxbb-aoclkc", "amlogic,meson-gx-aoclkc";
		#clock-cells = <1>;
		#reset-cells = <1>;
	};
};

Example: UART controller node that consumes the clock and reset generated
  by the clock controller:
+8 −1
Original line number Diff line number Diff line
@@ -16,18 +16,25 @@ Required Properties:
	   mapped region.

- #clock-cells: should be 1.
- #reset-cells: should be 1.

Each clock is assigned an identifier and client nodes can use this identifier
to specify the clock which they consume. All available clocks are defined as
preprocessor macros in the dt-bindings/clock/meson8b-clkc.h header and can be
used in device tree sources.

Similarly a preprocessor macro for each reset line is defined in
dt-bindings/reset/amlogic,meson8b-clkc-reset.h (which can be used from the
device tree sources).


Example: Clock controller node:

	clkc: clock-controller@c1104000 {
		#clock-cells = <1>;
		compatible = "amlogic,meson8b-clkc";
		reg = <0xc1108000 0x4>, <0xc1104000 0x460>;
		#clock-cells = <1>;
		#reset-cells = <1>;
	};


+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ config COMMON_CLK_AMLOGIC
config COMMON_CLK_MESON8B
	bool
	depends on COMMON_CLK_AMLOGIC
	select RESET_CONTROLLER
	help
	  Support for the clock controller on AmLogic S802 (Meson8),
	  S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
+1 −1
Original line number Diff line number Diff line
@@ -4,4 +4,4 @@

obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB)	 += gxbb.o gxbb-aoclk.o
obj-$(CONFIG_COMMON_CLK_GXBB)	 += gxbb.o gxbb-aoclk.o gxbb-aoclk-regmap.o gxbb-aoclk-32k.o
+194 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017 BayLibre, SAS.
 * Author: Neil Armstrong <narmstrong@baylibre.com>
 *
 * SPDX-License-Identifier: GPL-2.0+
 */

#include <linux/clk-provider.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include "gxbb-aoclk.h"

/*
 * The AO Domain embeds a dual/divider to generate a more precise
 * 32,768KHz clock for low-power suspend mode and CEC.
 *                      ______   ______
 *                     |      | |      |
 *         ______      | Div1 |-| Cnt1 |       ______
 *        |      |    /|______| |______|\     |      |
 * Xtal-->| Gate |---|  ______   ______  X-X--| Gate |-->
 *        |______| |  \|      | |      |/  |  |______|
 *                 |   | Div2 |-| Cnt2 |   |
 *                 |   |______| |______|   |
 *                 |_______________________|
 *
 * The dividing can be switched to single or dual, with a counter
 * for each divider to set when the switching is done.
 * The entire dividing mechanism can be also bypassed.
 */

#define CLK_CNTL0_N1_MASK	GENMASK(11, 0)
#define CLK_CNTL0_N2_MASK	GENMASK(23, 12)
#define CLK_CNTL0_DUALDIV_EN	BIT(28)
#define CLK_CNTL0_OUT_GATE_EN	BIT(30)
#define CLK_CNTL0_IN_GATE_EN	BIT(31)

#define CLK_CNTL1_M1_MASK	GENMASK(11, 0)
#define CLK_CNTL1_M2_MASK	GENMASK(23, 12)
#define CLK_CNTL1_BYPASS_EN	BIT(24)
#define CLK_CNTL1_SELECT_OSC	BIT(27)

#define PWR_CNTL_ALT_32K_SEL	GENMASK(13, 10)

struct cec_32k_freq_table {
	unsigned long parent_rate;
	unsigned long target_rate;
	bool dualdiv;
	unsigned int n1;
	unsigned int n2;
	unsigned int m1;
	unsigned int m2;
};

static const struct cec_32k_freq_table aoclk_cec_32k_table[] = {
	[0] = {
		.parent_rate = 24000000,
		.target_rate = 32768,
		.dualdiv = true,
		.n1 = 733,
		.n2 = 732,
		.m1 = 8,
		.m2 = 11,
	},
};

/*
 * If CLK_CNTL0_DUALDIV_EN == 0
 *  - will use N1 divider only
 * If CLK_CNTL0_DUALDIV_EN == 1
 *  - hold M1 cycles of N1 divider then changes to N2
 *  - hold M2 cycles of N2 divider then changes to N1
 * Then we can get more accurate division.
 */
static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw,
					       unsigned long parent_rate)
{
	struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
	unsigned long n1;
	u32 reg0, reg1;

	regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, &reg0);
	regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, &reg1);

	if (reg1 & CLK_CNTL1_BYPASS_EN)
		return parent_rate;

	if (reg0 & CLK_CNTL0_DUALDIV_EN) {
		unsigned long n2, m1, m2, f1, f2, p1, p2;

		n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
		n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1;

		m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1;
		m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1;

		f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
		f2 = DIV_ROUND_CLOSEST(parent_rate, n2);

		p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
		p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));

		return DIV_ROUND_UP(100000000, p1 + p2);
	}

	n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;

	return DIV_ROUND_CLOSEST(parent_rate, n1);
}

static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate,
							  unsigned long prate)
{
	int i;

	for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i)
		if (aoclk_cec_32k_table[i].parent_rate == prate &&
		    aoclk_cec_32k_table[i].target_rate == rate)
			return &aoclk_cec_32k_table[i];

	return NULL;
}

static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate,
				     unsigned long *prate)
{
	const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
								  *prate);

	/* If invalid return first one */
	if (!freq)
		return aoclk_cec_32k_table[0].target_rate;

	return freq->target_rate;
}

/*
 * From the Amlogic init procedure, the IN and OUT gates needs to be handled
 * in the init procedure to avoid any glitches.
 */

static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate,
				  unsigned long parent_rate)
{
	const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
								  parent_rate);
	struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
	u32 reg = 0;

	if (!freq)
		return -EINVAL;

	/* Disable clock */
	regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
			   CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0);

	reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1);
	if (freq->dualdiv)
		reg |= CLK_CNTL0_DUALDIV_EN |
		       FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1);

	regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg);

	reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1);
	if (freq->dualdiv)
		reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1);

	regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg);

	/* Enable clock */
	regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
			   CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN);

	udelay(200);

	regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
			   CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN);

	regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1,
			   CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC);

	/* Select 32k from XTAL */
	regmap_update_bits(cec_32k->regmap,
			  AO_RTI_PWR_CNTL_REG0,
			  PWR_CNTL_ALT_32K_SEL,
			  FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4));

	return 0;
}

const struct clk_ops meson_aoclk_cec_32k_ops = {
	.recalc_rate = aoclk_cec_32k_recalc_rate,
	.round_rate = aoclk_cec_32k_round_rate,
	.set_rate = aoclk_cec_32k_set_rate,
};
Loading