The patch adds support for TI gate clock binding. The code is based on the drivers/clk/ti/gate.c driver of the Linux kernel.
Signed-off-by: Dario Binacchi <dario...@libero.it> --- (no changes since v1) .../clock/gpio-gate-clock.txt | 21 ++++ .../clock/ti,clockdomain.txt | 24 ++++ doc/device-tree-bindings/clock/ti,gate.txt | 106 ++++++++++++++++++ drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-ti-gate.c | 92 +++++++++++++++ 6 files changed, 250 insertions(+) create mode 100644 doc/device-tree-bindings/clock/gpio-gate-clock.txt create mode 100644 doc/device-tree-bindings/clock/ti,clockdomain.txt create mode 100644 doc/device-tree-bindings/clock/ti,gate.txt create mode 100644 drivers/clk/clk-ti-gate.c diff --git a/doc/device-tree-bindings/clock/gpio-gate-clock.txt b/doc/device-tree-bindings/clock/gpio-gate-clock.txt new file mode 100644 index 0000000000..7cba766cbe --- /dev/null +++ b/doc/device-tree-bindings/clock/gpio-gate-clock.txt @@ -0,0 +1,21 @@ +Binding for simple gpio gated clock. + +This binding uses the common clock binding[1]. + +[1] doc/device-tree-bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "gpio-gate-clock". +- #clock-cells : from common clock binding; shall be set to 0. +- enable-gpios : GPIO reference for enabling and disabling the clock. + +Optional properties: +- clocks: Maximum of one parent clock is supported. + +Example: + clock { + compatible = "gpio-gate-clock"; + clocks = <&parentclk>; + #clock-cells = <0>; + enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + }; diff --git a/doc/device-tree-bindings/clock/ti,clockdomain.txt b/doc/device-tree-bindings/clock/ti,clockdomain.txt new file mode 100644 index 0000000000..cf05ee8bb9 --- /dev/null +++ b/doc/device-tree-bindings/clock/ti,clockdomain.txt @@ -0,0 +1,24 @@ +Binding for Texas Instruments clockdomain. + +Binding status: Unstable - ABI compatibility may be broken in the future + +This binding uses the common clock binding[1] in consumer role. +Every clock on TI SoC belongs to one clockdomain, but software +only needs this information for specific clocks which require +their parent clockdomain to be controlled when the clock is +enabled/disabled. This binding doesn't define a new clock +binding type, it is used to group existing clock nodes under +hardware hierarchy. + +[1] doc/device-tree-bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "ti,clockdomain" +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : link phandles of clocks within this domain + +Examples: + dss_clkdm: dss_clkdm { + compatible = "ti,clockdomain"; + clocks = <&dss1_alwon_fck_3430es2>, <&dss_ick_3430es2>; + }; diff --git a/doc/device-tree-bindings/clock/ti,gate.txt b/doc/device-tree-bindings/clock/ti,gate.txt new file mode 100644 index 0000000000..792e2033c0 --- /dev/null +++ b/doc/device-tree-bindings/clock/ti,gate.txt @@ -0,0 +1,106 @@ +Binding for Texas Instruments gate clock. + +Binding status: Unstable - ABI compatibility may be broken in the future + +This binding uses the common clock binding[1]. This clock is +quite much similar to the basic gate-clock [2], however, +it supports a number of additional features. If no register +is provided for this clock, the code assumes that a clockdomain +will be controlled instead and the corresponding hw-ops for +that is used. + +[1] doc/device-tree-bindings/clock/clock-bindings.txt +[2] doc/device-tree-bindings/clock/gpio-gate-clock.txt +[3] doc/device-tree-bindings/clock/ti,clockdomain.txt + +Required properties: +- compatible : shall be one of: + "ti,gate-clock" - basic gate clock + "ti,wait-gate-clock" - gate clock which waits until clock is active before + returning from clk_enable() + "ti,dss-gate-clock" - gate clock with DSS specific hardware handling + "ti,am35xx-gate-clock" - gate clock with AM35xx specific hardware handling + "ti,clkdm-gate-clock" - clockdomain gate clock, which derives its functional + clock directly from a clockdomain, see [3] how + to map clockdomains properly + "ti,hsdiv-gate-clock" - gate clock with OMAP36xx specific hardware handling, + required for a hardware errata + "ti,composite-gate-clock" - composite gate clock, to be part of composite + clock + "ti,composite-no-wait-gate-clock" - composite gate clock that does not wait + for clock to be active before returning + from clk_enable() +- #clock-cells : from common clock binding; shall be set to 0 +- clocks : link to phandle of parent clock +- reg : offset for register controlling adjustable gate, not needed for + ti,clkdm-gate-clock type + +Optional properties: +- ti,bit-shift : bit shift for programming the clock gate, invalid for + ti,clkdm-gate-clock type +- ti,set-bit-to-disable : inverts default gate programming. Setting the bit + gates the clock and clearing the bit ungates the clock. + +Examples: + mmchs2_fck: mmchs2_fck@48004a00 { + #clock-cells = <0>; + compatible = "ti,gate-clock"; + clocks = <&core_96m_fck>; + reg = <0x0a00>; + ti,bit-shift = <25>; + }; + + uart4_fck_am35xx: uart4_fck_am35xx { + #clock-cells = <0>; + compatible = "ti,wait-gate-clock"; + clocks = <&core_48m_fck>; + reg = <0x0a00>; + ti,bit-shift = <23>; + }; + + dss1_alwon_fck_3430es2: dss1_alwon_fck_3430es2@48004e00 { + #clock-cells = <0>; + compatible = "ti,dss-gate-clock"; + clocks = <&dpll4_m4x2_ck>; + reg = <0x0e00>; + ti,bit-shift = <0>; + }; + + emac_ick: emac_ick@4800259c { + #clock-cells = <0>; + compatible = "ti,am35xx-gate-clock"; + clocks = <&ipss_ick>; + reg = <0x059c>; + ti,bit-shift = <1>; + }; + + emu_src_ck: emu_src_ck { + #clock-cells = <0>; + compatible = "ti,clkdm-gate-clock"; + clocks = <&emu_src_mux_ck>; + }; + + dpll4_m2x2_ck: dpll4_m2x2_ck@48004d00 { + #clock-cells = <0>; + compatible = "ti,hsdiv-gate-clock"; + clocks = <&dpll4_m2x2_mul_ck>; + ti,bit-shift = <0x1b>; + reg = <0x0d00>; + ti,set-bit-to-disable; + }; + + vlynq_gate_fck: vlynq_gate_fck { + #clock-cells = <0>; + compatible = "ti,composite-gate-clock"; + clocks = <&core_ck>; + ti,bit-shift = <3>; + reg = <0x0200>; + }; + + sys_clkout2_src_gate: sys_clkout2_src_gate { + #clock-cells = <0>; + compatible = "ti,composite-no-wait-gate-clock"; + clocks = <&core_ck>; + ti,bit-shift = <15>; + reg = <0x0070>; + }; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index d7b546d9db..c0528c2aeb 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -111,6 +111,12 @@ config CLK_TI_DIVIDER help This enables the divider clock driver support on TI's SoCs. +config CLK_TI_GATE + bool "TI gate clock driver" + depends on CLK && OF_CONTROL + help + This enables the gate clock driver support on TI's SoCs. + config CLK_TI_MUX bool "TI mux clock driver" depends on CLK && OF_CONTROL && CLK_CCF diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 21b1e9c364..4d16fb7a50 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o obj-$(CONFIG_STM32H7) += clk_stm32h7.o obj-$(CONFIG_CLK_TI_AM3_DPLL) += clk-ti-am3-dpll.o clk-ti-am3-dpll-x2.o obj-$(CONFIG_CLK_TI_DIVIDER) += clk-ti-divider.o +obj-$(CONFIG_CLK_TI_GATE) += clk-ti-gate.o obj-$(CONFIG_CLK_TI_MUX) += clk-ti-mux.o obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o obj-$(CONFIG_CLK_VERSAL) += clk_versal.o diff --git a/drivers/clk/clk-ti-gate.c b/drivers/clk/clk-ti-gate.c new file mode 100644 index 0000000000..0f86b8a4b3 --- /dev/null +++ b/drivers/clk/clk-ti-gate.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI gate clock support + * + * Copyright (C) 2020 Dario Binacchi <dario...@libero.it> + * + * Loosely based on Linux kernel drivers/clk/ti/gate.c + */ + +#include <common.h> +#include <dm.h> +#include <clk-uclass.h> +#include <asm/io.h> +#include <linux/clk-provider.h> + +struct clk_ti_gate_priv { + fdt_addr_t reg; + u8 enable_bit; + u32 flags; + bool invert_enable; +}; + +static int clk_ti_gate_disable(struct clk *clk) +{ + struct clk_ti_gate_priv *priv = dev_get_priv(clk->dev); + u32 v; + + v = readl(priv->reg); + if (priv->invert_enable) + v |= (1 << priv->enable_bit); + else + v &= ~(1 << priv->enable_bit); + + writel(v, priv->reg); + /* No OCP barrier needed here since it is a disable operation */ + return 0; +} + +static int clk_ti_gate_enable(struct clk *clk) +{ + struct clk_ti_gate_priv *priv = dev_get_priv(clk->dev); + u32 v; + + v = readl(priv->reg); + if (priv->invert_enable) + v &= ~(1 << priv->enable_bit); + else + v |= (1 << priv->enable_bit); + + writel(v, priv->reg); + /* OCP barrier */ + v = readl(priv->reg); + return 0; +} + +static int clk_ti_gate_ofdata_to_platdata(struct udevice *dev) +{ + struct clk_ti_gate_priv *priv = dev_get_priv(dev); + + priv->reg = dev_read_addr(dev); + if (priv->reg == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get control register\n"); + return -EINVAL; + } + + dev_dbg(dev, "reg=0x%08lx\n", priv->reg); + priv->enable_bit = dev_read_u32_default(dev, "ti,bit-shift", 0); + if (dev_read_bool(dev, "ti,set-rate-parent")) + priv->flags |= CLK_SET_RATE_PARENT; + + priv->invert_enable = dev_read_bool(dev, "ti,set-bit-to-disable"); + return 0; +} + +static struct clk_ops clk_ti_gate_ops = { + .enable = clk_ti_gate_enable, + .disable = clk_ti_gate_disable, +}; + +static const struct udevice_id clk_ti_gate_of_match[] = { + { .compatible = "ti,gate-clock" }, + { }, +}; + +U_BOOT_DRIVER(clk_ti_gate) = { + .name = "ti_gate_clock", + .id = UCLASS_CLK, + .of_match = clk_ti_gate_of_match, + .ofdata_to_platdata = clk_ti_gate_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct clk_ti_gate_priv), + .ops = &clk_ti_gate_ops, +}; -- 2.17.1